]> SALOME platform Git repositories - modules/shaper.git/blob - src/Model/Model_Document.cpp
Salome HOME
Make edition transaction where actually nothing was changed as empty one and do not...
[modules/shaper.git] / src / Model / Model_Document.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:        Model_Document.cxx
4 // Created:     28 Feb 2014
5 // Author:      Mikhail PONIKAROV
6
7 #include <Model_Document.h>
8 #include <Model_Data.h>
9 #include <Model_Objects.h>
10 #include <Model_Application.h>
11 #include <Model_Session.h>
12 #include <Model_Events.h>
13 #include <ModelAPI_ResultPart.h>
14 #include <ModelAPI_Validator.h>
15 #include <ModelAPI_CompositeFeature.h>
16 #include <ModelAPI_AttributeSelectionList.h>
17 #include <ModelAPI_Tools.h>
18
19 #include <Events_Loop.h>
20 #include <Events_Error.h>
21
22 #include <TDataStd_Integer.hxx>
23 #include <TDataStd_Comment.hxx>
24 #include <TDF_ChildIDIterator.hxx>
25 #include <TDataStd_ReferenceArray.hxx>
26 #include <TDataStd_IntegerArray.hxx>
27 #include <TDataStd_HLabelArray1.hxx>
28 #include <TDataStd_Name.hxx>
29 #include <TDF_Reference.hxx>
30 #include <TDF_ChildIDIterator.hxx>
31 #include <TDF_LabelMapHasher.hxx>
32 #include <TDF_Delta.hxx>
33 #include <OSD_File.hxx>
34 #include <OSD_Path.hxx>
35 #include <TDF_AttributeDelta.hxx>
36 #include <TDF_AttributeDeltaList.hxx>
37 #include <TDF_ListIteratorOfAttributeDeltaList.hxx>
38 #include <TDF_ListIteratorOfLabelList.hxx>
39
40 #include <climits>
41 #ifndef WIN32
42 #include <sys/stat.h>
43 #endif
44
45 #ifdef WIN32
46 # define _separator_ '\\'
47 #else
48 # define _separator_ '/'
49 #endif
50
51 static const int UNDO_LIMIT = 1000;  // number of possible undo operations (big for sketcher)
52
53 static const int TAG_GENERAL = 1;  // general properties tag
54
55 // general sub-labels
56 static const int TAG_CURRENT_FEATURE = 1; ///< where the reference to the current feature label is located (or no attribute if null feature)
57 static const int TAG_CURRENT_TRANSACTION = 2; ///< integer, index of the transaction
58 static const int TAG_SELECTION_FEATURE = 3; ///< integer, tag of the selection feature label
59
60 Model_Document::Model_Document(const std::string theID, const std::string theKind)
61     : myID(theID), myKind(theKind), myIsActive(false),
62       myDoc(new TDocStd_Document("BinOcaf"))  // binary OCAF format
63 {
64   myObjs = new Model_Objects(myDoc->Main());
65   myDoc->SetUndoLimit(UNDO_LIMIT);  
66   myTransactionSave = 0;
67   myExecuteFeatures = true;
68   // to have something in the document and avoid empty doc open/save problem
69   // in transaction for nesting correct working
70   myDoc->NewCommand();
71   TDataStd_Integer::Set(myDoc->Main().Father(), 0);
72   // this to avoid creation of integer attribute outside the transaction after undo
73   transactionID();
74   myDoc->CommitCommand();
75 }
76
77 void Model_Document::setThis(DocumentPtr theDoc)
78 {
79   myObjs->setOwner(theDoc);
80 }
81
82 /// Returns the file name of this document by the nameof directory and identifuer of a document
83 static TCollection_ExtendedString DocFileName(const char* theFileName, const std::string& theID)
84 {
85   TCollection_ExtendedString aPath((const Standard_CString) theFileName);
86   // remove end-separators
87   while(aPath.Length() && 
88         (aPath.Value(aPath.Length()) == '\\' || aPath.Value(aPath.Length()) == '/'))
89     aPath.Remove(aPath.Length());
90   aPath += _separator_;
91   aPath += theID.c_str();
92   aPath += ".cbf";  // standard binary file extension
93   return aPath;
94 }
95
96 bool Model_Document::isRoot() const
97 {
98   return this == Model_Session::get()->moduleDocument().get();
99 }
100
101 bool Model_Document::load(const char* theFileName, DocumentPtr theThis)
102 {
103   Handle(Model_Application) anApp = Model_Application::getApplication();
104   if (isRoot()) {
105     anApp->setLoadPath(theFileName);
106   }
107   TCollection_ExtendedString aPath(DocFileName(theFileName, myID));
108   PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus) -1;
109   Handle(TDocStd_Document) aLoaded;
110   try {
111     aStatus = anApp->Open(aPath, aLoaded);
112   } catch (Standard_Failure) {
113     Handle(Standard_Failure) aFail = Standard_Failure::Caught();
114     Events_Error::send(
115         std::string("Exception in opening of document: ") + aFail->GetMessageString());
116     return false;
117   }
118   bool isError = aStatus != PCDM_RS_OK;
119   if (isError) {
120     switch (aStatus) {
121       case PCDM_RS_UnknownDocument:
122         Events_Error::send(std::string("Can not open document"));
123         break;
124       case PCDM_RS_AlreadyRetrieved:
125         Events_Error::send(std::string("Can not open document: already opened"));
126         break;
127       case PCDM_RS_AlreadyRetrievedAndModified:
128         Events_Error::send(
129             std::string("Can not open document: already opened and modified"));
130         break;
131       case PCDM_RS_NoDriver:
132         Events_Error::send(std::string("Can not open document: driver library is not found"));
133         break;
134       case PCDM_RS_UnknownFileDriver:
135         Events_Error::send(std::string("Can not open document: unknown driver for opening"));
136         break;
137       case PCDM_RS_OpenError:
138         Events_Error::send(std::string("Can not open document: file open error"));
139         break;
140       case PCDM_RS_NoVersion:
141         Events_Error::send(std::string("Can not open document: invalid version"));
142         break;
143       case PCDM_RS_NoModel:
144         Events_Error::send(std::string("Can not open document: no data model"));
145         break;
146       case PCDM_RS_NoDocument:
147         Events_Error::send(std::string("Can not open document: no document inside"));
148         break;
149       case PCDM_RS_FormatFailure:
150         Events_Error::send(std::string("Can not open document: format failure"));
151         break;
152       case PCDM_RS_TypeNotFoundInSchema:
153         Events_Error::send(std::string("Can not open document: invalid object"));
154         break;
155       case PCDM_RS_UnrecognizedFileFormat:
156         Events_Error::send(std::string("Can not open document: unrecognized file format"));
157         break;
158       case PCDM_RS_MakeFailure:
159         Events_Error::send(std::string("Can not open document: make failure"));
160         break;
161       case PCDM_RS_PermissionDenied:
162         Events_Error::send(std::string("Can not open document: permission denied"));
163         break;
164       case PCDM_RS_DriverFailure:
165         Events_Error::send(std::string("Can not open document: driver failure"));
166         break;
167       default:
168         Events_Error::send(std::string("Can not open document: unknown error"));
169         break;
170     }
171   }
172   std::shared_ptr<Model_Session> aSession = 
173     std::dynamic_pointer_cast<Model_Session>(Model_Session::get());
174   if (!isError) {
175     myDoc = aLoaded;
176     myDoc->SetUndoLimit(UNDO_LIMIT);
177     // to avoid the problem that feature is created in the current, not this, document
178     aSession->setActiveDocument(anApp->getDocument(myID), false);
179     aSession->setCheckTransactions(false);
180     if (myObjs)
181       delete myObjs;
182     myObjs = new Model_Objects(myDoc->Main()); // synchronisation is inside
183     myObjs->setOwner(theThis);
184     // update the current features status
185     setCurrentFeature(currentFeature(false), false);
186     aSession->setCheckTransactions(true);
187     aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false);
188     // this is done in Part result "activate", so no needed here. Causes not-blue active part.
189     // aSession->setActiveDocument(anApp->getDocument(myID), true);
190   } else { // open failed, but new documnet was created to work with it: inform the model
191     aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false);
192   } 
193   return !isError;
194 }
195
196 bool Model_Document::save(const char* theFileName, std::list<std::string>& theResults)
197 {
198   // create a directory in the root document if it is not yet exist
199   Handle(Model_Application) anApp = Model_Application::getApplication();
200   if (isRoot()) {
201 #ifdef WIN32
202     CreateDirectory(theFileName, NULL);
203 #else
204     mkdir(theFileName, 0x1ff);
205 #endif
206   }
207   // filename in the dir is id of document inside of the given directory
208   TCollection_ExtendedString aPath(DocFileName(theFileName, myID));
209   PCDM_StoreStatus aStatus;
210   try {
211     aStatus = anApp->SaveAs(myDoc, aPath);
212   } catch (Standard_Failure) {
213     Handle(Standard_Failure) aFail = Standard_Failure::Caught();
214     Events_Error::send(
215         std::string("Exception in saving of document: ") + aFail->GetMessageString());
216     return false;
217   }
218   bool isDone = aStatus == PCDM_SS_OK || aStatus == PCDM_SS_No_Obj;
219   if (!isDone) {
220     switch (aStatus) {
221       case PCDM_SS_DriverFailure:
222         Events_Error::send(std::string("Can not save document: save driver-library failure"));
223         break;
224       case PCDM_SS_WriteFailure:
225         Events_Error::send(std::string("Can not save document: file writing failure"));
226         break;
227       case PCDM_SS_Failure:
228       default:
229         Events_Error::send(std::string("Can not save document"));
230         break;
231     }
232   }
233   myTransactionSave = myTransactions.size();
234   if (isDone) {  // save also sub-documents if any
235     theResults.push_back(TCollection_AsciiString(aPath).ToCString());
236     const std::set<std::string> aSubs = subDocuments(false);
237     std::set<std::string>::iterator aSubIter = aSubs.begin();
238     for (; aSubIter != aSubs.end() && isDone; aSubIter++) {
239       if (anApp->isLoadByDemand(*aSubIter)) { 
240         // copy not-activated document that is not in the memory
241         std::string aDocName = *aSubIter;
242         if (!aDocName.empty()) {
243           // just copy file
244           TCollection_AsciiString aSubPath(DocFileName(anApp->loadPath().c_str(), aDocName));
245           OSD_Path aPath(aSubPath);
246           OSD_File aFile(aPath);
247           if (aFile.Exists()) {
248             TCollection_AsciiString aDestinationDir(DocFileName(theFileName, aDocName));
249             OSD_Path aDestination(aDestinationDir);
250             aFile.Copy(aDestination);
251             theResults.push_back(aDestinationDir.ToCString());
252           } else {
253             Events_Error::send(
254               std::string("Can not open file ") + aSubPath.ToCString() + " for saving");
255           }
256         }
257       } else { // simply save opened document
258         isDone = subDoc(*aSubIter)->save(theFileName, theResults);
259       }
260     }
261   }
262   return isDone;
263 }
264
265 void Model_Document::close(const bool theForever)
266 {
267   std::shared_ptr<ModelAPI_Session> aPM = Model_Session::get();
268   if (!isRoot() && this == aPM->activeDocument().get()) {
269     aPM->setActiveDocument(aPM->moduleDocument());
270   } else if (isRoot()) {
271     // erase the active document if root is closed
272     aPM->setActiveDocument(DocumentPtr());
273   }
274   // close all subs
275   const std::set<std::string> aSubs = subDocuments(true);
276   std::set<std::string>::iterator aSubIter = aSubs.begin();
277   for (; aSubIter != aSubs.end(); aSubIter++) {
278     std::shared_ptr<Model_Document> aSub = subDoc(*aSubIter);
279     if (aSub->myObjs) // if it was not closed before
280       aSub->close(theForever);
281   }
282
283   // close for thid document needs no transaction in this document
284   std::static_pointer_cast<Model_Session>(Model_Session::get())->setCheckTransactions(false);
285
286   // close all only if it is really asked, otherwise it can be undoed/redoed
287   if (theForever) {
288     // flush everything to avoid messages with bad objects
289     delete myObjs;
290     myObjs = 0;
291     if (myDoc->CanClose() == CDM_CCS_OK)
292       myDoc->Close();
293     mySelectionFeature.reset();
294   } else {
295     setCurrentFeature(FeaturePtr(), false); // disables all features
296   }
297
298   std::static_pointer_cast<Model_Session>(Model_Session::get())->setCheckTransactions(true);
299 }
300
301 void Model_Document::startOperation()
302 {
303   incrementTransactionID(); // outside of transaction in order to avoid empty transactions keeping
304   if (myDoc->HasOpenCommand()) {  // start of nested command
305     if (myDoc->CommitCommand()) { // commit the current: it will contain all nested after compactification
306       myTransactions.rbegin()->myOCAFNum++; // if has open command, the list is not empty
307     }
308     myNestedNum.push_back(0); // start of nested operation with zero transactions inside yet
309     myDoc->OpenCommand();
310   } else {  // start the simple command
311     myDoc->NewCommand();
312   }
313   // starts a new operation
314   myTransactions.push_back(Transaction());
315   if (!myNestedNum.empty())
316     (*myNestedNum.rbegin())++;
317   myRedos.clear();
318   // new command for all subs
319   const std::set<std::string> aSubs = subDocuments(true);
320   std::set<std::string>::iterator aSubIter = aSubs.begin();
321   for (; aSubIter != aSubs.end(); aSubIter++)
322     subDoc(*aSubIter)->startOperation();
323 }
324
325 void Model_Document::compactNested()
326 {
327   if (!myNestedNum.empty()) {
328     int aNumToCompact = *(myNestedNum.rbegin());
329     int aSumOfTransaction = 0;
330     for(int a = 0; a < aNumToCompact; a++) {
331       aSumOfTransaction += myTransactions.rbegin()->myOCAFNum;
332       myTransactions.pop_back();
333     }
334     // the latest transaction is the start of lower-level operation which startes the nested
335     myTransactions.rbegin()->myOCAFNum += aSumOfTransaction;
336     myNestedNum.pop_back();
337   }
338 }
339
340 /// Compares the content ofthe given attributes, returns true if equal.
341 /// This method is used to avoid empty transactions when only "current" is changed
342 /// to some value and then comes back in this transaction, so, it compares only
343 /// references and Boolean and Integer Arrays for the current moment.
344 static bool isEqualContent(Handle(TDF_Attribute) theAttr1, Handle(TDF_Attribute) theAttr2)
345 {
346   if (Standard_GUID::IsEqual(theAttr1->ID(), TDF_Reference::GetID())) { // reference
347     Handle(TDF_Reference) aRef1 = Handle(TDF_Reference)::DownCast(theAttr1);
348     Handle(TDF_Reference) aRef2 = Handle(TDF_Reference)::DownCast(theAttr2);
349     if (aRef1.IsNull() && aRef2.IsNull())
350       return true;
351     if (aRef1.IsNull() || aRef2.IsNull())
352       return false;
353     return aRef1->Get().IsEqual(aRef2->Get()) == Standard_True;
354   } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDataStd_BooleanArray::GetID())) {
355     Handle(TDataStd_BooleanArray) anArr1 = Handle(TDataStd_BooleanArray)::DownCast(theAttr1);
356     Handle(TDataStd_BooleanArray) anArr2 = Handle(TDataStd_BooleanArray)::DownCast(theAttr2);
357     if (anArr1.IsNull() && anArr2.IsNull())
358       return true;
359     if (anArr1.IsNull() || anArr2.IsNull())
360       return false;
361     if (anArr1->Lower() == anArr2->Lower() && anArr1->Upper() == anArr2->Upper()) {
362       for(int a = anArr1->Lower(); a <= anArr1->Upper(); a++)
363         if (anArr1->Value(a) != anArr2->Value(a))
364           return false;
365       return true;
366     }
367   } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDataStd_IntegerArray::GetID())) {
368     Handle(TDataStd_IntegerArray) anArr1 = Handle(TDataStd_IntegerArray)::DownCast(theAttr1);
369     Handle(TDataStd_IntegerArray) anArr2 = Handle(TDataStd_IntegerArray)::DownCast(theAttr2);
370     if (anArr1.IsNull() && anArr2.IsNull())
371       return true;
372     if (anArr1.IsNull() || anArr2.IsNull())
373       return false;
374     if (anArr1->Lower() == anArr2->Lower() && anArr1->Upper() == anArr2->Upper()) {
375       for(int a = anArr1->Lower(); a <= anArr1->Upper(); a++)
376         if (anArr1->Value(a) != anArr2->Value(a)) {
377           // avoid the transaction ID checking
378           if (a == 2 && anArr1->Upper() == 2 && anArr2->Label().Tag() == 1 &&
379             (anArr2->Label().Depth() == 4 || anArr2->Label().Depth() == 6))
380             continue;
381           return false;
382         }
383       return true;
384     }
385   }
386   return false;
387 }
388
389 /// Returns true if the last transaction is actually empty: modification to te same values 
390 /// were performed only
391 static bool isEmptyTransaction(const Handle(TDocStd_Document)& theDoc) {
392   Handle(TDF_Delta) aDelta;
393   aDelta = theDoc->GetUndos().Last();
394   TDF_LabelList aDeltaList;
395   aDelta->Labels(aDeltaList); // it clears list, so, use new one and then append to the result
396   for(TDF_ListIteratorOfLabelList aListIter(aDeltaList); aListIter.More(); aListIter.Next()) {
397     return false;
398   }
399   // add also label of the modified attributes
400   const TDF_AttributeDeltaList& anAttrs = aDelta->AttributeDeltas();
401   for (TDF_ListIteratorOfAttributeDeltaList anAttr(anAttrs); anAttr.More(); anAttr.Next()) {
402     Handle(TDF_AttributeDelta)& anADelta = anAttr.Value();
403     if (!anADelta->Label().IsNull() && !anADelta->Attribute().IsNull()) {
404       Handle(TDF_Attribute) aCurrentAttr;
405       if (anADelta->Label().FindAttribute(anADelta->Attribute()->ID(), aCurrentAttr)) {
406         if (isEqualContent(anADelta->Attribute(), aCurrentAttr)) {
407           continue; // attribute is not changed actually
408         }
409       }
410     }
411     return false;
412   }
413   return true;
414 }
415
416 bool Model_Document::finishOperation()
417 {
418   bool isNestedClosed = !myDoc->HasOpenCommand() && !myNestedNum.empty();
419   static std::shared_ptr<Model_Session> aSession = 
420     std::static_pointer_cast<Model_Session>(Model_Session::get());
421   // do it before flashes to enable and recompute nesting features correctly
422   if (myNestedNum.empty() || (isNestedClosed && myNestedNum.size() == 1)) {
423     // if all nested operations are closed, make current the higher level objects (to perform 
424     // it in the python scripts correctly): sketch become current after creation ofsub-elements
425     FeaturePtr aCurrent = currentFeature(false);
426     CompositeFeaturePtr aMain, aNext = ModelAPI_Tools::compositeOwner(aCurrent);
427     while(aNext.get()) {
428       aMain = aNext;
429       aNext = ModelAPI_Tools::compositeOwner(aMain);
430     }
431     if (aMain.get() && aMain != aCurrent)
432       setCurrentFeature(aMain, false);
433   }
434   myObjs->synchronizeBackRefs();
435   Events_Loop* aLoop = Events_Loop::loop();
436   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
437   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
438   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
439   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
440   // this must be here just after everything is finished but before real transaction stop
441   // to avoid messages about modifications outside of the transaction
442   // and to rebuild everything after all updates and creates
443   if (isRoot()) { // once for root document
444     Events_Loop::loop()->autoFlush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
445     static std::shared_ptr<Events_Message> aFinishMsg
446       (new Events_Message(Events_Loop::eventByName("FinishOperation")));
447     Events_Loop::loop()->send(aFinishMsg);
448     Events_Loop::loop()->autoFlush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED), false);
449   }
450   // to avoid "updated" message appearance by updater
451   //aLoop->clear(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
452
453   // finish for all subs first: to avoid nested finishing and "isOperation" calls problems inside
454   bool aResult = false;
455   const std::set<std::string> aSubs = subDocuments(true);
456   std::set<std::string>::iterator aSubIter = aSubs.begin();
457   for (; aSubIter != aSubs.end(); aSubIter++)
458     if (subDoc(*aSubIter)->finishOperation())
459       aResult = true;
460
461   // transaction may be empty if this document was created during this transaction (create part)
462   if (!myTransactions.empty() && myDoc->CommitCommand()) { // if commit is successfull, just increment counters
463     if (isEmptyTransaction(myDoc)) { // erase this transaction
464       myDoc->Undo();
465       myDoc->ClearRedos();
466     } else {
467       myTransactions.rbegin()->myOCAFNum++;
468       aResult = true;
469     }
470   }
471
472   if (isNestedClosed) {
473     compactNested();
474   }
475   if (!aResult && !myTransactions.empty() /* it can be for just created part document */)
476     aResult = myTransactions.rbegin()->myOCAFNum != 0;
477
478   if (!aResult && isRoot()) {
479     // nothing inside in all documents, so remove this transaction from the transactions list
480     undoInternal(true, false);
481   }
482   // on finish clear redos in any case (issue 446) and for all subs (issue 408)
483   myDoc->ClearRedos();
484   myRedos.clear();
485   for (aSubIter = aSubs.begin(); aSubIter != aSubs.end(); aSubIter++) {
486     subDoc(*aSubIter)->myDoc->ClearRedos();
487     subDoc(*aSubIter)->myRedos.clear();
488   }
489
490   return aResult;
491 }
492
493 /// Returns in theDelta labels that has been modified in the latest transaction of theDoc
494 static void modifiedLabels(const Handle(TDocStd_Document)& theDoc, TDF_LabelList& theDelta,
495   const bool isRedo = false) {
496   Handle(TDF_Delta) aDelta;
497   if (isRedo)
498     aDelta = theDoc->GetRedos().First();
499   else 
500     aDelta = theDoc->GetUndos().Last();
501   TDF_LabelList aDeltaList;
502   aDelta->Labels(aDeltaList); // it clears list, so, use new one and then append to the result
503   for(TDF_ListIteratorOfLabelList aListIter(aDeltaList); aListIter.More(); aListIter.Next()) {
504     theDelta.Append(aListIter.Value());
505   }
506   // add also label of the modified attributes
507   const TDF_AttributeDeltaList& anAttrs = aDelta->AttributeDeltas();
508   for (TDF_ListIteratorOfAttributeDeltaList anAttr(anAttrs); anAttr.More(); anAttr.Next()) {
509     theDelta.Append(anAttr.Value()->Label());
510   }
511 }
512
513 void Model_Document::abortOperation()
514 {
515   TDF_LabelList aDeltaLabels; // labels that are updated during "abort"
516   if (!myNestedNum.empty() && !myDoc->HasOpenCommand()) {  // abort all what was done in nested
517     compactNested();
518     // store undo-delta here as undo actually does in the method later
519     int a, aNumTransactions = myTransactions.rbegin()->myOCAFNum;
520     for(a = 0; a < aNumTransactions; a++) {
521       modifiedLabels(myDoc, aDeltaLabels);
522       myDoc->Undo();
523     }
524     for(a = 0; a < aNumTransactions; a++) {
525       myDoc->Redo();
526     }
527
528     undoInternal(false, false);
529     myDoc->ClearRedos();
530     myRedos.clear();
531   } else { // abort the current
532     int aNumTransactions = myTransactions.rbegin()->myOCAFNum;
533     myTransactions.pop_back();
534     if (!myNestedNum.empty())
535       (*myNestedNum.rbegin())--;
536     // roll back the needed number of transactions
537     // make commit/undo to get the modification delta
538     //myDoc->AbortCommand();
539     if (myDoc->CommitCommand()) {
540       modifiedLabels(myDoc, aDeltaLabels);
541       myDoc->Undo();
542     }
543     for(int a = 0; a < aNumTransactions; a++) {
544       modifiedLabels(myDoc, aDeltaLabels);
545       myDoc->Undo();
546     }
547     myDoc->ClearRedos();
548   }
549   // abort for all subs, flushes will be later, in the end of root abort
550   const std::set<std::string> aSubs = subDocuments(true);
551   std::set<std::string>::iterator aSubIter = aSubs.begin();
552   for (; aSubIter != aSubs.end(); aSubIter++)
553     subDoc(*aSubIter)->abortOperation();
554   // references may be changed because they are set in attributes on the fly
555   myObjs->synchronizeFeatures(aDeltaLabels, true, isRoot());
556 }
557
558 bool Model_Document::isOperation() const
559 {
560   // operation is opened for all documents: no need to check subs
561   return myDoc->HasOpenCommand() == Standard_True ;
562 }
563
564 bool Model_Document::isModified()
565 {
566   // is modified if at least one operation was commited and not undoed
567   return myTransactions.size() != myTransactionSave || isOperation();
568 }
569
570 bool Model_Document::canUndo()
571 {
572   // issue 406 : if transaction is opened, but nothing to undo behind, can not undo
573   int aCurrentNum = isOperation() ? 1 : 0;
574   if (myDoc->GetAvailableUndos() > 0 && 
575       (myNestedNum.empty() || *myNestedNum.rbegin() - aCurrentNum > 0) && // there is something to undo in nested
576       myTransactions.size() - aCurrentNum > 0 /* for omitting the first useless transaction */)
577     return true;
578   // check other subs contains operation that can be undoed
579   const std::set<std::string> aSubs = subDocuments(true);
580   std::set<std::string>::iterator aSubIter = aSubs.begin();
581   for (; aSubIter != aSubs.end(); aSubIter++) {
582     std::shared_ptr<Model_Document> aSub = subDoc(*aSubIter);
583     if (aSub->myObjs) {// if it was not closed before
584       if (aSub->canUndo())
585         return true;
586     }
587   }
588
589   return false;
590 }
591
592 void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchronize)
593 {
594   int aNumTransactions = myTransactions.rbegin()->myOCAFNum;
595   myRedos.push_back(*myTransactions.rbegin());
596   myTransactions.pop_back();
597   if (!myNestedNum.empty())
598     (*myNestedNum.rbegin())--;
599   // roll back the needed number of transactions
600   TDF_LabelList aDeltaLabels;
601   for(int a = 0; a < aNumTransactions; a++) {
602     if (theSynchronize)
603       modifiedLabels(myDoc, aDeltaLabels);
604     myDoc->Undo();
605   }
606
607   if (theWithSubs) {
608     // undo for all subs
609     const std::set<std::string> aSubs = subDocuments(true);
610     std::set<std::string>::iterator aSubIter = aSubs.begin();
611     for (; aSubIter != aSubs.end(); aSubIter++) {
612       if (!subDoc(*aSubIter)->myObjs)
613         continue;
614       subDoc(*aSubIter)->undoInternal(theWithSubs, theSynchronize);
615     }
616   }
617   // after undo of all sub-documents to avoid updates on not-modified data (issue 370)
618   if (theSynchronize) {
619     myObjs->synchronizeFeatures(aDeltaLabels, true, isRoot());
620     // update the current features status
621     setCurrentFeature(currentFeature(false), false);
622   }
623 }
624
625 void Model_Document::undo()
626 {
627   undoInternal(true, true);
628 }
629
630 bool Model_Document::canRedo()
631 {
632   if (!myRedos.empty())
633     return true;
634   // check other subs contains operation that can be redoed
635   const std::set<std::string> aSubs = subDocuments(true);
636   std::set<std::string>::iterator aSubIter = aSubs.begin();
637   for (; aSubIter != aSubs.end(); aSubIter++) {
638     if (!subDoc(*aSubIter)->myObjs)
639       continue;
640     if (subDoc(*aSubIter)->canRedo())
641       return true;
642   }
643   return false;
644 }
645
646 void Model_Document::redo()
647 {
648   if (!myNestedNum.empty())
649     (*myNestedNum.rbegin())++;
650   int aNumRedos = myRedos.rbegin()->myOCAFNum;
651   myTransactions.push_back(*myRedos.rbegin());
652   myRedos.pop_back();
653   TDF_LabelList aDeltaLabels;
654   for(int a = 0; a < aNumRedos; a++) {
655     modifiedLabels(myDoc, aDeltaLabels, true);
656     myDoc->Redo();
657   }
658
659   // redo for all subs
660   const std::set<std::string> aSubs = subDocuments(true);
661   std::set<std::string>::iterator aSubIter = aSubs.begin();
662   for (; aSubIter != aSubs.end(); aSubIter++)
663     subDoc(*aSubIter)->redo();
664
665   // after redo of all sub-documents to avoid updates on not-modified data (issue 370)
666   myObjs->synchronizeFeatures(aDeltaLabels, true, isRoot());
667   // update the current features status
668   setCurrentFeature(currentFeature(false), false);
669 }
670
671 std::list<std::string> Model_Document::undoList() const
672 {
673   std::list<std::string> aResult;
674   // the number of skipped current operations (on undo they will be aborted)
675   int aSkipCurrent = isOperation() ? 1 : 0;
676   std::list<Transaction>::const_reverse_iterator aTrIter = myTransactions.crbegin();
677   int aNumUndo = myTransactions.size();
678   if (!myNestedNum.empty())
679     aNumUndo = *myNestedNum.rbegin();
680   for( ; aNumUndo > 0; aTrIter++, aNumUndo--) {
681     if (aSkipCurrent == 0) aResult.push_back(aTrIter->myId);
682     else aSkipCurrent--;
683   }
684   return aResult;
685 }
686
687 std::list<std::string> Model_Document::redoList() const
688 {
689   std::list<std::string> aResult;
690   std::list<Transaction>::const_reverse_iterator aTrIter = myRedos.crbegin();
691   for( ; aTrIter != myRedos.crend(); aTrIter++) {
692     aResult.push_back(aTrIter->myId);
693   }
694   return aResult;
695 }
696
697 void Model_Document::operationId(const std::string& theId)
698 {
699   myTransactions.rbegin()->myId = theId;
700 }
701
702 FeaturePtr Model_Document::addFeature(std::string theID, const bool theMakeCurrent)
703 {
704   std::shared_ptr<Model_Session> aSession = 
705     std::dynamic_pointer_cast<Model_Session>(ModelAPI_Session::get());
706   FeaturePtr aFeature = aSession->createFeature(theID, this);
707   if (!aFeature)
708     return aFeature;
709   aFeature->init();
710   Model_Document* aDocToAdd;
711   if (!aFeature->documentToAdd().empty()) { // use the customized document to add
712     if (aFeature->documentToAdd() != kind()) { // the root document by default
713       aDocToAdd = std::dynamic_pointer_cast<Model_Document>(aSession->moduleDocument()).get();
714     } else {
715       aDocToAdd = this;
716     }
717   } else { // if customized is not presented, add to "this" document
718     aDocToAdd = this;
719   }
720   if (aFeature) {
721     // searching for feature after which must be added the next feature: this is the current feature
722     // but also all sub-features of this feature
723     FeaturePtr aCurrent = aDocToAdd->currentFeature(false);
724     bool isModified = true;
725     for(CompositeFeaturePtr aComp = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aCurrent);
726         aComp.get() && isModified; 
727         aComp = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aCurrent)) {
728       isModified =  false;
729       int aSubs = aComp->numberOfSubs(false);
730       for(int a = 0; a < aSubs; a++) {
731         FeaturePtr aSub = aComp->subFeature(a, false);
732         if (myObjs->isLater(aSub, aCurrent)) {
733           isModified =  true;
734           aCurrent = aSub;
735         }
736       }
737     }
738     aDocToAdd->myObjs->addFeature(aFeature, aCurrent);
739     if (!aFeature->isAction()) {  // do not add action to the data model
740       if (theMakeCurrent)  // after all this feature stays in the document, so make it current
741         aDocToAdd->setCurrentFeature(aFeature, false);
742     } else { // feature must be executed
743        // no creation event => updater not working, problem with remove part
744       aFeature->execute();
745     }
746   }
747   return aFeature;
748 }
749
750
751 void Model_Document::refsToFeature(FeaturePtr theFeature,
752   std::set<std::shared_ptr<ModelAPI_Feature> >& theRefs, const bool isSendError)
753 {
754   myObjs->refsToFeature(theFeature, theRefs, isSendError);
755 }
756
757 void Model_Document::removeFeature(FeaturePtr theFeature)
758 {
759   myObjs->removeFeature(theFeature);
760 }
761
762 void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis)
763 {
764   myObjs->moveFeature(theMoved, theAfterThis);
765   if (theAfterThis == currentFeature(true))
766     setCurrentFeature(theMoved, true);
767 }
768
769 void Model_Document::updateHistory(const std::shared_ptr<ModelAPI_Object> theObject)
770 {
771   myObjs->updateHistory(theObject);
772 }
773
774 void Model_Document::updateHistory(const std::string theGroup)
775 {
776   myObjs->updateHistory(theGroup);
777 }
778
779 std::shared_ptr<ModelAPI_Document> Model_Document::subDocument(std::string theDocID)
780 {
781   return Model_Application::getApplication()->getDocument(theDocID);
782 }
783
784 const std::set<std::string> Model_Document::subDocuments(const bool theActivatedOnly) const
785 {
786   std::set<std::string> aResult;
787   std::list<ResultPtr> aPartResults;
788   myObjs->allResults(ModelAPI_ResultPart::group(), aPartResults);
789   std::list<ResultPtr>::iterator aPartRes = aPartResults.begin();
790   for(; aPartRes != aPartResults.end(); aPartRes++) {
791     ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aPartRes);
792     if (aPart && (!theActivatedOnly || aPart->isActivated())) {
793       aResult.insert(aPart->original()->data()->name());
794     }
795   }
796   return aResult;
797 }
798
799 std::shared_ptr<Model_Document> Model_Document::subDoc(std::string theDocID)
800 {
801   // just store sub-document identifier here to manage it later
802   return std::dynamic_pointer_cast<Model_Document>(
803     Model_Application::getApplication()->getDocument(theDocID));
804 }
805
806 ObjectPtr Model_Document::object(const std::string& theGroupID, const int theIndex)
807 {
808   return myObjs->object(theGroupID, theIndex);
809 }
810
811 std::shared_ptr<ModelAPI_Object> Model_Document::objectByName(
812     const std::string& theGroupID, const std::string& theName)
813 {
814   return myObjs->objectByName(theGroupID, theName);
815 }
816
817 const int Model_Document::index(std::shared_ptr<ModelAPI_Object> theObject)
818 {
819   return myObjs->index(theObject);
820 }
821
822 int Model_Document::size(const std::string& theGroupID)
823 {
824   return myObjs->size(theGroupID);
825 }
826
827 std::shared_ptr<ModelAPI_Feature> Model_Document::currentFeature(const bool theVisible)
828 {
829   if (!myObjs) // on close document feature destruction it may call this method
830     return std::shared_ptr<ModelAPI_Feature>();
831   TDF_Label aRefLab = generalLabel().FindChild(TAG_CURRENT_FEATURE);
832   Handle(TDF_Reference) aRef;
833   if (aRefLab.FindAttribute(TDF_Reference::GetID(), aRef)) {
834     TDF_Label aLab = aRef->Get();
835     FeaturePtr aResult = myObjs->feature(aLab);
836     if (theVisible) { // get nearest visible (in history) going up
837       while(aResult.get() &&  !aResult->isInHistory()) {
838         aResult = myObjs->nextFeature(aResult, true);
839       }
840     }
841     return aResult;
842   }
843   return std::shared_ptr<ModelAPI_Feature>(); // null feature means the higher than first
844 }
845
846 void Model_Document::setCurrentFeature(
847   std::shared_ptr<ModelAPI_Feature> theCurrent, const bool theVisible)
848 {
849   // blocks the flush signals to avoid each objects visualization in the viewer
850   // they should not be shown once after all modifications are performed
851   Events_Loop* aLoop = Events_Loop::loop();
852   bool isActive = aLoop->activateFlushes(false);
853
854   TDF_Label aRefLab = generalLabel().FindChild(TAG_CURRENT_FEATURE);
855   CompositeFeaturePtr aMain; // main feature that may nest the new current
856   std::set<FeaturePtr> anOwners; // composites that contain theCurrent (with any level of nesting)
857   if (theCurrent.get()) {
858     aMain = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theCurrent);
859     CompositeFeaturePtr anOwner = ModelAPI_Tools::compositeOwner(theCurrent);
860     while(anOwner.get()) {
861       if (!aMain.get()) {
862         aMain = anOwner;
863       }
864       anOwners.insert(anOwner);
865       anOwner = ModelAPI_Tools::compositeOwner(anOwner);
866     }
867   }
868
869   if (theVisible) { // make features below which are not in history also enabled: sketch subs
870     FeaturePtr aNext = 
871       theCurrent.get() ? myObjs->nextFeature(theCurrent) : myObjs->firstFeature();
872     for (; aNext.get(); aNext = myObjs->nextFeature(theCurrent)) {
873       if (aNext->isInHistory()) {
874         break; // next in history is not needed
875       } else { // next not in history is good for making current
876         theCurrent = aNext;
877       }
878     }
879   }
880   if (theCurrent.get()) {
881     std::shared_ptr<Model_Data> aData = std::static_pointer_cast<Model_Data>(theCurrent->data());
882     if (!aData.get() || !aData->isValid()) {
883       aLoop->activateFlushes(isActive);
884       return;
885     }
886     TDF_Label aFeatureLabel = aData->label().Father();
887
888     Handle(TDF_Reference) aRef;
889     if (aRefLab.FindAttribute(TDF_Reference::GetID(), aRef)) {
890       aRef->Set(aFeatureLabel);
891     } else {
892       aRef = TDF_Reference::Set(aRefLab, aFeatureLabel);
893     }
894   } else { // remove reference for the null feature
895     aRefLab.ForgetAttribute(TDF_Reference::GetID());
896   }
897   // make all features after this feature disabled in reversed order (to remove results without deps)
898   static Events_ID aRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
899   static Events_ID aCreateEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
900   static Events_ID aDeleteEvent = aLoop->eventByName(EVENT_OBJECT_DELETED);
901
902   bool aPassed = false; // flag that the current object is already passed in cycle
903   FeaturePtr anIter = myObjs->lastFeature();
904   bool aWasChanged = false;
905   for(; anIter.get(); anIter = myObjs->nextFeature(anIter, true)) {
906     // check this before passed become enabled: the current feature is enabled!
907     if (anIter == theCurrent) aPassed = true;
908
909     bool aDisabledFlag = !aPassed;
910     if (aMain.get()) {
911       if (aMain->isSub(anIter)) // sub-elements of not-disabled feature are not disabled
912         aDisabledFlag = false;
913       else if (anOwners.find(anIter) != anOwners.end()) // disable the higher-level feature is the nested is the current
914         aDisabledFlag = true;
915     }
916
917     if (anIter->getKind() == "Parameter") {// parameters are always out of the history of features, but not parameters
918       if (theCurrent.get() && theCurrent->getKind() != "Parameter")
919         aDisabledFlag = false;
920     }
921     if (anIter->setDisabled(aDisabledFlag)) {
922       // state of feature is changed => so feature become updated
923       static Events_ID anUpdateEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED);
924       ModelAPI_EventCreator::get()->sendUpdated(anIter, anUpdateEvent);
925       // flush is in the end of this method
926       ModelAPI_EventCreator::get()->sendUpdated(anIter, aRedispEvent /*, false*/);
927       aWasChanged = true;
928     }
929     // update for everyone the concealment flag immideately: on edit feature in the midle of history
930     if (aWasChanged) {
931       const std::list<std::shared_ptr<ModelAPI_Result> >& aResList = anIter->results();
932       std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRes = aResList.begin();
933       for(; aRes != aResList.end(); aRes++) {
934         if ((*aRes).get() && (*aRes)->data()->isValid() && !(*aRes)->isDisabled())
935           std::dynamic_pointer_cast<Model_Data>((*aRes)->data())->updateConcealmentFlag();
936       }
937
938     }
939   }
940   // unblock  the flush signals and up them after this
941   aLoop->activateFlushes(isActive);
942 }
943
944 void Model_Document::setCurrentFeatureUp()
945 {
946   // on remove just go up for minimum step: highlight external objects in sketch causes 
947   // problems if it is true: here and in "setCurrentFeature"
948   FeaturePtr aCurrent = currentFeature(false);
949   if (aCurrent.get()) { // if not, do nothing because null is the upper
950     FeaturePtr aPrev = myObjs->nextFeature(aCurrent, true);
951     // do not flush: it is called only on remove, it will be flushed in the end of transaction
952     setCurrentFeature(aPrev, false);
953   }
954 }
955
956 TDF_Label Model_Document::generalLabel() const
957 {
958   return myDoc->Main().FindChild(TAG_GENERAL);
959 }
960
961 std::shared_ptr<ModelAPI_ResultConstruction> Model_Document::createConstruction(
962     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
963 {
964   return myObjs->createConstruction(theFeatureData, theIndex);
965 }
966
967 std::shared_ptr<ModelAPI_ResultBody> Model_Document::createBody(
968     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
969 {
970   return myObjs->createBody(theFeatureData, theIndex);
971 }
972
973 std::shared_ptr<ModelAPI_ResultPart> Model_Document::createPart(
974     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
975 {
976   return myObjs->createPart(theFeatureData, theIndex);
977 }
978
979 std::shared_ptr<ModelAPI_ResultPart> Model_Document::copyPart(
980       const std::shared_ptr<ModelAPI_ResultPart>& theOrigin,
981       const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
982 {
983   return myObjs->copyPart(theOrigin, theFeatureData, theIndex);
984 }
985
986 std::shared_ptr<ModelAPI_ResultGroup> Model_Document::createGroup(
987     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
988 {
989   return myObjs->createGroup(theFeatureData, theIndex);
990 }
991
992 std::shared_ptr<ModelAPI_ResultParameter> Model_Document::createParameter(
993       const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
994 {
995   return myObjs->createParameter(theFeatureData, theIndex);
996 }
997
998 std::shared_ptr<ModelAPI_Feature> Model_Document::feature(
999     const std::shared_ptr<ModelAPI_Result>& theResult)
1000 {
1001   return myObjs->feature(theResult);
1002 }
1003
1004 Standard_Integer HashCode(const TDF_Label& theLab, const Standard_Integer theUpper)
1005 {
1006   return TDF_LabelMapHasher::HashCode(theLab, theUpper);
1007
1008 }
1009 Standard_Boolean IsEqual(const TDF_Label& theLab1, const TDF_Label& theLab2)
1010 {
1011   return TDF_LabelMapHasher::IsEqual(theLab1, theLab2);
1012 }
1013
1014 void Model_Document::addNamingName(const TDF_Label theLabel, std::string theName)
1015 {
1016   myNamingNames[theName] = theLabel;
1017 }
1018
1019 TDF_Label Model_Document::findNamingName(std::string theName)
1020 {
1021   std::map<std::string, TDF_Label>::iterator aFind = myNamingNames.find(theName);
1022   if (aFind == myNamingNames.end())
1023     return TDF_Label(); // not found
1024   return aFind->second;
1025 }
1026
1027 ResultPtr Model_Document::findByName(const std::string theName)
1028 {
1029   return myObjs->findByName(theName);
1030 }
1031
1032 std::list<std::shared_ptr<ModelAPI_Feature> > Model_Document::allFeatures()
1033 {
1034   return myObjs->allFeatures();
1035 }
1036
1037 void Model_Document::setActive(const bool theFlag)
1038 {
1039   if (theFlag != myIsActive) {
1040     myIsActive = theFlag;
1041     // redisplay all the objects of this part
1042     static Events_Loop* aLoop = Events_Loop::loop();
1043     static Events_ID aRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
1044
1045     for(int a = size(ModelAPI_Feature::group()) - 1; a >= 0; a--) {
1046       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(
1047         object(ModelAPI_Feature::group(), a));
1048       if (aFeature.get() && aFeature->data()->isValid()) {
1049         const std::list<std::shared_ptr<ModelAPI_Result> >& aResList = aFeature->results();
1050         std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRes = aResList.begin();
1051         for(; aRes != aResList.end(); aRes++) {
1052           ModelAPI_EventCreator::get()->sendUpdated(*aRes, aRedispEvent);
1053           // #issue 1048: sub-compsolids also
1054           ResultCompSolidPtr aCompRes = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(*aRes);
1055           if (aCompRes.get()) {
1056             int aNumSubs = aCompRes->numberOfSubs();
1057             for(int a = 0; a < aNumSubs; a++) {
1058               ResultPtr aSub = aCompRes->subResult(a);
1059               if (aSub.get()) {
1060                 ModelAPI_EventCreator::get()->sendUpdated(aSub, aRedispEvent);
1061               }
1062             }
1063           }
1064         }
1065       }
1066     }
1067   }
1068 }
1069
1070 bool Model_Document::isActive() const
1071 {
1072   return myIsActive;
1073 }
1074
1075 int Model_Document::transactionID()
1076 {
1077   Handle(TDataStd_Integer) anIndex;
1078   if (!generalLabel().FindChild(TAG_CURRENT_TRANSACTION).
1079       FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
1080     anIndex = TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), 1);
1081   }
1082   return anIndex->Get();
1083 }
1084
1085 void Model_Document::incrementTransactionID()
1086 {
1087   int aNewVal = transactionID() + 1;
1088   TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), aNewVal);
1089 }
1090 void Model_Document::decrementTransactionID()
1091 {
1092   int aNewVal = transactionID() - 1;
1093   TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), aNewVal);
1094 }
1095
1096 bool Model_Document::isOpened()
1097 {
1098   return myObjs && !myDoc.IsNull();
1099 }
1100
1101 int Model_Document::numInternalFeatures()
1102 {
1103   return myObjs->numInternalFeatures();
1104 }
1105
1106 std::shared_ptr<ModelAPI_Feature> Model_Document::internalFeature(const int theIndex)
1107 {
1108   return myObjs->internalFeature(theIndex);
1109 }
1110
1111 void Model_Document::synchronizeTransactions()
1112 {
1113   Model_Document* aRoot = 
1114     std::dynamic_pointer_cast<Model_Document>(ModelAPI_Session::get()->moduleDocument()).get();
1115   if (aRoot == this)
1116     return; // don't need to synchronise root with root
1117
1118   std::shared_ptr<Model_Session> aSession = 
1119     std::dynamic_pointer_cast<Model_Session>(Model_Session::get());
1120   while(myRedos.size() > aRoot->myRedos.size()) { // remove redos in this
1121     aSession->setCheckTransactions(false);
1122     redo();
1123     aSession->setCheckTransactions(true);
1124   }
1125   /* this case can not be reproduced in any known case for the current moment, so, just comment
1126   while(myRedos.size() < aRoot->myRedos.size()) { // add more redos in this
1127     undoInternal(false, true);
1128   }*/
1129 }
1130
1131 /// Feature that is used for selection in the Part document by the external request
1132 class Model_SelectionInPartFeature : public ModelAPI_Feature {
1133 public:
1134   /// Nothing to do in constructor
1135   Model_SelectionInPartFeature() : ModelAPI_Feature() {}
1136
1137   /// Returns the unique kind of a feature
1138   virtual const std::string& getKind() {
1139     static std::string MY_KIND("InternalSelectionInPartFeature");
1140     return MY_KIND;
1141   }
1142   /// Request for initialization of data model of the object: adding all attributes
1143   virtual void initAttributes() {
1144     data()->addAttribute("selection", ModelAPI_AttributeSelectionList::typeId());
1145   }
1146   /// Nothing to do in the execution function
1147   virtual void execute() {}
1148
1149 };
1150
1151 //! Returns the feature that is used for calculation of selection externally from the document
1152 AttributeSelectionListPtr Model_Document::selectionInPartFeature()
1153 {
1154   // return already created, otherwise create
1155   if (!mySelectionFeature.get() || !mySelectionFeature->data()->isValid()) {
1156     // create a new one
1157     mySelectionFeature = FeaturePtr(new Model_SelectionInPartFeature);
1158   
1159     TDF_Label aFeatureLab = generalLabel().FindChild(TAG_SELECTION_FEATURE);
1160     std::shared_ptr<Model_Data> aData(new Model_Data);
1161     aData->setLabel(aFeatureLab.FindChild(1));
1162     aData->setObject(mySelectionFeature);
1163     mySelectionFeature->setDoc(myObjs->owner());
1164     mySelectionFeature->setData(aData);
1165     std::string aName = id() + "_Part";
1166     mySelectionFeature->data()->setName(aName);
1167     mySelectionFeature->setDoc(myObjs->owner());
1168     mySelectionFeature->initAttributes();
1169   }
1170   return mySelectionFeature->selectionList("selection");
1171 }
1172
1173 FeaturePtr Model_Document::lastFeature()
1174 {
1175   if (myObjs)
1176     return myObjs->lastFeature();
1177   return FeaturePtr();
1178 }