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