]> SALOME platform Git repositories - modules/shaper.git/blob - src/Model/Model_Document.cpp
Salome HOME
#2027 Sketcher Trim feature - highlight should be cleared when the mouse is moved...
[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 #include <ModelAPI_ResultBody.h>
19 #include <Events_Loop.h>
20 #include <Events_InfoMessage.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_ReferenceList.hxx>
27 #include <TDataStd_IntegerArray.hxx>
28 #include <TDataStd_HLabelArray1.hxx>
29 #include <TDataStd_Name.hxx>
30 #include <TDataStd_AsciiString.hxx>
31 #include <TDF_Reference.hxx>
32 #include <TDF_ChildIDIterator.hxx>
33 #include <TDF_LabelMapHasher.hxx>
34 #include <TDF_Delta.hxx>
35 #include <TDF_AttributeDelta.hxx>
36 #include <TDF_AttributeDeltaList.hxx>
37 #include <TDF_ListIteratorOfAttributeDeltaList.hxx>
38 #include <TDF_ListIteratorOfLabelList.hxx>
39 #include <TDF_LabelMap.hxx>
40 #include <TDF_DeltaOnAddition.hxx>
41 #include <TNaming_Builder.hxx>
42 #include <TNaming_SameShapeIterator.hxx>
43 #include <TNaming_Iterator.hxx>
44 #include <TNaming_NamedShape.hxx>
45 #include <TNaming_Tool.hxx>
46
47 #include <TopExp_Explorer.hxx>
48 #include <TopoDS_Shape.hxx>
49
50 #include <OSD_File.hxx>
51 #include <OSD_Path.hxx>
52 #include <CDF_Session.hxx>
53 #include <CDF_Directory.hxx>
54
55 #include <climits>
56 #ifndef WIN32
57 #include <sys/stat.h>
58 #endif
59
60 #ifdef WIN32
61 # define _separator_ '\\'
62 #else
63 # define _separator_ '/'
64 #endif
65
66 static const int UNDO_LIMIT = 1000;  // number of possible undo operations (big for sketcher)
67
68 static const int TAG_GENERAL = 1;  // general properties tag
69
70 // general sub-labels
71 /// where the reference to the current feature label is located (or no attribute if null feature)
72 static const int TAG_CURRENT_FEATURE = 1; ///< reference to the current feature
73 static const int TAG_CURRENT_TRANSACTION = 2; ///< integer, index of the transaction
74 static const int TAG_SELECTION_FEATURE = 3; ///< integer, tag of the selection feature label
75 static const int TAG_NODES_STATE = 4; ///< array, tag of the Object Browser nodes states
76
77 Model_Document::Model_Document(const int theID, const std::string theKind)
78     : myID(theID), myKind(theKind), myIsActive(false),
79       myDoc(new TDocStd_Document("BinOcaf"))  // binary OCAF format
80 {
81 #ifdef TINSPECTOR
82   CDF_Session::CurrentSession()->Directory()->Add(myDoc);
83 #endif
84   myObjs = new Model_Objects(myDoc->Main());
85   myDoc->SetUndoLimit(UNDO_LIMIT);
86   myTransactionSave = 0;
87   myExecuteFeatures = true;
88   // to have something in the document and avoid empty doc open/save problem
89   // in transaction for nesting correct working
90   myDoc->NewCommand();
91   TDataStd_Integer::Set(myDoc->Main().Father(), 0);
92   // this to avoid creation of integer attribute outside the transaction after undo
93   transactionID();
94   myDoc->CommitCommand();
95 }
96
97 void Model_Document::setThis(DocumentPtr theDoc)
98 {
99   myObjs->setOwner(theDoc);
100 }
101
102 /// Returns the file name of this document by the name of directory and identifier of a document
103 static TCollection_ExtendedString DocFileName(const char* theDirName, const std::string& theID)
104 {
105   TCollection_ExtendedString aPath((const Standard_CString) theDirName);
106   // remove end-separators
107   while(aPath.Length() &&
108         (aPath.Value(aPath.Length()) == '\\' || aPath.Value(aPath.Length()) == '/'))
109     aPath.Remove(aPath.Length());
110   aPath += _separator_;
111   aPath += theID.c_str();
112   aPath += ".cbf";  // standard binary file extension
113   return aPath;
114 }
115
116 bool Model_Document::isRoot() const
117 {
118   return this == Model_Session::get()->moduleDocument().get();
119 }
120
121 bool Model_Document::load(const char* theDirName, const char* theFileName, DocumentPtr theThis)
122 {
123   Handle(Model_Application) anApp = Model_Application::getApplication();
124   if (isRoot()) {
125     anApp->setLoadPath(theDirName);
126   }
127   TCollection_ExtendedString aPath(DocFileName(theDirName, theFileName));
128   PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus) -1;
129   Handle(TDocStd_Document) aLoaded;
130   try {
131     aStatus = anApp->Open(aPath, aLoaded);
132   } catch (Standard_Failure) {
133     Handle(Standard_Failure) aFail = Standard_Failure::Caught();
134     Events_InfoMessage("Model_Document",
135         "Exception in opening of document: %1").arg(aFail->GetMessageString()).send();
136     return false;
137   }
138   bool isError = aStatus != PCDM_RS_OK;
139   if (isError) {
140     switch (aStatus) {
141       case PCDM_RS_UnknownDocument:
142         Events_InfoMessage("Model_Document", "Can not open document").send();
143         break;
144       case PCDM_RS_AlreadyRetrieved:
145         Events_InfoMessage("Model_Document", "Can not open document: already opened").send();
146         break;
147       case PCDM_RS_AlreadyRetrievedAndModified:
148         Events_InfoMessage("Model_Document",
149             "Can not open document: already opened and modified").send();
150         break;
151       case PCDM_RS_NoDriver:
152         Events_InfoMessage("Model_Document",
153                            "Can not open document: driver library is not found").send();
154         break;
155       case PCDM_RS_UnknownFileDriver:
156         Events_InfoMessage("Model_Document",
157                            "Can not open document: unknown driver for opening").send();
158         break;
159       case PCDM_RS_OpenError:
160         Events_InfoMessage("Model_Document", "Can not open document: file open error").send();
161         break;
162       case PCDM_RS_NoVersion:
163         Events_InfoMessage("Model_Document", "Can not open document: invalid version").send();
164         break;
165       case PCDM_RS_NoModel:
166         Events_InfoMessage("Model_Document", "Can not open document: no data model").send();
167         break;
168       case PCDM_RS_NoDocument:
169         Events_InfoMessage("Model_Document", "Can not open document: no document inside").send();
170         break;
171       case PCDM_RS_FormatFailure:
172         Events_InfoMessage("Model_Document", "Can not open document: format failure").send();
173         break;
174       case PCDM_RS_TypeNotFoundInSchema:
175         Events_InfoMessage("Model_Document", "Can not open document: invalid object").send();
176         break;
177       case PCDM_RS_UnrecognizedFileFormat:
178         Events_InfoMessage("Model_Document",
179                            "Can not open document: unrecognized file format").send();
180         break;
181       case PCDM_RS_MakeFailure:
182         Events_InfoMessage("Model_Document", "Can not open document: make failure").send();
183         break;
184       case PCDM_RS_PermissionDenied:
185         Events_InfoMessage("Model_Document", "Can not open document: permission denied").send();
186         break;
187       case PCDM_RS_DriverFailure:
188         Events_InfoMessage("Model_Document", "Can not open document: driver failure").send();
189         break;
190       default:
191         Events_InfoMessage("Model_Document", "Can not open document: unknown error").send();
192         break;
193     }
194   }
195   std::shared_ptr<Model_Session> aSession =
196     std::dynamic_pointer_cast<Model_Session>(Model_Session::get());
197   if (!isError) {
198     myDoc = aLoaded;
199     myDoc->SetUndoLimit(UNDO_LIMIT);
200
201     // to avoid the problem that feature is created in the current, not this, document
202     aSession->setActiveDocument(anApp->document(myID), false);
203     aSession->setCheckTransactions(false);
204     if (myObjs)
205       delete myObjs;
206     myObjs = new Model_Objects(myDoc->Main()); // synchronisation is inside
207     myObjs->setOwner(theThis);
208     // update the current features status
209     setCurrentFeature(currentFeature(false), false);
210     aSession->setCheckTransactions(true);
211     aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false);
212     // this is done in Part result "activate", so no needed here. Causes not-blue active part.
213     // aSession->setActiveDocument(anApp->getDocument(myID), true);
214
215     // make sub-parts as loaded by demand
216     std::list<ResultPtr> aPartResults;
217     myObjs->allResults(ModelAPI_ResultPart::group(), aPartResults);
218     std::list<ResultPtr>::iterator aPartRes = aPartResults.begin();
219     for(; aPartRes != aPartResults.end(); aPartRes++) {
220       ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aPartRes);
221       if (aPart.get())
222         anApp->setLoadByDemand(aPart->data()->name());
223     }
224
225   } else { // open failed, but new documnet was created to work with it: inform the model
226     aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false);
227   }
228   return !isError;
229 }
230
231 bool Model_Document::save(
232   const char* theDirName, const char* theFileName, std::list<std::string>& theResults)
233 {
234   // create a directory in the root document if it is not yet exist
235   Handle(Model_Application) anApp = Model_Application::getApplication();
236   if (isRoot()) {
237 #ifdef WIN32
238     CreateDirectory(theDirName, NULL);
239 #else
240     mkdir(theDirName, 0x1ff);
241 #endif
242   }
243   // filename in the dir is id of document inside of the given directory
244   TCollection_ExtendedString aPath(DocFileName(theDirName, theFileName));
245   PCDM_StoreStatus aStatus;
246   try {
247     aStatus = anApp->SaveAs(myDoc, aPath);
248   } catch (Standard_Failure) {
249     Handle(Standard_Failure) aFail = Standard_Failure::Caught();
250     Events_InfoMessage("Model_Document",
251         "Exception in saving of document: %1").arg(aFail->GetMessageString()).send();
252     return false;
253   }
254   bool isDone = aStatus == PCDM_SS_OK || aStatus == PCDM_SS_No_Obj;
255   if (!isDone) {
256     switch (aStatus) {
257       case PCDM_SS_DriverFailure:
258         Events_InfoMessage("Model_Document",
259                            "Can not save document: save driver-library failure").send();
260         break;
261       case PCDM_SS_WriteFailure:
262         Events_InfoMessage("Model_Document", "Can not save document: file writing failure").send();
263         break;
264       case PCDM_SS_Failure:
265       default:
266         Events_InfoMessage("Model_Document", "Can not save document").send();
267         break;
268     }
269   }
270   myTransactionSave = int(myTransactions.size());
271   if (isDone) {  // save also sub-documents if any
272     theResults.push_back(TCollection_AsciiString(aPath).ToCString());
273     // iterate all result parts to find all loaded or not yet loaded documents
274     std::list<ResultPtr> aPartResults;
275     myObjs->allResults(ModelAPI_ResultPart::group(), aPartResults);
276     std::list<ResultPtr>::iterator aPartRes = aPartResults.begin();
277     for(; aPartRes != aPartResults.end(); aPartRes++) {
278       ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aPartRes);
279       if (!aPart->isActivated()) {
280         // copy not-activated document that is not in the memory
281         std::string aDocName = aPart->data()->name();
282         if (!aDocName.empty()) {
283           // just copy file
284           TCollection_AsciiString aSubPath(DocFileName(anApp->loadPath().c_str(), aDocName));
285           OSD_Path aPath(aSubPath);
286           OSD_File aFile(aPath);
287           if (aFile.Exists()) {
288             TCollection_AsciiString aDestinationDir(DocFileName(theDirName, aDocName));
289             OSD_Path aDestination(aDestinationDir);
290             aFile.Copy(aDestination);
291             theResults.push_back(aDestinationDir.ToCString());
292           } else {
293             Events_InfoMessage("Model_Document",
294               "Can not open file %1 for saving").arg(aSubPath.ToCString()).send();
295           }
296         }
297       } else { // simply save opened document
298         isDone = std::dynamic_pointer_cast<Model_Document>(aPart->partDoc())->
299           save(theDirName, aPart->data()->name().c_str(), theResults);
300       }
301     }
302   }
303   return isDone;
304 }
305
306 void Model_Document::close(const bool theForever)
307 {
308   std::shared_ptr<ModelAPI_Session> aPM = Model_Session::get();
309   if (!isRoot() && this == aPM->activeDocument().get()) {
310     aPM->setActiveDocument(aPM->moduleDocument());
311   } else if (isRoot()) {
312     // erase the active document if root is closed
313     aPM->setActiveDocument(DocumentPtr());
314   }
315   // close all subs
316   const std::set<int> aSubs = subDocuments();
317   std::set<int>::iterator aSubIter = aSubs.begin();
318   for (; aSubIter != aSubs.end(); aSubIter++) {
319     std::shared_ptr<Model_Document> aSub = subDoc(*aSubIter);
320     if (aSub->myObjs) // if it was not closed before
321       aSub->close(theForever);
322   }
323
324   // close for thid document needs no transaction in this document
325   std::static_pointer_cast<Model_Session>(Model_Session::get())->setCheckTransactions(false);
326
327   // close all only if it is really asked, otherwise it can be undoed/redoed
328   if (theForever) {
329     // flush everything to avoid messages with bad objects
330     delete myObjs;
331     myObjs = 0;
332     if (myDoc->CanClose() == CDM_CCS_OK)
333       myDoc->Close();
334     mySelectionFeature.reset();
335   } else {
336     setCurrentFeature(FeaturePtr(), false); // disables all features
337     // update the OB: features are disabled (on remove of Part)
338     Events_Loop* aLoop = Events_Loop::loop();
339     static Events_ID aDeleteEvent = Events_Loop::eventByName(EVENT_OBJECT_DELETED);
340     aLoop->flush(aDeleteEvent);
341   }
342
343   std::static_pointer_cast<Model_Session>(Model_Session::get())->setCheckTransactions(true);
344 }
345
346 void Model_Document::startOperation()
347 {
348   incrementTransactionID(); // outside of transaction in order to avoid empty transactions keeping
349   if (myDoc->HasOpenCommand()) {  // start of nested command
350     if (myDoc->CommitCommand()) {
351       // commit the current: it will contain all nested after compactification
352       myTransactions.rbegin()->myOCAFNum++; // if has open command, the list is not empty
353     }
354     myNestedNum.push_back(0); // start of nested operation with zero transactions inside yet
355     myDoc->OpenCommand();
356   } else {  // start the simple command
357     myDoc->NewCommand();
358   }
359   // starts a new operation
360   myTransactions.push_back(Transaction());
361   if (!myNestedNum.empty())
362     (*myNestedNum.rbegin())++;
363   myRedos.clear();
364   // new command for all subs
365   const std::set<int> aSubs = subDocuments();
366   std::set<int>::iterator aSubIter = aSubs.begin();
367   for (; aSubIter != aSubs.end(); aSubIter++)
368     subDoc(*aSubIter)->startOperation();
369 }
370
371 void Model_Document::compactNested()
372 {
373   if (!myNestedNum.empty()) {
374     int aNumToCompact = *(myNestedNum.rbegin());
375     int aSumOfTransaction = 0;
376     for(int a = 0; a < aNumToCompact; a++) {
377       aSumOfTransaction += myTransactions.rbegin()->myOCAFNum;
378       myTransactions.pop_back();
379     }
380     // the latest transaction is the start of lower-level operation which startes the nested
381     myTransactions.rbegin()->myOCAFNum += aSumOfTransaction;
382     myNestedNum.pop_back();
383   }
384 }
385
386 /// Compares the content of the given attributes, returns true if equal.
387 /// This method is used to avoid empty transactions when only "current" is changed
388 /// to some value and then comes back in this transaction, so, it compares only
389 /// references and Boolean and Integer Arrays for the current moment.
390 static bool isEqualContent(Handle(TDF_Attribute) theAttr1, Handle(TDF_Attribute) theAttr2)
391 {
392   if (Standard_GUID::IsEqual(theAttr1->ID(), TDF_Reference::GetID())) { // reference
393     Handle(TDF_Reference) aRef1 = Handle(TDF_Reference)::DownCast(theAttr1);
394     Handle(TDF_Reference) aRef2 = Handle(TDF_Reference)::DownCast(theAttr2);
395     if (aRef1.IsNull() && aRef2.IsNull())
396       return true;
397     if (aRef1.IsNull() || aRef2.IsNull())
398       return false;
399     return aRef1->Get().IsEqual(aRef2->Get()) == Standard_True;
400   } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDataStd_BooleanArray::GetID())) {
401     Handle(TDataStd_BooleanArray) anArr1 = Handle(TDataStd_BooleanArray)::DownCast(theAttr1);
402     Handle(TDataStd_BooleanArray) anArr2 = Handle(TDataStd_BooleanArray)::DownCast(theAttr2);
403     if (anArr1.IsNull() && anArr2.IsNull())
404       return true;
405     if (anArr1.IsNull() || anArr2.IsNull())
406       return false;
407     if (anArr1->Lower() == anArr2->Lower() && anArr1->Upper() == anArr2->Upper()) {
408       for(int a = anArr1->Lower(); a <= anArr1->Upper(); a++) {
409         if (a == 1 && // second is for display
410             anArr2->Label().Tag() == 1 && (anArr2->Label().Depth() == 4 ||
411             anArr2->Label().Depth() == 6))
412           continue;
413         if (anArr1->Value(a) != anArr2->Value(a))
414           return false;
415       }
416       return true;
417     }
418   } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDataStd_IntegerArray::GetID())) {
419     Handle(TDataStd_IntegerArray) anArr1 = Handle(TDataStd_IntegerArray)::DownCast(theAttr1);
420     Handle(TDataStd_IntegerArray) anArr2 = Handle(TDataStd_IntegerArray)::DownCast(theAttr2);
421     if (anArr1.IsNull() && anArr2.IsNull())
422       return true;
423     if (anArr1.IsNull() || anArr2.IsNull())
424       return false;
425     if (anArr1->Lower() == anArr2->Lower() && anArr1->Upper() == anArr2->Upper()) {
426       for(int a = anArr1->Lower(); a <= anArr1->Upper(); a++)
427         if (anArr1->Value(a) != anArr2->Value(a)) {
428           // avoid the transaction ID checking
429           if (a == 2 && anArr1->Upper() == 2 && anArr2->Label().Tag() == 1 &&
430             (anArr2->Label().Depth() == 4 || anArr2->Label().Depth() == 6))
431             continue;
432           return false;
433         }
434       return true;
435     }
436   } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDataStd_ReferenceArray::GetID())) {
437     Handle(TDataStd_ReferenceArray) anArr1 = Handle(TDataStd_ReferenceArray)::DownCast(theAttr1);
438     Handle(TDataStd_ReferenceArray) anArr2 = Handle(TDataStd_ReferenceArray)::DownCast(theAttr2);
439     if (anArr1.IsNull() && anArr2.IsNull())
440       return true;
441     if (anArr1.IsNull() || anArr2.IsNull())
442       return false;
443     if (anArr1->Lower() == anArr2->Lower() && anArr1->Upper() == anArr2->Upper()) {
444       for(int a = anArr1->Lower(); a <= anArr1->Upper(); a++)
445         if (anArr1->Value(a) != anArr2->Value(a)) {
446           // avoid the transaction ID checking
447           if (a == 2 && anArr1->Upper() == 2 && anArr2->Label().Tag() == 1 &&
448             (anArr2->Label().Depth() == 4 || anArr2->Label().Depth() == 6))
449             continue;
450           return false;
451         }
452       return true;
453     }
454   } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDataStd_ReferenceList::GetID())) {
455     Handle(TDataStd_ReferenceList) aList1 = Handle(TDataStd_ReferenceList)::DownCast(theAttr1);
456     Handle(TDataStd_ReferenceList) aList2= Handle(TDataStd_ReferenceList)::DownCast(theAttr2);
457     if (aList1.IsNull() && aList2.IsNull())
458       return true;
459     if (aList1.IsNull() || aList2.IsNull())
460       return false;
461     const TDF_LabelList& aLList1 = aList1->List();
462     const TDF_LabelList& aLList2 = aList2->List();
463     TDF_ListIteratorOfLabelList aLIter1(aLList1);
464     TDF_ListIteratorOfLabelList aLIter2(aLList2);
465     for(; aLIter1.More() && aLIter2.More(); aLIter1.Next(), aLIter2.Next()) {
466       if (aLIter1.Value() != aLIter2.Value())
467         return false;
468     }
469     return !aLIter1.More() && !aLIter2.More(); // both lists are with the same size
470   } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDF_TagSource::GetID())) {
471     return true; // it just for created and removed feature: nothing is changed
472   }
473   return false;
474 }
475
476 /// Returns true if the last transaction is actually empty: modification to te same values
477 /// were performed only
478 static bool isEmptyTransaction(const Handle(TDocStd_Document)& theDoc) {
479   Handle(TDF_Delta) aDelta;
480   aDelta = theDoc->GetUndos().Last();
481   TDF_LabelList aDeltaList;
482   aDelta->Labels(aDeltaList); // it clears list, so, use new one and then append to the result
483   for(TDF_ListIteratorOfLabelList aListIter(aDeltaList); aListIter.More(); aListIter.Next()) {
484     return false;
485   }
486   // add also label of the modified attributes
487   const TDF_AttributeDeltaList& anAttrs = aDelta->AttributeDeltas();
488   for (TDF_ListIteratorOfAttributeDeltaList anAttr(anAttrs); anAttr.More(); anAttr.Next()) {
489     Handle(TDF_AttributeDelta)& anADelta = anAttr.Value();
490     Handle(TDF_DeltaOnAddition) anAddition = Handle(TDF_DeltaOnAddition)::DownCast(anADelta);
491     if (anAddition.IsNull()) { // if the attribute was added, transaction is not empty
492       if (!anADelta->Label().IsNull() && !anADelta->Attribute().IsNull()) {
493         Handle(TDF_Attribute) aCurrentAttr;
494         if (anADelta->Label().FindAttribute(anADelta->Attribute()->ID(), aCurrentAttr)) {
495           if (isEqualContent(anADelta->Attribute(), aCurrentAttr)) {
496             continue; // attribute is not changed actually
497           }
498         } else
499           if (Standard_GUID::IsEqual(anADelta->Attribute()->ID(), TDataStd_AsciiString::GetID())) {
500             continue; // error message is disappeared
501         }
502       }
503     }
504     return false;
505   }
506   return true;
507 }
508
509 bool Model_Document::finishOperation()
510 {
511   bool isNestedClosed = !myDoc->HasOpenCommand() && !myNestedNum.empty();
512   static std::shared_ptr<Model_Session> aSession =
513     std::static_pointer_cast<Model_Session>(Model_Session::get());
514
515   // open transaction if nested is closed to fit inside
516   // all synchronizeBackRefs and flushed consequences
517   if (isNestedClosed) {
518     myDoc->OpenCommand();
519   }
520   // do it before flashes to enable and recompute nesting features correctly
521   if (myNestedNum.empty() || (isNestedClosed && myNestedNum.size() == 1)) {
522     // if all nested operations are closed, make current the higher level objects (to perform
523     // it in the python scripts correctly): sketch become current after creation ofsub-elements
524     FeaturePtr aCurrent = currentFeature(false);
525     CompositeFeaturePtr aMain, aNext = ModelAPI_Tools::compositeOwner(aCurrent);
526     while(aNext.get()) {
527       aMain = aNext;
528       aNext = ModelAPI_Tools::compositeOwner(aMain);
529     }
530     if (aMain.get() && aMain != aCurrent)
531       setCurrentFeature(aMain, false);
532   }
533   myObjs->synchronizeBackRefs();
534   Events_Loop* aLoop = Events_Loop::loop();
535   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
536   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
537   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
538   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
539
540   if (isNestedClosed) {
541     if (myDoc->CommitCommand())
542       myTransactions.rbegin()->myOCAFNum++;
543   }
544
545   // this must be here just after everything is finished but before real transaction stop
546   // to avoid messages about modifications outside of the transaction
547   // and to rebuild everything after all updates and creates
548   if (isRoot()) { // once for root document
549     Events_Loop::loop()->autoFlush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
550     static std::shared_ptr<Events_Message> aFinishMsg
551       (new Events_Message(Events_Loop::eventByName("FinishOperation")));
552     Events_Loop::loop()->send(aFinishMsg);
553     Events_Loop::loop()->autoFlush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED), false);
554   }
555   // to avoid "updated" message appearance by updater
556   //aLoop->clear(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
557
558   // finish for all subs first: to avoid nested finishing and "isOperation" calls problems inside
559   bool aResult = false;
560   const std::set<int> aSubs = subDocuments();
561   std::set<int>::iterator aSubIter = aSubs.begin();
562   for (; aSubIter != aSubs.end(); aSubIter++)
563     if (subDoc(*aSubIter)->finishOperation())
564       aResult = true;
565
566   // transaction may be empty if this document was created during this transaction (create part)
567   if (!myTransactions.empty() && myDoc->CommitCommand()) {
568     // if commit is successfull, just increment counters
569     if (isEmptyTransaction(myDoc)) { // erase this transaction
570       myDoc->Undo();
571       myDoc->ClearRedos();
572     } else {
573       myTransactions.rbegin()->myOCAFNum++;
574       aResult = true;
575     }
576   }
577
578   if (isNestedClosed) {
579     compactNested();
580   }
581   if (!aResult && !myTransactions.empty() /* it can be for just created part document */)
582     aResult = myTransactions.rbegin()->myOCAFNum != 0;
583
584   if (!aResult && isRoot()) {
585     // nothing inside in all documents, so remove this transaction from the transactions list
586     undoInternal(true, false);
587   }
588   // on finish clear redos in any case (issue 446) and for all subs (issue 408)
589   myDoc->ClearRedos();
590   myRedos.clear();
591   for (aSubIter = aSubs.begin(); aSubIter != aSubs.end(); aSubIter++) {
592     subDoc(*aSubIter)->myDoc->ClearRedos();
593     subDoc(*aSubIter)->myRedos.clear();
594   }
595
596   return aResult;
597 }
598
599 /// Returns in theDelta labels that has been modified in the latest transaction of theDoc
600 static void modifiedLabels(const Handle(TDocStd_Document)& theDoc, TDF_LabelList& theDelta,
601   const bool isRedo = false) {
602   Handle(TDF_Delta) aDelta;
603   if (isRedo)
604     aDelta = theDoc->GetRedos().First();
605   else
606     aDelta = theDoc->GetUndos().Last();
607   TDF_LabelList aDeltaList;
608   aDelta->Labels(aDeltaList); // it clears list, so, use new one and then append to the result
609   for(TDF_ListIteratorOfLabelList aListIter(aDeltaList); aListIter.More(); aListIter.Next()) {
610     theDelta.Append(aListIter.Value());
611   }
612   // add also label of the modified attributes
613   const TDF_AttributeDeltaList& anAttrs = aDelta->AttributeDeltas();
614   /// named shape evolution also modifies integer on this label: exclude it
615   TDF_LabelMap anExcludedInt;
616   for (TDF_ListIteratorOfAttributeDeltaList anAttr(anAttrs); anAttr.More(); anAttr.Next()) {
617     if (anAttr.Value()->Attribute()->ID() == TDataStd_BooleanArray::GetID()) {
618       // Boolean array is used for feature auxiliary attributes only, feature args are not modified
619       continue;
620     }
621     if (anAttr.Value()->Attribute()->ID() == TNaming_NamedShape::GetID()) {
622       anExcludedInt.Add(anAttr.Value()->Label());
623       // named shape evolution is changed in history update => skip them,
624       // they are not the features arguents
625       continue;
626     }
627     if (anAttr.Value()->Attribute()->ID() == TDataStd_Integer::GetID()) {
628       if (anExcludedInt.Contains(anAttr.Value()->Label()))
629         continue;
630     }
631       theDelta.Append(anAttr.Value()->Label());
632   }
633   TDF_ListIteratorOfLabelList aDeltaIter(theDelta);
634   for(; aDeltaIter.More(); aDeltaIter.Next()) {
635     if (anExcludedInt.Contains(aDeltaIter.Value())) {
636       theDelta.Remove(aDeltaIter);
637       if (!aDeltaIter.More())
638         break;
639     }
640   }
641 }
642
643 void Model_Document::abortOperation()
644 {
645   TDF_LabelList aDeltaLabels; // labels that are updated during "abort"
646   if (!myNestedNum.empty() && !myDoc->HasOpenCommand()) {  // abort all what was done in nested
647     compactNested();
648     // store undo-delta here as undo actually does in the method later
649     int a, aNumTransactions = myTransactions.rbegin()->myOCAFNum;
650     for(a = 0; a < aNumTransactions; a++) {
651       modifiedLabels(myDoc, aDeltaLabels);
652       myDoc->Undo();
653     }
654     for(a = 0; a < aNumTransactions; a++) {
655       myDoc->Redo();
656     }
657
658     undoInternal(false, false);
659     myDoc->ClearRedos();
660     myRedos.clear();
661   } else { // abort the current
662     int aNumTransactions = myTransactions.rbegin()->myOCAFNum;
663     myTransactions.pop_back();
664     if (!myNestedNum.empty())
665       (*myNestedNum.rbegin())--;
666     // roll back the needed number of transactions
667     //myDoc->AbortCommand();
668     // instead of abort, do commit and undo: to get the delta of modifications
669     if (myDoc->CommitCommand())  {
670       modifiedLabels(myDoc, aDeltaLabels);
671       myDoc->Undo();
672     }
673     for(int a = 0; a < aNumTransactions; a++) {
674       modifiedLabels(myDoc, aDeltaLabels);
675       myDoc->Undo();
676     }
677     myDoc->ClearRedos();
678   }
679   // abort for all subs, flushes will be later, in the end of root abort
680   const std::set<int> aSubs = subDocuments();
681   std::set<int>::iterator aSubIter = aSubs.begin();
682   for (; aSubIter != aSubs.end(); aSubIter++)
683     subDoc(*aSubIter)->abortOperation();
684   // references may be changed because they are set in attributes on the fly
685   myObjs->synchronizeFeatures(aDeltaLabels, true, false, false, isRoot());
686 }
687
688 bool Model_Document::isOperation() const
689 {
690   // operation is opened for all documents: no need to check subs
691   return myDoc->HasOpenCommand() == Standard_True ;
692 }
693
694 bool Model_Document::isModified()
695 {
696   // is modified if at least one operation was commited and not undoed
697   return myTransactions.size() != myTransactionSave || isOperation();
698 }
699
700 bool Model_Document::canUndo()
701 {
702   // issue 406 : if transaction is opened, but nothing to undo behind, can not undo
703   int aCurrentNum = isOperation() ? 1 : 0;
704   if (myDoc->GetAvailableUndos() > 0 &&
705       // there is something to undo in nested
706       (myNestedNum.empty() || *myNestedNum.rbegin() - aCurrentNum > 0) &&
707       myTransactions.size() - aCurrentNum > 0 /* for omitting the first useless transaction */)
708     return true;
709   // check other subs contains operation that can be undoed
710   const std::set<int> aSubs = subDocuments();
711   std::set<int>::iterator aSubIter = aSubs.begin();
712   for (; aSubIter != aSubs.end(); aSubIter++) {
713     std::shared_ptr<Model_Document> aSub = subDoc(*aSubIter);
714     if (aSub->myObjs) {// if it was not closed before
715       if (aSub->canUndo())
716         return true;
717     }
718   }
719
720   return false;
721 }
722
723 void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchronize)
724 {
725   if (myTransactions.empty())
726     return;
727   int aNumTransactions = myTransactions.rbegin()->myOCAFNum;
728   myRedos.push_back(*myTransactions.rbegin());
729   myTransactions.pop_back();
730   if (!myNestedNum.empty())
731     (*myNestedNum.rbegin())--;
732   // roll back the needed number of transactions
733   TDF_LabelList aDeltaLabels;
734   for(int a = 0; a < aNumTransactions; a++) {
735     if (theSynchronize)
736       modifiedLabels(myDoc, aDeltaLabels);
737     myDoc->Undo();
738   }
739
740   if (theWithSubs) {
741     // undo for all subs
742     const std::set<int> aSubs = subDocuments();
743     std::set<int>::iterator aSubIter = aSubs.begin();
744     for (; aSubIter != aSubs.end(); aSubIter++) {
745       if (!subDoc(*aSubIter)->myObjs)
746         continue;
747       subDoc(*aSubIter)->undoInternal(theWithSubs, theSynchronize);
748     }
749   }
750   // after undo of all sub-documents to avoid updates on not-modified data (issue 370)
751   if (theSynchronize) {
752     myObjs->synchronizeFeatures(aDeltaLabels, true, false, false, isRoot());
753     // update the current features status
754     setCurrentFeature(currentFeature(false), false);
755   }
756 }
757
758 void Model_Document::undo()
759 {
760   undoInternal(true, true);
761 }
762
763 bool Model_Document::canRedo()
764 {
765   if (!myRedos.empty())
766     return true;
767   // check other subs contains operation that can be redoed
768   const std::set<int> aSubs = subDocuments();
769   std::set<int>::iterator aSubIter = aSubs.begin();
770   for (; aSubIter != aSubs.end(); aSubIter++) {
771     if (!subDoc(*aSubIter)->myObjs)
772       continue;
773     if (subDoc(*aSubIter)->canRedo())
774       return true;
775   }
776   return false;
777 }
778
779 void Model_Document::redo()
780 {
781   if (!myNestedNum.empty())
782     (*myNestedNum.rbegin())++;
783   int aNumRedos = myRedos.rbegin()->myOCAFNum;
784   myTransactions.push_back(*myRedos.rbegin());
785   myRedos.pop_back();
786   TDF_LabelList aDeltaLabels;
787   for(int a = 0; a < aNumRedos; a++) {
788     modifiedLabels(myDoc, aDeltaLabels, true);
789     myDoc->Redo();
790   }
791
792   // redo for all subs
793   const std::set<int> aSubs = subDocuments();
794   std::set<int>::iterator aSubIter = aSubs.begin();
795   for (; aSubIter != aSubs.end(); aSubIter++)
796     subDoc(*aSubIter)->redo();
797
798   // after redo of all sub-documents to avoid updates on not-modified data (issue 370)
799   myObjs->synchronizeFeatures(aDeltaLabels, true, false, false, isRoot());
800   // update the current features status
801   setCurrentFeature(currentFeature(false), false);
802 }
803
804 std::list<std::string> Model_Document::undoList() const
805 {
806   std::list<std::string> aResult;
807   // the number of skipped current operations (on undo they will be aborted)
808   int aSkipCurrent = isOperation() ? 1 : 0;
809   std::list<Transaction>::const_reverse_iterator aTrIter = myTransactions.crbegin();
810   int aNumUndo = int(myTransactions.size());
811   if (!myNestedNum.empty())
812     aNumUndo = *myNestedNum.rbegin();
813   for( ; aNumUndo > 0; aTrIter++, aNumUndo--) {
814     if (aSkipCurrent == 0) aResult.push_back(aTrIter->myId);
815     else aSkipCurrent--;
816   }
817   return aResult;
818 }
819
820 std::list<std::string> Model_Document::redoList() const
821 {
822   std::list<std::string> aResult;
823   std::list<Transaction>::const_reverse_iterator aTrIter = myRedos.crbegin();
824   for( ; aTrIter != myRedos.crend(); aTrIter++) {
825     aResult.push_back(aTrIter->myId);
826   }
827   return aResult;
828 }
829
830 void Model_Document::operationId(const std::string& theId)
831 {
832   myTransactions.rbegin()->myId = theId;
833 }
834
835 FeaturePtr Model_Document::addFeature(std::string theID, const bool theMakeCurrent)
836 {
837   std::shared_ptr<Model_Session> aSession =
838     std::dynamic_pointer_cast<Model_Session>(ModelAPI_Session::get());
839   FeaturePtr aFeature = aSession->createFeature(theID, this);
840   if (!aFeature)
841     return aFeature;
842   aFeature->init();
843   Model_Document* aDocToAdd;
844   if (!aFeature->documentToAdd().empty()) { // use the customized document to add
845     if (aFeature->documentToAdd() != kind()) { // the root document by default
846       aDocToAdd = std::dynamic_pointer_cast<Model_Document>(aSession->moduleDocument()).get();
847     } else {
848       aDocToAdd = this;
849     }
850   } else { // if customized is not presented, add to "this" document
851     aDocToAdd = this;
852   }
853   if (aFeature) {
854     // searching for feature after which must be added the next feature: this is the current feature
855     // but also all sub-features of this feature
856     FeaturePtr aCurrent = aDocToAdd->currentFeature(false);
857     bool isModified = true;
858     for(CompositeFeaturePtr aComp = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aCurrent);
859         aComp.get() && isModified;
860         aComp = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aCurrent)) {
861       isModified =  false;
862       int aSubs = aComp->numberOfSubs(false);
863       for(int a = 0; a < aSubs; a++) {
864         FeaturePtr aSub = aComp->subFeature(a, false);
865         if (myObjs->isLater(aSub, aCurrent)) {
866           isModified =  true;
867           aCurrent = aSub;
868         }
869       }
870     }
871     aDocToAdd->myObjs->addFeature(aFeature, aCurrent);
872     if (!aFeature->isAction()) {  // do not add action to the data model
873       if (theMakeCurrent)  // after all this feature stays in the document, so make it current
874         aDocToAdd->setCurrentFeature(aFeature, false);
875     } else { // feature must be executed
876        // no creation event => updater not working, problem with remove part
877       aFeature->execute();
878     }
879   }
880   return aFeature;
881 }
882
883
884 void Model_Document::refsToFeature(FeaturePtr theFeature,
885   std::set<std::shared_ptr<ModelAPI_Feature> >& theRefs, const bool isSendError)
886 {
887   myObjs->refsToFeature(theFeature, theRefs, isSendError);
888 }
889
890 void Model_Document::removeFeature(FeaturePtr theFeature)
891 {
892   myObjs->removeFeature(theFeature);
893 }
894
895 // recursive function to check if theSub is a child of theMain composite feature
896 // through all the hierarchy of parents
897 static bool isSub(const CompositeFeaturePtr theMain, const FeaturePtr theSub) {
898   CompositeFeaturePtr aParent = ModelAPI_Tools::compositeOwner(theSub);
899   if (!aParent.get())
900     return false;
901   if (aParent == theMain)
902     return true;
903   return isSub(theMain, aParent);
904 }
905
906
907 void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis)
908 {
909   bool aCurrentUp = theMoved == currentFeature(false);
910   if (aCurrentUp) {
911     setCurrentFeatureUp();
912   }
913   // if user adds after high-level feature with nested,
914   // add it after all nested (otherwise the nested will be disabled)
915   CompositeFeaturePtr aCompositeAfter =
916     std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theAfterThis);
917   if (aCompositeAfter.get()) {
918     FeaturePtr aSub = aCompositeAfter;
919     do {
920       FeaturePtr aNext = myObjs->nextFeature(aSub);
921       if (!isSub(aCompositeAfter, aNext)) {
922         theAfterThis = aSub;
923         break;
924       }
925       aSub = aNext;
926     } while (aSub.get());
927   }
928
929   myObjs->moveFeature(theMoved, theAfterThis);
930   if (aCurrentUp) { // make the moved feature enabled or disabled due to the real status
931     setCurrentFeature(currentFeature(false), false);
932   } else if (theAfterThis == currentFeature(false)) {
933     // must be after move to make enabled all features which are before theMoved
934     setCurrentFeature(theMoved, true);
935   }
936 }
937
938 void Model_Document::updateHistory(const std::shared_ptr<ModelAPI_Object> theObject)
939 {
940   myObjs->updateHistory(theObject);
941 }
942
943 void Model_Document::updateHistory(const std::string theGroup)
944 {
945   myObjs->updateHistory(theGroup);
946 }
947
948 const std::set<int> Model_Document::subDocuments() const
949 {
950   std::set<int> aResult;
951   std::list<ResultPtr> aPartResults;
952   myObjs->allResults(ModelAPI_ResultPart::group(), aPartResults);
953   std::list<ResultPtr>::iterator aPartRes = aPartResults.begin();
954   for(; aPartRes != aPartResults.end(); aPartRes++) {
955     ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aPartRes);
956     if (aPart && aPart->isActivated()) {
957       aResult.insert(aPart->original()->partDoc()->id());
958     }
959   }
960   return aResult;
961 }
962
963 std::shared_ptr<Model_Document> Model_Document::subDoc(int theDocID)
964 {
965   // just store sub-document identifier here to manage it later
966   return std::dynamic_pointer_cast<Model_Document>(
967     Model_Application::getApplication()->document(theDocID));
968 }
969
970 ObjectPtr Model_Document::object(const std::string& theGroupID, const int theIndex)
971 {
972   return myObjs->object(theGroupID, theIndex);
973 }
974
975 std::shared_ptr<ModelAPI_Object> Model_Document::objectByName(
976     const std::string& theGroupID, const std::string& theName)
977 {
978   return myObjs->objectByName(theGroupID, theName);
979 }
980
981 const int Model_Document::index(std::shared_ptr<ModelAPI_Object> theObject)
982 {
983   return myObjs->index(theObject);
984 }
985
986 int Model_Document::size(const std::string& theGroupID)
987 {
988   if (myObjs == 0) // may be on close
989     return 0;
990   return myObjs->size(theGroupID);
991 }
992
993 std::shared_ptr<ModelAPI_Feature> Model_Document::currentFeature(const bool theVisible)
994 {
995   if (!myObjs) // on close document feature destruction it may call this method
996     return std::shared_ptr<ModelAPI_Feature>();
997   TDF_Label aRefLab = generalLabel().FindChild(TAG_CURRENT_FEATURE);
998   Handle(TDF_Reference) aRef;
999   if (aRefLab.FindAttribute(TDF_Reference::GetID(), aRef)) {
1000     TDF_Label aLab = aRef->Get();
1001     FeaturePtr aResult = myObjs->feature(aLab);
1002     if (theVisible) { // get nearest visible (in history) going up
1003       while(aResult.get() &&  !aResult->isInHistory()) {
1004         aResult = myObjs->nextFeature(aResult, true);
1005       }
1006     }
1007     return aResult;
1008   }
1009   return std::shared_ptr<ModelAPI_Feature>(); // null feature means the higher than first
1010 }
1011
1012 void Model_Document::setCurrentFeature(
1013   std::shared_ptr<ModelAPI_Feature> theCurrent, const bool theVisible)
1014 {
1015   // blocks the flush signals to avoid each objects visualization in the viewer
1016   // they should not be shown once after all modifications are performed
1017   Events_Loop* aLoop = Events_Loop::loop();
1018   bool isActive = aLoop->activateFlushes(false);
1019
1020   TDF_Label aRefLab = generalLabel().FindChild(TAG_CURRENT_FEATURE);
1021   CompositeFeaturePtr aMain; // main feature that may nest the new current
1022   std::set<FeaturePtr> anOwners; // composites that contain theCurrent (with any level of nesting)
1023   if (theCurrent.get()) {
1024     aMain = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theCurrent);
1025     CompositeFeaturePtr anOwner = ModelAPI_Tools::compositeOwner(theCurrent);
1026     while(anOwner.get()) {
1027       if (!aMain.get()) {
1028         aMain = anOwner;
1029       }
1030       anOwners.insert(anOwner);
1031       anOwner = ModelAPI_Tools::compositeOwner(anOwner);
1032     }
1033   }
1034
1035   if (theVisible && !theCurrent.get()) {
1036     // needed to avoid disabling of PartSet initial constructions
1037     FeaturePtr aNext =
1038       theCurrent.get() ? myObjs->nextFeature(theCurrent) : myObjs->firstFeature();
1039     for (; aNext.get(); aNext = myObjs->nextFeature(theCurrent)) {
1040       if (aNext->isInHistory()) {
1041         break; // next in history is not needed
1042       } else { // next not in history is good for making current
1043         theCurrent = aNext;
1044       }
1045     }
1046   }
1047   if (theCurrent.get()) {
1048     std::shared_ptr<Model_Data> aData = std::static_pointer_cast<Model_Data>(theCurrent->data());
1049     if (!aData.get() || !aData->isValid()) {
1050       aLoop->activateFlushes(isActive);
1051       return;
1052     }
1053     TDF_Label aFeatureLabel = aData->label().Father();
1054
1055     Handle(TDF_Reference) aRef;
1056     if (aRefLab.FindAttribute(TDF_Reference::GetID(), aRef)) {
1057       aRef->Set(aFeatureLabel);
1058     } else {
1059       aRef = TDF_Reference::Set(aRefLab, aFeatureLabel);
1060     }
1061   } else { // remove reference for the null feature
1062     aRefLab.ForgetAttribute(TDF_Reference::GetID());
1063   }
1064   // make all features after this feature disabled in reversed order
1065   // (to remove results without deps)
1066   static Events_ID aRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
1067
1068   bool aPassed = false; // flag that the current object is already passed in cycle
1069   FeaturePtr anIter = myObjs->lastFeature();
1070   bool aWasChanged = false;
1071   bool isCurrentParameter = theCurrent.get() && theCurrent->getKind() == "Parameter";
1072   for(; anIter.get(); anIter = myObjs->nextFeature(anIter, true)) {
1073     // check this before passed become enabled: the current feature is enabled!
1074     if (anIter == theCurrent) aPassed = true;
1075
1076     bool aDisabledFlag = !aPassed;
1077     if (aMain.get()) {
1078       if (isSub(aMain, anIter)) // sub-elements of not-disabled feature are not disabled
1079         aDisabledFlag = false;
1080       else if (anOwners.find(anIter) != anOwners.end())
1081         // disable the higher-level feature is the nested is the current
1082         aDisabledFlag = true;
1083     }
1084
1085     if (anIter->getKind() == "Parameter") {
1086       // parameters are always out of the history of features, but not parameters
1087       // due to the issue 1491 all parameters are kept enabled any time
1088       //if (!isCurrentParameter)
1089         aDisabledFlag = false;
1090     } else if (isCurrentParameter) {
1091       // if paramater is active, all other features become enabled (issue 1307)
1092       aDisabledFlag = false;
1093     }
1094
1095     if (anIter->setDisabled(aDisabledFlag)) {
1096       static Events_ID anUpdateEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED);
1097       // state of feature is changed => so inform that it must be updated if it has such state
1098       if (!aDisabledFlag &&
1099           (anIter->data()->execState() == ModelAPI_StateMustBeUpdated ||
1100            anIter->data()->execState() == ModelAPI_StateInvalidArgument))
1101         ModelAPI_EventCreator::get()->sendUpdated(anIter, anUpdateEvent);
1102       // flush is in the end of this method
1103       ModelAPI_EventCreator::get()->sendUpdated(anIter, aRedispEvent /*, false*/);
1104       aWasChanged = true;
1105     }
1106     // update for everyone the concealment flag immideately: on edit feature in the midle of history
1107     if (aWasChanged) {
1108       std::list<ResultPtr> aResults;
1109       ModelAPI_Tools::allResults(anIter, aResults);
1110       std::list<ResultPtr>::const_iterator aRes = aResults.begin();
1111       for(; aRes != aResults.end(); aRes++) {
1112         if ((*aRes).get() && (*aRes)->data()->isValid() && !(*aRes)->isDisabled())
1113           std::dynamic_pointer_cast<Model_Data>((*aRes)->data())->updateConcealmentFlag();
1114       }
1115       // update the concealment status for disply in isConcealed of ResultBody
1116       for(aRes = aResults.begin(); aRes != aResults.end(); aRes++) {
1117         if ((*aRes).get() && (*aRes)->data()->isValid() && !(*aRes)->isDisabled())
1118           (*aRes)->isConcealed();
1119       }
1120     }
1121   }
1122   // unblock  the flush signals and up them after this
1123   aLoop->activateFlushes(isActive);
1124 }
1125
1126 void Model_Document::setCurrentFeatureUp()
1127 {
1128   // on remove just go up for minimum step: highlight external objects in sketch causes
1129   // problems if it is true: here and in "setCurrentFeature"
1130   FeaturePtr aCurrent = currentFeature(false);
1131   if (aCurrent.get()) { // if not, do nothing because null is the upper
1132     FeaturePtr aPrev = myObjs->nextFeature(aCurrent, true);
1133     // make the higher level composite as current (sketch becomes disabled if line is enabled)
1134     if (aPrev.get()) {
1135       FeaturePtr aComp = ModelAPI_Tools::compositeOwner(aPrev);
1136       // without cycle (issue 1555): otherwise extrusion fuse
1137       // will be enabled and displayed whaen inside sketch
1138       if (aComp.get())
1139           aPrev = aComp;
1140     }
1141     // do not flush: it is called only on remove, it will be flushed in the end of transaction
1142     setCurrentFeature(aPrev, false);
1143   }
1144 }
1145
1146 TDF_Label Model_Document::generalLabel() const
1147 {
1148   return myDoc->Main().FindChild(TAG_GENERAL);
1149 }
1150
1151 std::shared_ptr<ModelAPI_ResultConstruction> Model_Document::createConstruction(
1152     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1153 {
1154   return myObjs->createConstruction(theFeatureData, theIndex);
1155 }
1156
1157 std::shared_ptr<ModelAPI_ResultBody> Model_Document::createBody(
1158     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1159 {
1160   return myObjs->createBody(theFeatureData, theIndex);
1161 }
1162
1163 std::shared_ptr<ModelAPI_ResultPart> Model_Document::createPart(
1164     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1165 {
1166   return myObjs->createPart(theFeatureData, theIndex);
1167 }
1168
1169 std::shared_ptr<ModelAPI_ResultPart> Model_Document::copyPart(
1170       const std::shared_ptr<ModelAPI_ResultPart>& theOrigin,
1171       const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1172 {
1173   return myObjs->copyPart(theOrigin, theFeatureData, theIndex);
1174 }
1175
1176 std::shared_ptr<ModelAPI_ResultGroup> Model_Document::createGroup(
1177     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1178 {
1179   return myObjs->createGroup(theFeatureData, theIndex);
1180 }
1181
1182 std::shared_ptr<ModelAPI_ResultField> Model_Document::createField(
1183     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1184 {
1185   return myObjs->createField(theFeatureData, theIndex);
1186 }
1187
1188 std::shared_ptr<ModelAPI_ResultParameter> Model_Document::createParameter(
1189       const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1190 {
1191   return myObjs->createParameter(theFeatureData, theIndex);
1192 }
1193
1194 std::shared_ptr<ModelAPI_Feature> Model_Document::feature(
1195     const std::shared_ptr<ModelAPI_Result>& theResult)
1196 {
1197   return myObjs->feature(theResult);
1198 }
1199
1200 Standard_Integer HashCode(const TDF_Label& theLab, const Standard_Integer theUpper)
1201 {
1202   return TDF_LabelMapHasher::HashCode(theLab, theUpper);
1203
1204 }
1205 Standard_Boolean IsEqual(const TDF_Label& theLab1, const TDF_Label& theLab2)
1206 {
1207   return TDF_LabelMapHasher::IsEqual(theLab1, theLab2);
1208 }
1209
1210 void Model_Document::addNamingName(const TDF_Label theLabel, std::string theName)
1211 {
1212   myNamingNames[theName] = theLabel;
1213 }
1214
1215 void Model_Document::changeNamingName(const std::string theOldName, const std::string theNewName)
1216 {
1217   std::map<std::string, TDF_Label>::iterator aFind = myNamingNames.find(theOldName);
1218   if (aFind != myNamingNames.end()) {
1219     myNamingNames[theNewName] = aFind->second;
1220     myNamingNames.erase(theOldName);
1221   }
1222 }
1223
1224 TDF_Label Model_Document::findNamingName(std::string theName)
1225 {
1226   std::map<std::string, TDF_Label>::iterator aFind = myNamingNames.find(theName);
1227   if (aFind != myNamingNames.end()) {
1228     return aFind->second;
1229   }
1230   // not found exact name, try to find by sub-components
1231   std::string::size_type aSlash = theName.rfind('/');
1232   if (aSlash != std::string::npos) {
1233     std::string anObjName = theName.substr(0, aSlash);
1234     aFind = myNamingNames.find(anObjName);
1235     if (aFind != myNamingNames.end()) {
1236       TCollection_ExtendedString aSubName(theName.substr(aSlash + 1).c_str());
1237       // searching sub-labels with this name
1238       TDF_ChildIDIterator aNamesIter(aFind->second, TDataStd_Name::GetID(), Standard_True);
1239       for(; aNamesIter.More(); aNamesIter.Next()) {
1240         Handle(TDataStd_Name) aName = Handle(TDataStd_Name)::DownCast(aNamesIter.Value());
1241         if (aName->Get() == aSubName)
1242           return aName->Label();
1243       }
1244       // If not found child label with the exact sub-name, then try to find compound with
1245       // such sub-name without suffix.
1246       Standard_Integer aSuffixPos = aSubName.SearchFromEnd('_');
1247       if (aSuffixPos != -1) {
1248         TCollection_ExtendedString anIndexStr = aSubName.Split(aSuffixPos);
1249         aSubName.Remove(aSuffixPos);
1250         aNamesIter.Initialize(aFind->second, TDataStd_Name::GetID(), Standard_True);
1251         for(; aNamesIter.More(); aNamesIter.Next()) {
1252           Handle(TDataStd_Name) aName = Handle(TDataStd_Name)::DownCast(aNamesIter.Value());
1253           if (aName->Get() == aSubName) {
1254             return aName->Label();
1255           }
1256         }
1257       }
1258     }
1259   }
1260   return TDF_Label(); // not found
1261 }
1262
1263 ResultPtr Model_Document::findByName(const std::string theName)
1264 {
1265   return myObjs->findByName(theName);
1266 }
1267
1268 std::list<std::shared_ptr<ModelAPI_Feature> > Model_Document::allFeatures()
1269 {
1270   return myObjs->allFeatures();
1271 }
1272
1273 void Model_Document::setActive(const bool theFlag)
1274 {
1275   if (theFlag != myIsActive) {
1276     myIsActive = theFlag;
1277     // redisplay all the objects of this part
1278     static Events_Loop* aLoop = Events_Loop::loop();
1279     static Events_ID aRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
1280
1281     for(int a = size(ModelAPI_Feature::group()) - 1; a >= 0; a--) {
1282       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(
1283         object(ModelAPI_Feature::group(), a));
1284       if (aFeature.get() && aFeature->data()->isValid()) {
1285         const std::list<std::shared_ptr<ModelAPI_Result> >& aResList = aFeature->results();
1286         std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRes = aResList.begin();
1287         for(; aRes != aResList.end(); aRes++) {
1288           ModelAPI_EventCreator::get()->sendUpdated(*aRes, aRedispEvent);
1289           // #issue 1048: sub-compsolids also
1290           ResultCompSolidPtr aCompRes = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(*aRes);
1291           if (aCompRes.get()) {
1292             int aNumSubs = aCompRes->numberOfSubs();
1293             for(int a = 0; a < aNumSubs; a++) {
1294               ResultPtr aSub = aCompRes->subResult(a);
1295               if (aSub.get()) {
1296                 ModelAPI_EventCreator::get()->sendUpdated(aSub, aRedispEvent);
1297               }
1298             }
1299           }
1300         }
1301       }
1302     }
1303   }
1304 }
1305
1306 bool Model_Document::isActive() const
1307 {
1308   return myIsActive;
1309 }
1310
1311 int Model_Document::transactionID()
1312 {
1313   Handle(TDataStd_Integer) anIndex;
1314   if (!generalLabel().FindChild(TAG_CURRENT_TRANSACTION).
1315       FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
1316     anIndex = TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), 1);
1317   }
1318   return anIndex->Get();
1319 }
1320
1321 void Model_Document::incrementTransactionID()
1322 {
1323   int aNewVal = transactionID() + 1;
1324   TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), aNewVal);
1325 }
1326 void Model_Document::decrementTransactionID()
1327 {
1328   int aNewVal = transactionID() - 1;
1329   TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), aNewVal);
1330 }
1331
1332 bool Model_Document::isOpened()
1333 {
1334   return myObjs && !myDoc.IsNull();
1335 }
1336
1337 int Model_Document::numInternalFeatures()
1338 {
1339   return myObjs->numInternalFeatures();
1340 }
1341
1342 std::shared_ptr<ModelAPI_Feature> Model_Document::internalFeature(const int theIndex)
1343 {
1344   return myObjs->internalFeature(theIndex);
1345 }
1346
1347 std::shared_ptr<ModelAPI_Feature> Model_Document::featureById(const int theId)
1348 {
1349   return myObjs->featureById(theId);
1350 }
1351
1352 void Model_Document::synchronizeTransactions()
1353 {
1354   Model_Document* aRoot =
1355     std::dynamic_pointer_cast<Model_Document>(ModelAPI_Session::get()->moduleDocument()).get();
1356   if (aRoot == this)
1357     return; // don't need to synchronise root with root
1358
1359   std::shared_ptr<Model_Session> aSession =
1360     std::dynamic_pointer_cast<Model_Session>(Model_Session::get());
1361   while(myRedos.size() > aRoot->myRedos.size()) { // remove redos in this
1362     aSession->setCheckTransactions(false);
1363     redo();
1364     aSession->setCheckTransactions(true);
1365   }
1366   /* this case can not be reproduced in any known case for the current moment, so, just comment
1367   while(myRedos.size() < aRoot->myRedos.size()) { // add more redos in this
1368     undoInternal(false, true);
1369   }*/
1370 }
1371
1372 /// Feature that is used for selection in the Part document by the external request
1373 class Model_SelectionInPartFeature : public ModelAPI_Feature {
1374 public:
1375   /// Nothing to do in constructor
1376   Model_SelectionInPartFeature() : ModelAPI_Feature() {}
1377
1378   /// Returns the unique kind of a feature
1379   virtual const std::string& getKind() {
1380     static std::string MY_KIND("InternalSelectionInPartFeature");
1381     return MY_KIND;
1382   }
1383   /// Request for initialization of data model of the object: adding all attributes
1384   virtual void initAttributes() {
1385     data()->addAttribute("selection", ModelAPI_AttributeSelectionList::typeId());
1386   }
1387   /// Nothing to do in the execution function
1388   virtual void execute() {}
1389
1390 };
1391
1392 //! Returns the feature that is used for calculation of selection externally from the document
1393 AttributeSelectionListPtr Model_Document::selectionInPartFeature()
1394 {
1395   // return already created, otherwise create
1396   if (!mySelectionFeature.get() || !mySelectionFeature->data()->isValid()) {
1397     // create a new one
1398     mySelectionFeature = FeaturePtr(new Model_SelectionInPartFeature);
1399
1400     TDF_Label aFeatureLab = generalLabel().FindChild(TAG_SELECTION_FEATURE);
1401     std::shared_ptr<Model_Data> aData(new Model_Data);
1402     aData->setLabel(aFeatureLab.FindChild(1));
1403     aData->setObject(mySelectionFeature);
1404     mySelectionFeature->setDoc(myObjs->owner());
1405     mySelectionFeature->setData(aData);
1406     std::string aName = id() + "_Part";
1407     mySelectionFeature->data()->setName(aName);
1408     mySelectionFeature->setDoc(myObjs->owner());
1409     mySelectionFeature->initAttributes();
1410     mySelectionFeature->init(); // to make it enabled and Update correctly
1411     // this update may cause recomputation of the part after selection on it, that is not needed
1412     mySelectionFeature->data()->blockSendAttributeUpdated(true);
1413   }
1414   return mySelectionFeature->selectionList("selection");
1415 }
1416
1417 FeaturePtr Model_Document::lastFeature()
1418 {
1419   if (myObjs)
1420     return myObjs->lastFeature();
1421   return FeaturePtr();
1422 }
1423
1424 static Handle(TNaming_NamedShape) searchForOriginalShape(TopoDS_Shape theShape, TDF_Label aMain) {
1425   Handle(TNaming_NamedShape) aResult;
1426   while(!theShape.IsNull()) { // searching for the very initial shape that produces this one
1427     TopoDS_Shape aShape = theShape;
1428     theShape.Nullify();
1429     // to avoid crash of TNaming_SameShapeIterator if pure shape does not exists
1430     if (!TNaming_Tool::HasLabel(aMain, aShape))
1431       break;
1432     for(TNaming_SameShapeIterator anIter(aShape, aMain); anIter.More(); anIter.Next()) {
1433       TDF_Label aNSLab = anIter.Label();
1434       Handle(TNaming_NamedShape) aNS;
1435       if (aNSLab.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
1436         for(TNaming_Iterator aShapesIter(aNS); aShapesIter.More(); aShapesIter.Next()) {
1437           if (aShapesIter.Evolution() == TNaming_SELECTED ||
1438               aShapesIter.Evolution() == TNaming_DELETE)
1439             continue; // don't use the selection evolution
1440           if (aShapesIter.NewShape().IsSame(aShape)) { // found the original shape
1441             aResult = aNS;
1442             if (aResult->Evolution() == TNaming_MODIFY)
1443               theShape = aShapesIter.OldShape();
1444             // otherwise may me searching for another item of this shape with longer history
1445             if (!theShape.IsNull())
1446               break;
1447           }
1448         }
1449       }
1450     }
1451   }
1452   return aResult;
1453 }
1454
1455 std::shared_ptr<ModelAPI_Feature> Model_Document::producedByFeature(
1456     std::shared_ptr<ModelAPI_Result> theResult,
1457     const std::shared_ptr<GeomAPI_Shape>& theShape)
1458 {
1459   ResultBodyPtr aBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(theResult);
1460   if (!aBody.get()) {
1461     return feature(theResult); // for not-body just returns the feature that produced this result
1462   }
1463   // otherwise get the shape and search the very initial label for it
1464   TopoDS_Shape aShape = theShape->impl<TopoDS_Shape>();
1465   if (aShape.IsNull())
1466     return FeaturePtr();
1467
1468   // for comsolids and compounds all the naming is located in the main object, so, try to use
1469   // it first
1470   ResultCompSolidPtr aMain = ModelAPI_Tools::compSolidOwner(theResult);
1471   if (aMain.get()) {
1472     FeaturePtr aMainRes = producedByFeature(aMain, theShape);
1473     if (aMainRes)
1474       return aMainRes;
1475   }
1476
1477   std::shared_ptr<Model_Data> aBodyData = std::dynamic_pointer_cast<Model_Data>(theResult->data());
1478   if (!aBodyData.get() || !aBodyData->isValid())
1479     return FeaturePtr();
1480
1481   TopoDS_Shape anOldShape; // old shape in the pair oldshape->theShape in the named shape
1482   TopoDS_Shape aShapeContainer; // old shape of the shape that contains aShape as sub-element
1483   Handle(TNaming_NamedShape) aCandidatInThis, aCandidatContainer;
1484   TDF_Label aBodyLab = aBodyData->label();
1485   // use childs and this label (the lowest priority)
1486   TDF_ChildIDIterator aNSIter(aBodyLab, TNaming_NamedShape::GetID(), Standard_True);
1487   bool aUseThis = !aNSIter.More();
1488   while(anOldShape.IsNull() && (aNSIter.More() || aUseThis)) {
1489     Handle(TNaming_NamedShape) aNS;
1490     if (aUseThis) {
1491       if (!aBodyLab.FindAttribute(TNaming_NamedShape::GetID(), aNS))
1492         break;
1493     } else {
1494       aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value());
1495     }
1496     for(TNaming_Iterator aShapesIter(aNS); aShapesIter.More(); aShapesIter.Next()) {
1497       if (aShapesIter.Evolution() == TNaming_SELECTED || aShapesIter.Evolution() == TNaming_DELETE)
1498         continue; // don't use the selection evolution
1499       if (aShapesIter.NewShape().IsSame(aShape)) { // found the original shape
1500         aCandidatInThis = aNS;
1501         if (aCandidatInThis->Evolution() == TNaming_MODIFY)
1502           anOldShape = aShapesIter.OldShape();
1503         // otherwise may me searching for another item of this shape with longer history
1504         if (!anOldShape.IsNull())
1505           break;
1506       }
1507       // check that the shape contains aShape as sub-shape to fill container
1508       if (aShapesIter.NewShape().ShapeType() < aShape.ShapeType() && aCandidatContainer.IsNull()) {
1509         TopExp_Explorer anExp(aShapesIter.NewShape(), aShape.ShapeType());
1510         for(; anExp.More(); anExp.Next()) {
1511           if (aShape.IsSame(anExp.Current())) {
1512             aCandidatContainer = aNS;
1513             aShapeContainer = aShapesIter.NewShape();
1514           }
1515         }
1516       }
1517     }
1518     // iterate to the next label or to the body label in the end
1519     if (!aUseThis)
1520       aNSIter.Next();
1521     if (!aNSIter.More()) {
1522       if (aUseThis)
1523         break;
1524       aUseThis = true;
1525     }
1526   }
1527   if (aCandidatInThis.IsNull()) {
1528     // to fix 1512: searching for original shape of this shape
1529     // if modification of it is not in this result
1530     aCandidatInThis = searchForOriginalShape(aShape, myDoc->Main());
1531     if (aCandidatInThis.IsNull()) {
1532       if (aCandidatContainer.IsNull())
1533         return FeaturePtr();
1534       // with the lower priority use the higher level shape that contains aShape
1535       aCandidatInThis = aCandidatContainer;
1536       anOldShape = aShapeContainer;
1537     } else {
1538       // to stop the searching by the following searchForOriginalShape
1539       anOldShape.Nullify();
1540     }
1541   }
1542
1543   Handle(TNaming_NamedShape) aNS = searchForOriginalShape(anOldShape, myDoc->Main());
1544   if (!aNS.IsNull())
1545     aCandidatInThis = aNS;
1546
1547   FeaturePtr aResult;
1548   TDF_Label aResultLab = aCandidatInThis->Label();
1549   while(aResultLab.Depth() > 3)
1550     aResultLab = aResultLab.Father();
1551   FeaturePtr aFeature = myObjs->feature(aResultLab);
1552   if (aFeature.get()) {
1553     if (!aResult.get() || myObjs->isLater(aResult, aFeature)) {
1554       aResult = aFeature;
1555     }
1556   }
1557   return aResult;
1558 }
1559
1560 bool Model_Document::isLater(FeaturePtr theLater, FeaturePtr theCurrent) const
1561 {
1562   return myObjs->isLater(theLater, theCurrent);
1563 }
1564
1565 void Model_Document::storeNodesState(const std::list<bool>& theStates)
1566 {
1567   TDF_Label aLab = generalLabel().FindChild(TAG_NODES_STATE);
1568   aLab.ForgetAllAttributes();
1569   if (!theStates.empty()) {
1570     Handle(TDataStd_BooleanArray) anArray =
1571       TDataStd_BooleanArray::Set(aLab, 0, int(theStates.size()) - 1);
1572     std::list<bool>::const_iterator aState = theStates.begin();
1573     for(int anIndex = 0; aState != theStates.end(); aState++, anIndex++) {
1574       anArray->SetValue(anIndex, *aState);
1575     }
1576   }
1577 }
1578
1579 void Model_Document::restoreNodesState(std::list<bool>& theStates) const
1580 {
1581   TDF_Label aLab = generalLabel().FindChild(TAG_NODES_STATE);
1582   Handle(TDataStd_BooleanArray) anArray;
1583   if (aLab.FindAttribute(TDataStd_BooleanArray::GetID(), anArray)) {
1584     int anUpper = anArray->Upper();
1585     for(int anIndex = 0; anIndex <= anUpper; anIndex++) {
1586       theStates.push_back(anArray->Value(anIndex) == Standard_True);
1587     }
1588   }
1589 }