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