]> SALOME platform Git repositories - modules/shaper.git/blob - src/Model/Model_Document.cpp
Salome HOME
Task 5.1.7: To be able to export a part to a file and import it into an existing...
[modules/shaper.git] / src / Model / Model_Document.cpp
1 // Copyright (C) 2014-2019  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include <Model_Document.h>
21 #include <Model_Data.h>
22 #include <Model_Objects.h>
23 #include <Model_Application.h>
24 #include <Model_Session.h>
25 #include <Model_Events.h>
26 #include <Model_Tools.h>
27 #include <ModelAPI_ResultPart.h>
28 #include <ModelAPI_Validator.h>
29 #include <ModelAPI_CompositeFeature.h>
30 #include <ModelAPI_AttributeSelectionList.h>
31 #include <ModelAPI_Tools.h>
32 #include <ModelAPI_ResultBody.h>
33 #include <Events_Loop.h>
34 #include <Events_InfoMessage.h>
35 #include <GeomAPI_Tools.h>
36
37 #include <TDataStd_Integer.hxx>
38 #include <TDataStd_Comment.hxx>
39 #include <TDF_ChildIDIterator.hxx>
40 #include <TDataStd_ReferenceArray.hxx>
41 #include <TDataStd_ReferenceList.hxx>
42 #include <TDataStd_IntegerArray.hxx>
43 #include <TDataStd_HLabelArray1.hxx>
44 #include <TDataStd_Name.hxx>
45 #include <TDataStd_AsciiString.hxx>
46 #include <TDF_Reference.hxx>
47 #include <TDF_ChildIDIterator.hxx>
48 #include <TDF_Delta.hxx>
49 #include <TDF_AttributeDelta.hxx>
50 #include <TDF_AttributeDeltaList.hxx>
51 #include <TDF_ListIteratorOfAttributeDeltaList.hxx>
52 #include <TDF_ListIteratorOfLabelList.hxx>
53 #include <TDF_LabelMap.hxx>
54 #include <TDF_Tool.hxx>
55 #include <TDF_DeltaOnAddition.hxx>
56 #include <TDataStd_ExtStringList.hxx>
57 #include <TDataStd_UAttribute.hxx>
58 #include <TNaming_Builder.hxx>
59 #include <TNaming_SameShapeIterator.hxx>
60 #include <TNaming_Iterator.hxx>
61 #include <TNaming_NamedShape.hxx>
62 #include <TNaming_Tool.hxx>
63 #include <TNaming_OldShapeIterator.hxx>
64 #include <TopTools_DataMapOfShapeShape.hxx>
65 #include <TopTools_ListOfShape.hxx>
66
67 #include <TopExp_Explorer.hxx>
68 #include <TopoDS_Shape.hxx>
69
70 #include <OSD_Directory.hxx>
71 #include <OSD_File.hxx>
72 #include <OSD_Path.hxx>
73 #include <OSD_Protection.hxx>
74 #include <CDF_Session.hxx>
75 #include <CDF_Directory.hxx>
76 #include <UTL.hxx>
77
78 #include <climits>
79 #ifndef WIN32
80 #include <sys/stat.h>
81 #endif
82
83 #ifdef WIN32
84 # define _separator_ '\\'
85 #else
86 # define _separator_ '/'
87 #endif
88
89 static const int UNDO_LIMIT = 1000;  // number of possible undo operations (big for sketcher)
90
91 static const int TAG_GENERAL = 1;  // general properties tag
92
93 // general sub-labels
94 /// where the reference to the current feature label is located (or no attribute if null feature)
95 static const int TAG_CURRENT_FEATURE = 1; ///< reference to the current feature
96 /// integer, index of the transaction + GUID for auto recomputation blocking
97 static const int TAG_CURRENT_TRANSACTION = 2;
98 static const int TAG_SELECTION_FEATURE = 3; ///< integer, tag of the selection feature label
99 static const int TAG_NODES_STATE = 4; ///< array, tag of the Object Browser nodes states
100 ///< naming structures constructions selected from other document
101 static const int TAG_EXTERNAL_CONSTRUCTIONS = 5;
102
103 /// reference to the shape in external document: sting list attribute identifier
104 static const Standard_GUID kEXTERNAL_SHAPE_REF("9aa5dd14-6d34-4a8d-8786-05842fd7bbbd");
105
106 Model_Document::Model_Document(const int theID, const std::string theKind)
107     : myID(theID), myKind(theKind), myIsActive(false), myIsSetCurrentFeature(false),
108       myDoc(new TDocStd_Document("BinOcaf"))  // binary OCAF format
109 {
110 #ifdef TINSPECTOR
111   CDF_Session::CurrentSession()->Directory()->Add(myDoc);
112 #endif
113   myObjs = new Model_Objects(myDoc->Main());
114   myDoc->SetUndoLimit(UNDO_LIMIT);
115   myTransactionSave = 0;
116   myExecuteFeatures = true;
117   // to have something in the document and avoid empty doc open/save problem
118   // in transaction for nesting correct working
119   myDoc->NewCommand();
120   TDataStd_Integer::Set(myDoc->Main().Father(), 0);
121   // this to avoid creation of integer attribute outside the transaction after undo
122   transactionID();
123   myDoc->CommitCommand();
124 }
125
126 void Model_Document::setThis(DocumentPtr theDoc)
127 {
128   myObjs->setOwner(theDoc);
129 }
130
131 /// Returns the file name of this document by the name of directory and identifier of a document
132 static TCollection_ExtendedString DocFileName(const char* theDirName, const std::string& theID)
133 {
134   TCollection_ExtendedString aPath((const Standard_CString) theDirName);
135   // remove end-separators
136   while(aPath.Length() &&
137         (aPath.Value(aPath.Length()) == '\\' || aPath.Value(aPath.Length()) == '/'))
138     aPath.Remove(aPath.Length());
139   aPath += _separator_;
140   aPath += theID.c_str();
141   aPath += ".cbf";  // standard binary file extension
142   return aPath;
143 }
144
145 bool Model_Document::isRoot() const
146 {
147   return this == Model_Session::get()->moduleDocument().get();
148 }
149
150 // LCOV_EXCL_START
151 /// Makes all modification and generation naming shapes that have old shapes corresponding to
152 /// shapes in a root document be equal to this root document
153 static void updateShapesFromRoot(const TDF_Label theThisAccess, const TDF_Label theRootAccess)
154 {
155   TopTools_DataMapOfShapeShape aCurrentToRoot; // shapes that must be updated: from this to root
156   TDF_ChildIDIterator aThisIter(theThisAccess.Root(), kEXTERNAL_SHAPE_REF, true);
157   for(; aThisIter.More(); aThisIter.Next()) {
158     aCurrentToRoot.Clear();
159     Handle(TNaming_NamedShape) aNS;
160     if (!aThisIter.Value()->Label().FindAttribute(TNaming_NamedShape::GetID(), aNS))
161       continue;
162     if (aNS->Evolution() != TNaming_GENERATED && aNS->Evolution() != TNaming_MODIFY)
163       continue;
164     for (TNaming_Iterator aNSIter(aNS); aNSIter.More(); aNSIter.Next()) {
165       const TopoDS_Shape& anOld = aNSIter.OldShape();
166       if (anOld.IsNull())
167         continue;
168       TNaming_OldShapeIterator aNewIter(anOld, theThisAccess);
169       for (; aNewIter.More(); aNewIter.Next()) {
170         TNaming_Evolution anEvolution = aNewIter.NamedShape()->Evolution();
171         if (anEvolution != TNaming_SELECTED && anEvolution != TNaming_DELETE)
172           break;
173       }
174       if (aNewIter.More())
175         continue;
176       GeomShapePtr anOldShape(new GeomAPI_Shape), aRootShape(new GeomAPI_Shape);
177       anOldShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(anOld));
178       anOldShape = GeomAPI_Tools::getTypedShape(anOldShape);
179
180       // search the same shape in the root document
181       Handle(TDataStd_ExtStringList) anEntries =
182         Handle(TDataStd_ExtStringList)::DownCast(aThisIter.Value());
183       TDataStd_ListOfExtendedString::Iterator anIter(anEntries->List());
184       for (; anIter.More(); anIter.Next()) {
185         TDF_Label aRootLab;
186         TDF_Tool::Label(theRootAccess.Data(), anIter.Value(), aRootLab);
187         if (aRootLab.IsNull())
188           continue;
189         Handle(TNaming_NamedShape) aRootNS;
190         if (!aRootLab.FindAttribute(TNaming_NamedShape::GetID(), aRootNS))
191           continue;
192         TNaming_Iterator aRootShapes(aRootNS);
193         for (; aRootShapes.More(); aRootShapes.Next()) {
194           if (aRootShapes.NewShape().IsNull())
195             continue;
196           aRootShape->setImpl(new TopoDS_Shape(aRootShapes.NewShape()));
197           aRootShape = GeomAPI_Tools::getTypedShape(aRootShape);
198           if (!anOldShape->isEqual(aRootShape)) // special checking by geometry
199             continue;
200           // found a good corresponded shape
201           if (!anOld.IsEqual(aRootShapes.NewShape()))
202             aCurrentToRoot.Bind(anOld, aRootShapes.NewShape());
203         }
204       }
205     }
206     if (!aCurrentToRoot.IsEmpty()) { // update the whole named shape content
207       TopTools_ListOfShape anOld, aNew;
208       TNaming_Evolution anEvol = aNS->Evolution();
209       for(TNaming_Iterator aNSIter(aNS); aNSIter.More(); aNSIter.Next()) {
210         anOld.Prepend(aCurrentToRoot.IsBound(aNSIter.OldShape()) ?
211           aCurrentToRoot.Find(aNSIter.OldShape()) : aNSIter.OldShape());
212         aNew.Prepend(aNSIter.NewShape());
213       }
214       TNaming_Builder aBuilder(aNS->Label());
215       TopTools_ListOfShape::Iterator anOldIter(anOld), aNewIter(aNew);
216       for(; anOldIter.More(); anOldIter.Next(), aNewIter.Next()) {
217         if (anEvol == TNaming_GENERATED) {
218           aBuilder.Generated(anOldIter.Value(), aNewIter.Value());
219         } else if (anEvol == TNaming_MODIFY) {
220           aBuilder.Modify(anOldIter.Value(), aNewIter.Value());
221         }
222       }
223     }
224   }
225 }
226 // LCOV_EXCL_STOP
227
228 static bool loadDocument(Handle(Model_Application) theApp,
229                          Handle(TDocStd_Document)& theDoc,
230                          const TCollection_ExtendedString& theFilename)
231 {
232   PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus)-1;
233   try {
234     aStatus = theApp->Open(theFilename, theDoc);
235   } catch (Standard_Failure const& anException) {
236     Events_InfoMessage("Model_Document",
237         "Exception in opening of document: %1").arg(anException.GetMessageString()).send();
238     return false;
239   }
240   bool isOk = aStatus == PCDM_RS_OK;
241   if (!isOk) {
242     // LCOV_EXCL_START
243     switch (aStatus) {
244       case PCDM_RS_UnknownDocument:
245         Events_InfoMessage("Model_Document", "Can not open document").send();
246         break;
247       case PCDM_RS_AlreadyRetrieved:
248         Events_InfoMessage("Model_Document", "Can not open document: already opened").send();
249         break;
250       case PCDM_RS_AlreadyRetrievedAndModified:
251         Events_InfoMessage("Model_Document",
252             "Can not open document: already opened and modified").send();
253         break;
254       case PCDM_RS_NoDriver:
255         Events_InfoMessage("Model_Document",
256                            "Can not open document: driver library is not found").send();
257         break;
258       case PCDM_RS_UnknownFileDriver:
259         Events_InfoMessage("Model_Document",
260                            "Can not open document: unknown driver for opening").send();
261         break;
262       case PCDM_RS_OpenError:
263         Events_InfoMessage("Model_Document", "Can not open document: file open error").send();
264         break;
265       case PCDM_RS_NoVersion:
266         Events_InfoMessage("Model_Document", "Can not open document: invalid version").send();
267         break;
268       case PCDM_RS_NoModel:
269         Events_InfoMessage("Model_Document", "Can not open document: no data model").send();
270         break;
271       case PCDM_RS_NoDocument:
272         Events_InfoMessage("Model_Document", "Can not open document: no document inside").send();
273         break;
274       case PCDM_RS_FormatFailure:
275         Events_InfoMessage("Model_Document", "Can not open document: format failure").send();
276         break;
277       case PCDM_RS_TypeNotFoundInSchema:
278         Events_InfoMessage("Model_Document", "Can not open document: invalid object").send();
279         break;
280       case PCDM_RS_UnrecognizedFileFormat:
281         Events_InfoMessage("Model_Document",
282                            "Can not open document: unrecognized file format").send();
283         break;
284       case PCDM_RS_MakeFailure:
285         Events_InfoMessage("Model_Document", "Can not open document: make failure").send();
286         break;
287       case PCDM_RS_PermissionDenied:
288         Events_InfoMessage("Model_Document", "Can not open document: permission denied").send();
289         break;
290       case PCDM_RS_DriverFailure:
291         Events_InfoMessage("Model_Document", "Can not open document: driver failure").send();
292         break;
293       default:
294         Events_InfoMessage("Model_Document", "Can not open document: unknown error").send();
295         break;
296     }
297     // LCOV_EXCL_STOP
298   }
299   return isOk;
300 }
301
302 bool Model_Document::load(const char* theDirName, const char* theFileName, DocumentPtr theThis)
303 {
304   Handle(Model_Application) anApp = Model_Application::getApplication();
305   if (isRoot()) {
306     anApp->setLoadPath(theDirName);
307   }
308   TCollection_ExtendedString aPath(DocFileName(theDirName, theFileName));
309   Handle(TDocStd_Document) aLoaded;
310   bool isOk = loadDocument(anApp, aLoaded, aPath);
311
312   std::shared_ptr<Model_Session> aSession =
313     std::dynamic_pointer_cast<Model_Session>(Model_Session::get());
314   if (isOk) {
315     myDoc = aLoaded;
316     myDoc->SetUndoLimit(UNDO_LIMIT);
317
318     // to avoid the problem that feature is created in the current, not this, document
319     aSession->setActiveDocument(anApp->document(myID), false);
320     aSession->setCheckTransactions(false);
321     if (myObjs)
322       delete myObjs;
323     myObjs = new Model_Objects(myDoc->Main()); // synchronization is inside
324     myObjs->setOwner(theThis);
325     // update the current features status
326     setCurrentFeature(currentFeature(false), false);
327     aSession->setCheckTransactions(true);
328     aSession->setActiveDocument(aSession->moduleDocument(), false);
329     // this is done in Part result "activate", so no needed here. Causes not-blue active part.
330     // aSession->setActiveDocument(anApp->getDocument(myID), true);
331
332     // make sub-parts as loaded by demand
333     std::list<ResultPtr> aPartResults;
334     myObjs->allResults(ModelAPI_ResultPart::group(), aPartResults);
335     std::list<ResultPtr>::iterator aPartRes = aPartResults.begin();
336     for(; aPartRes != aPartResults.end(); aPartRes++) {
337       ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aPartRes);
338       if (aPart.get())
339         anApp->setLoadByDemand(aPart->data()->name(),
340           aPart->data()->document(ModelAPI_ResultPart::DOC_REF())->docId());
341     }
342     if (!isRoot()) {
343       updateShapesFromRoot(myDoc->Main(),
344         std::dynamic_pointer_cast<Model_Document>(aSession->moduleDocument())->generalLabel());
345     }
346   } else { // open failed, but new document was created to work with it: inform the model
347     aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false);
348   }
349   return isOk;
350 }
351
352 bool Model_Document::import(const char* theFileName, bool theCheckBefore)
353 {
354   Handle(Model_Application) anApp = Model_Application::getApplication();
355   TCollection_ExtendedString aFormat;
356   if (!anApp->Format(theFileName, aFormat))
357     return false;
358
359   Handle(TDocStd_Document) aTempDoc;
360   bool isOk = loadDocument(anApp, aTempDoc, theFileName);
361
362   if (isOk && theCheckBefore) {
363     // verify all features are applicable for the current document type (e.g. PartSet)
364     std::shared_ptr<Model_Session> aSession =
365         std::dynamic_pointer_cast<Model_Session>(ModelAPI_Session::get());
366     for (TDF_ChildIterator anIt(aTempDoc->Main()); anIt.More() && isOk; anIt.Next()) {
367       TDF_Label aCurrentLab = anIt.Value();
368       Handle(TDataStd_Comment) aFeatureID;
369       TDF_Label aNewFeatuerLab;
370       if (aCurrentLab.FindAttribute(TDataStd_Comment::GetID(), aFeatureID)) {
371         TCollection_AsciiString anID(aFeatureID->Get());
372         std::string aFeatureKind(anID.ToCString());
373         if (aSession->myPlugins.find(aFeatureKind) != aSession->myPlugins.end()) {
374           std::string& aDocKind = aSession->myPlugins[aFeatureKind].second;
375           isOk = aDocKind.empty() || aDocKind == kind();
376         }
377       }
378     }
379   }
380
381   if (isOk) {
382     // copy features from the temporary document to the current
383     Handle(TDF_RelocationTable) aRelocTable = new TDF_RelocationTable();
384     TDF_LabelList anAllNewFeatures;
385     // Perform the copying twice for correct references:
386     // 1. copy labels hierarchy and fill the relocation table
387     TDF_Label aMain = myDoc->Main();
388     for (TDF_ChildIterator anIt(aTempDoc->Main()); anIt.More(); anIt.Next()) {
389       TDF_Label aCurrentLab = anIt.Value();
390       Handle(TDataStd_Comment) aFeatureID;
391       TDF_Label aNewFeatuerLab;
392       if (aCurrentLab.FindAttribute(TDataStd_Comment::GetID(), aFeatureID)) {
393         TCollection_AsciiString anID(aFeatureID->Get());
394         FeaturePtr aNewFeature = addFeature(anID.ToCString());
395         std::shared_ptr<Model_Data> aData =
396             std::dynamic_pointer_cast<Model_Data>(aNewFeature->data());
397         aNewFeatuerLab = aData->label().Father();
398         Model_Tools::copyLabels(aCurrentLab, aNewFeatuerLab, aRelocTable);
399       }
400       anAllNewFeatures.Append(aNewFeatuerLab);
401     }
402     // 2. copy attributes
403     std::set<TCollection_AsciiString> aCoordinateLabels;
404     Model_Tools::labelsOfCoordinates(aCoordinateLabels, aRelocTable);
405     TDF_ListIteratorOfLabelList aNewIt(anAllNewFeatures);
406     for (TDF_ChildIterator anIt(aTempDoc->Main()); anIt.More(); anIt.Next()) {
407       TDF_Label aCurrentLab = anIt.Value();
408       TDF_Label aFeatureLab = aNewIt.Value();
409       if (aFeatureLab.IsNull())
410         anAllNewFeatures.Remove(aNewIt);
411       else {
412         Model_Tools::copyAttrsAndKeepRefsToCoordinates(
413             aCurrentLab, aFeatureLab, aCoordinateLabels, aRelocTable);
414         aNewIt.Next();
415       }
416     }
417
418     myObjs->synchronizeFeatures(anAllNewFeatures, true, false, false, true);
419   }
420
421   if (anApp->CanClose(aTempDoc) == CDM_CCS_OK)
422     anApp->Close(aTempDoc);
423   return isOk;
424 }
425
426 static bool saveDocument(Handle(Model_Application) theApp,
427                          Handle(TDocStd_Document) theDoc,
428                          const TCollection_ExtendedString& theFilename)
429 {
430   PCDM_StoreStatus aStatus;
431   try {
432     // create the directory to save the document
433     OSD_Path aPathToFile = UTL::Path(theFilename);
434     aPathToFile.SetName("");
435     aPathToFile.SetExtension("");
436     OSD_Directory aBaseDir(aPathToFile);
437     if (!aBaseDir.Exists())
438       aBaseDir.Build(OSD_Protection());
439     // save the document
440     aStatus = theApp->SaveAs(theDoc, theFilename);
441   }
442   catch (Standard_Failure const& anException) {
443     Events_InfoMessage("Model_Document",
444       "Exception in saving of document: %1").arg(anException.GetMessageString()).send();
445     return false;
446   }
447   bool isDone = aStatus == PCDM_SS_OK || aStatus == PCDM_SS_No_Obj;
448   if (!isDone) {
449     switch (aStatus) {
450     case PCDM_SS_DriverFailure:
451       Events_InfoMessage("Model_Document",
452         "Can not save document: save driver-library failure").send();
453       break;
454     case PCDM_SS_WriteFailure:
455       Events_InfoMessage("Model_Document", "Can not save document: file writing failure").send();
456       break;
457     case PCDM_SS_Failure:
458     default:
459       Events_InfoMessage("Model_Document", "Can not save document").send();
460       break;
461     }
462   }
463   return isDone;
464 }
465
466 bool Model_Document::save(
467   const char* theDirName, const char* theFileName, std::list<std::string>& theResults)
468 {
469   // if the history line is not in the end, move it to the end before save, otherwise
470   // problems with results restore and (the most important) naming problems will appear
471   // due to change evolution to SELECTION (problems in NamedShape and Name)
472   FeaturePtr aWasCurrent;
473   std::shared_ptr<Model_Session> aSession =
474     std::dynamic_pointer_cast<Model_Session>(Model_Session::get());
475   if (currentFeature(false) != lastFeature()) {
476     aSession->setCheckTransactions(false);
477     aWasCurrent = currentFeature(false);
478     // if last is nested into something else, make this something else as last:
479     // otherwise it will look like edition of sub-element, so, the main will be disabled
480     FeaturePtr aLast = lastFeature();
481     if (aLast.get()) {
482       CompositeFeaturePtr aMain = ModelAPI_Tools::compositeOwner(aLast);
483       while(aMain.get()) {
484         aLast = aMain;
485         aMain = ModelAPI_Tools::compositeOwner(aLast);
486       }
487     }
488     setCurrentFeature(aLast, true);
489   }
490   // create a directory in the root document if it is not yet exist
491   Handle(Model_Application) anApp = Model_Application::getApplication();
492   if (isRoot()) {
493 #ifdef WIN32
494     size_t aDirLen = strlen(theDirName);
495     std::wstring aWStr(aDirLen, L'#');
496     mbstowcs(&aWStr[0], theDirName, aDirLen);
497     CreateDirectory(aWStr.c_str(), NULL);
498 #else
499     mkdir(theDirName, 0x1ff);
500 #endif
501   }
502   // filename in the dir is id of document inside of the given directory
503   TCollection_ExtendedString aPath(DocFileName(theDirName, theFileName));
504   bool isDone = saveDocument(anApp, myDoc, aPath);
505
506   if (aWasCurrent.get()) { // return the current feature to the initial position
507     setCurrentFeature(aWasCurrent, false);
508     aSession->setCheckTransactions(true);
509   }
510
511   myTransactionSave = int(myTransactions.size());
512   if (isDone) {  // save also sub-documents if any
513     theResults.push_back(TCollection_AsciiString(aPath).ToCString());
514     // iterate all result parts to find all loaded or not yet loaded documents
515     std::list<ResultPtr> aPartResults;
516     myObjs->allResults(ModelAPI_ResultPart::group(), aPartResults);
517     std::list<ResultPtr>::iterator aPartRes = aPartResults.begin();
518     for(; aPartRes != aPartResults.end(); aPartRes++) {
519       ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aPartRes);
520       if (!aPart->isActivated()) {
521         // copy not-activated document that is not in the memory
522         std::string aDocName = aPart->data()->name();
523         if (!aDocName.empty()) {
524           // just copy file
525           TCollection_AsciiString aSubPath(DocFileName(anApp->loadPath().c_str(), aDocName));
526           OSD_Path aPath(aSubPath);
527           OSD_File aFile(aPath);
528           if (aFile.Exists()) {
529             TCollection_AsciiString aDestinationDir(DocFileName(theDirName, aDocName));
530             OSD_Path aDestination(aDestinationDir);
531             aFile.Copy(aDestination);
532             theResults.push_back(aDestinationDir.ToCString());
533           } else {
534             Events_InfoMessage("Model_Document",
535               "Can not open file %1 for saving").arg(aSubPath.ToCString()).send();
536           }
537         }
538       } else { // simply save opened document
539         isDone = std::dynamic_pointer_cast<Model_Document>(aPart->partDoc())->
540           save(theDirName, aPart->data()->name().c_str(), theResults);
541       }
542     }
543   }
544   return isDone;
545 }
546
547 bool Model_Document::save(const char* theFilename,
548                           const std::list<FeaturePtr>& theExportFeatures) const
549 {
550   Handle(Model_Application) anApp = Model_Application::getApplication();
551   TCollection_ExtendedString aFormat;
552   if (!anApp->Format(theFilename, aFormat))
553     return false;
554
555   Handle(TDocStd_Document) aTempDoc = new TDocStd_Document(aFormat);
556   TDF_Label aMain = aTempDoc->Main();
557
558   Handle(TDF_RelocationTable) aRelocTable = new TDF_RelocationTable();
559   std::list<FeaturePtr>::const_iterator anIt = theExportFeatures.begin();
560   // Perform the copying twice for correct references:
561   // 1. copy labels hierarchy and fill the relocation table
562   for (; anIt != theExportFeatures.end(); ++anIt) {
563     TDF_Label aFeatureLab = aMain.NewChild();
564     std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>((*anIt)->data());
565     Model_Tools::copyLabels(aData->label().Father(), aFeatureLab, aRelocTable);
566   }
567   // 2. copy attributes
568   std::set<TCollection_AsciiString> aCoordinateLabels;
569   Model_Tools::labelsOfCoordinates(aCoordinateLabels, aRelocTable);
570   TDF_ChildIterator aChildIt(aMain);
571   for (anIt = theExportFeatures.begin(); anIt != theExportFeatures.end(); ++anIt) {
572     TDF_Label aFeatureLab = aChildIt.Value();
573     std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>((*anIt)->data());
574     Model_Tools::copyAttrsAndKeepRefsToCoordinates(
575         aData->label().Father(), aFeatureLab, aCoordinateLabels, aRelocTable);
576     aChildIt.Next();
577   }
578
579   bool isDone = saveDocument(anApp, aTempDoc, theFilename);
580   if (aTempDoc->CanClose() == CDM_CCS_OK)
581     aTempDoc->Close();
582   return isDone;
583 }
584
585 void Model_Document::close(const bool theForever)
586 {
587   std::shared_ptr<ModelAPI_Session> aPM = Model_Session::get();
588   if (!isRoot() && this == aPM->activeDocument().get()) {
589     aPM->setActiveDocument(aPM->moduleDocument());
590   } else if (isRoot()) {
591     // erase the active document if root is closed
592     aPM->setActiveDocument(DocumentPtr());
593   }
594   // close all subs
595   const std::set<int> aSubs = subDocuments();
596   std::set<int>::iterator aSubIter = aSubs.begin();
597   for (; aSubIter != aSubs.end(); aSubIter++) {
598     std::shared_ptr<Model_Document> aSub = subDoc(*aSubIter);
599     if (aSub->myObjs) // if it was not closed before
600       aSub->close(theForever);
601   }
602
603   // close for this document needs no transaction in this document
604   std::static_pointer_cast<Model_Session>(Model_Session::get())->setCheckTransactions(false);
605
606   // close all only if it is really asked, otherwise it can be undone/redone
607   if (theForever) {
608     // flush everything to avoid messages with bad objects
609     delete myObjs;
610     myObjs = 0;
611     if (myDoc->CanClose() == CDM_CCS_OK)
612       myDoc->Close();
613     mySelectionFeature.reset();
614   } else {
615     setCurrentFeature(FeaturePtr(), false); // disables all features
616     // update the OB: features are disabled (on remove of Part)
617     Events_Loop* aLoop = Events_Loop::loop();
618     static Events_ID aDeleteEvent = Events_Loop::eventByName(EVENT_OBJECT_DELETED);
619     aLoop->flush(aDeleteEvent);
620   }
621
622   std::static_pointer_cast<Model_Session>(Model_Session::get())->setCheckTransactions(true);
623 }
624
625 void Model_Document::startOperation()
626 {
627   incrementTransactionID(); // outside of transaction in order to avoid empty transactions keeping
628   if (myDoc->HasOpenCommand()) {  // start of nested command
629     if (myDoc->CommitCommand()) {
630       // commit the current: it will contain all nested after compactification
631       myTransactions.rbegin()->myOCAFNum++; // if has open command, the list is not empty
632     }
633     myNestedNum.push_back(0); // start of nested operation with zero transactions inside yet
634     myDoc->OpenCommand();
635   } else {  // start the simple command
636     myDoc->NewCommand();
637   }
638   // starts a new operation
639   myTransactions.push_back(Transaction());
640   if (!myNestedNum.empty())
641     (*myNestedNum.rbegin())++;
642   myRedos.clear();
643   // new command for all subs
644   const std::set<int> aSubs = subDocuments();
645   std::set<int>::iterator aSubIter = aSubs.begin();
646   for (; aSubIter != aSubs.end(); aSubIter++)
647     subDoc(*aSubIter)->startOperation();
648 }
649
650 void Model_Document::compactNested()
651 {
652   if (!myNestedNum.empty()) {
653     int aNumToCompact = *(myNestedNum.rbegin());
654     int aSumOfTransaction = 0;
655     for(int a = 0; a < aNumToCompact; a++) {
656       aSumOfTransaction += myTransactions.rbegin()->myOCAFNum;
657       myTransactions.pop_back();
658     }
659     // the latest transaction is the start of lower-level operation which starts the nested
660     myTransactions.rbegin()->myOCAFNum += aSumOfTransaction;
661     myNestedNum.pop_back();
662   }
663 }
664
665 /// Compares the content of the given attributes, returns true if equal.
666 /// This method is used to avoid empty transactions when only "current" is changed
667 /// to some value and then comes back in this transaction, so, it compares only
668 /// references and Boolean and Integer Arrays for the current moment.
669 static bool isEqualContent(Handle(TDF_Attribute) theAttr1, Handle(TDF_Attribute) theAttr2)
670 {
671   if (Standard_GUID::IsEqual(theAttr1->ID(), TDF_Reference::GetID())) { // reference
672     Handle(TDF_Reference) aRef1 = Handle(TDF_Reference)::DownCast(theAttr1);
673     Handle(TDF_Reference) aRef2 = Handle(TDF_Reference)::DownCast(theAttr2);
674     if (aRef1.IsNull() && aRef2.IsNull())
675       return true;
676     if (aRef1.IsNull() || aRef2.IsNull())
677       return false;
678     return aRef1->Get().IsEqual(aRef2->Get()) == Standard_True;
679   } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDataStd_BooleanArray::GetID())) {
680     Handle(TDataStd_BooleanArray) anArr1 = Handle(TDataStd_BooleanArray)::DownCast(theAttr1);
681     Handle(TDataStd_BooleanArray) anArr2 = Handle(TDataStd_BooleanArray)::DownCast(theAttr2);
682     if (anArr1.IsNull() && anArr2.IsNull())
683       return true;
684     if (anArr1.IsNull() || anArr2.IsNull())
685       return false;
686     if (anArr1->Lower() == anArr2->Lower() && anArr1->Upper() == anArr2->Upper()) {
687       for(int a = anArr1->Lower(); a <= anArr1->Upper(); a++) {
688         if (a == 1 && // second is for display
689             anArr2->Label().Tag() == 1 && (anArr2->Label().Depth() == 4 ||
690             anArr2->Label().Depth() == 6))
691           continue;
692         if (anArr1->Value(a) != anArr2->Value(a))
693           return false;
694       }
695       return true;
696     }
697   } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDataStd_IntegerArray::GetID())) {
698     Handle(TDataStd_IntegerArray) anArr1 = Handle(TDataStd_IntegerArray)::DownCast(theAttr1);
699     Handle(TDataStd_IntegerArray) anArr2 = Handle(TDataStd_IntegerArray)::DownCast(theAttr2);
700     if (anArr1.IsNull() && anArr2.IsNull())
701       return true;
702     if (anArr1.IsNull() || anArr2.IsNull())
703       return false;
704     if (anArr1->Lower() == anArr2->Lower() && anArr1->Upper() == anArr2->Upper()) {
705       for(int a = anArr1->Lower(); a <= anArr1->Upper(); a++)
706         if (anArr1->Value(a) != anArr2->Value(a)) {
707           // avoid the transaction ID checking
708           if (a == 2 && anArr1->Upper() == 2 && anArr2->Label().Tag() == 1 &&
709             (anArr2->Label().Depth() == 4 || anArr2->Label().Depth() == 6))
710             continue;
711           return false;
712         }
713       return true;
714     }
715   } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDataStd_ReferenceArray::GetID())) {
716     Handle(TDataStd_ReferenceArray) anArr1 = Handle(TDataStd_ReferenceArray)::DownCast(theAttr1);
717     Handle(TDataStd_ReferenceArray) anArr2 = Handle(TDataStd_ReferenceArray)::DownCast(theAttr2);
718     if (anArr1.IsNull() && anArr2.IsNull())
719       return true;
720     if (anArr1.IsNull() || anArr2.IsNull())
721       return false;
722     if (anArr1->Lower() == anArr2->Lower() && anArr1->Upper() == anArr2->Upper()) {
723       for(int a = anArr1->Lower(); a <= anArr1->Upper(); a++)
724         if (anArr1->Value(a) != anArr2->Value(a)) {
725           // avoid the transaction ID checking
726           if (a == 2 && anArr1->Upper() == 2 && anArr2->Label().Tag() == 1 &&
727             (anArr2->Label().Depth() == 4 || anArr2->Label().Depth() == 6))
728             continue;
729           return false;
730         }
731       return true;
732     }
733   } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDataStd_ReferenceList::GetID())) {
734     Handle(TDataStd_ReferenceList) aList1 = Handle(TDataStd_ReferenceList)::DownCast(theAttr1);
735     Handle(TDataStd_ReferenceList) aList2= Handle(TDataStd_ReferenceList)::DownCast(theAttr2);
736     if (aList1.IsNull() && aList2.IsNull())
737       return true;
738     if (aList1.IsNull() || aList2.IsNull())
739       return false;
740     const TDF_LabelList& aLList1 = aList1->List();
741     const TDF_LabelList& aLList2 = aList2->List();
742     TDF_ListIteratorOfLabelList aLIter1(aLList1);
743     TDF_ListIteratorOfLabelList aLIter2(aLList2);
744     for(; aLIter1.More() && aLIter2.More(); aLIter1.Next(), aLIter2.Next()) {
745       if (aLIter1.Value() != aLIter2.Value())
746         return false;
747     }
748     return !aLIter1.More() && !aLIter2.More(); // both lists are with the same size
749   } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDF_TagSource::GetID())) {
750     return true; // it just for created and removed feature: nothing is changed
751   }
752   return false;
753 }
754
755 /// Returns true if the last transaction is actually empty: modification to the same values
756 /// were performed only
757 static bool isEmptyTransaction(const Handle(TDocStd_Document)& theDoc) {
758   Handle(TDF_Delta) aDelta;
759   aDelta = theDoc->GetUndos().Last();
760   TDF_LabelList aDeltaList;
761   aDelta->Labels(aDeltaList); // it clears list, so, use new one and then append to the result
762   for(TDF_ListIteratorOfLabelList aListIter(aDeltaList); aListIter.More(); aListIter.Next()) {
763     return false;
764   }
765   // add also label of the modified attributes
766   const TDF_AttributeDeltaList& anAttrs = aDelta->AttributeDeltas();
767   for (TDF_ListIteratorOfAttributeDeltaList anAttr(anAttrs); anAttr.More(); anAttr.Next()) {
768     Handle(TDF_AttributeDelta)& anADelta = anAttr.Value();
769     Handle(TDF_DeltaOnAddition) anAddition = Handle(TDF_DeltaOnAddition)::DownCast(anADelta);
770     if (anAddition.IsNull()) { // if the attribute was added, transaction is not empty
771       if (!anADelta->Label().IsNull() && !anADelta->Attribute().IsNull()) {
772         Handle(TDF_Attribute) aCurrentAttr;
773         if (anADelta->Label().FindAttribute(anADelta->Attribute()->ID(), aCurrentAttr)) {
774           if (isEqualContent(anADelta->Attribute(), aCurrentAttr)) {
775             continue; // attribute is not changed actually
776           }
777         } else
778           if (Standard_GUID::IsEqual(anADelta->Attribute()->ID(), TDataStd_AsciiString::GetID())) {
779             continue; // error message is disappeared
780         }
781       }
782     }
783     return false;
784   }
785   return true;
786 }
787
788 bool Model_Document::finishOperation()
789 {
790   bool isNestedClosed = !myDoc->HasOpenCommand() && !myNestedNum.empty();
791   static std::shared_ptr<Model_Session> aSession =
792     std::static_pointer_cast<Model_Session>(Model_Session::get());
793
794   // open transaction if nested is closed to fit inside
795   // all synchronizeBackRefs and flushed consequences
796   if (isNestedClosed) {
797     myDoc->OpenCommand();
798   }
799   // do it before flashes to enable and recompute nesting features correctly
800   if (myNestedNum.empty() || (isNestedClosed && myNestedNum.size() == 1)) {
801     // if all nested operations are closed, make current the higher level objects (to perform
802     // it in the python scripts correctly): sketch become current after creation of sub-elements
803     FeaturePtr aCurrent = currentFeature(false);
804     CompositeFeaturePtr aMain, aNext = ModelAPI_Tools::compositeOwner(aCurrent);
805     while(aNext.get()) {
806       aMain = aNext;
807       aNext = ModelAPI_Tools::compositeOwner(aMain);
808     }
809     if (aMain.get() && aMain != aCurrent)
810       setCurrentFeature(aMain, false);
811   }
812   myObjs->synchronizeBackRefs();
813   Events_Loop* aLoop = Events_Loop::loop();
814   static const Events_ID kCreatedEvent = aLoop->eventByName(EVENT_OBJECT_CREATED);
815   static const Events_ID kUpdatedEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED);
816   static const Events_ID kRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
817   static const Events_ID kDeletedEvent = aLoop->eventByName(EVENT_OBJECT_DELETED);
818   aLoop->flush(kCreatedEvent);
819   aLoop->flush(kUpdatedEvent);
820   aLoop->flush(kRedispEvent);
821   aLoop->flush(kDeletedEvent);
822
823   if (isNestedClosed) {
824     if (myDoc->CommitCommand())
825       myTransactions.rbegin()->myOCAFNum++;
826   }
827
828   // this must be here just after everything is finished but before real transaction stop
829   // to avoid messages about modifications outside of the transaction
830   // and to rebuild everything after all updates and creates
831   if (isRoot()) { // once for root document
832     static std::shared_ptr<Events_Message> aFinishMsg
833       (new Events_Message(Events_Loop::eventByName("FinishOperation")));
834     Events_Loop::loop()->send(aFinishMsg);
835   }
836
837   // for open of document with primitive box inside (finish transaction in initAttributes)
838   bool aWasActivatedFlushes = aLoop->activateFlushes(true);
839   while(aLoop->hasGrouppedEvent(kCreatedEvent) || aLoop->hasGrouppedEvent(kUpdatedEvent) ||
840         aLoop->hasGrouppedEvent(kRedispEvent) || aLoop->hasGrouppedEvent(kDeletedEvent)) {
841     aLoop->flush(kCreatedEvent);
842     aLoop->flush(kUpdatedEvent);
843     aLoop->flush(kRedispEvent);
844     aLoop->flush(kDeletedEvent);
845   }
846   aLoop->activateFlushes(aWasActivatedFlushes);
847
848   // to avoid "updated" message appearance by updater
849   //aLoop->clear(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
850
851   // finish for all subs first: to avoid nested finishing and "isOperation" calls problems inside
852   bool aResult = false;
853   const std::set<int> aSubs = subDocuments();
854   std::set<int>::iterator aSubIter = aSubs.begin();
855   for (; aSubIter != aSubs.end(); aSubIter++)
856     if (subDoc(*aSubIter)->finishOperation())
857       aResult = true;
858
859   // transaction may be empty if this document was created during this transaction (create part)
860   if (!myTransactions.empty() && myDoc->CommitCommand()) {
861     // if commit is successful, just increment counters
862     if (isEmptyTransaction(myDoc)) { // erase this transaction
863       myDoc->Undo();
864       myDoc->ClearRedos();
865     } else {
866       myTransactions.rbegin()->myOCAFNum++;
867       aResult = true;
868     }
869   }
870
871   if (isNestedClosed) {
872     compactNested();
873   }
874   if (!aResult && !myTransactions.empty() /* it can be for just created part document */)
875     aResult = myTransactions.rbegin()->myOCAFNum != 0;
876
877   if (!aResult && isRoot()) {
878     // nothing inside in all documents, so remove this transaction from the transactions list
879     undoInternal(true, false);
880   }
881   // on finish clear redo in any case (issue 446) and for all subs (issue 408)
882   myDoc->ClearRedos();
883   myRedos.clear();
884   for (aSubIter = aSubs.begin(); aSubIter != aSubs.end(); aSubIter++) {
885     subDoc(*aSubIter)->myDoc->ClearRedos();
886     subDoc(*aSubIter)->myRedos.clear();
887   }
888
889   return aResult;
890 }
891
892 /// Returns in theDelta labels that has been modified in the latest transaction of theDoc
893 static void modifiedLabels(const Handle(TDocStd_Document)& theDoc, TDF_LabelList& theDelta,
894   const bool isRedo = false) {
895   Handle(TDF_Delta) aDelta;
896   if (isRedo)
897     aDelta = theDoc->GetRedos().First();
898   else
899     aDelta = theDoc->GetUndos().Last();
900   TDF_LabelList aDeltaList;
901   aDelta->Labels(aDeltaList); // it clears list, so, use new one and then append to the result
902   for(TDF_ListIteratorOfLabelList aListIter(aDeltaList); aListIter.More(); aListIter.Next()) {
903     theDelta.Append(aListIter.Value());
904   }
905   // add also label of the modified attributes
906   const TDF_AttributeDeltaList& anAttrs = aDelta->AttributeDeltas();
907   /// named shape evolution also modifies integer on this label: exclude it
908   TDF_LabelMap anExcludedInt;
909   for (TDF_ListIteratorOfAttributeDeltaList anAttr(anAttrs); anAttr.More(); anAttr.Next()) {
910     if (anAttr.Value()->Attribute()->ID() == TDataStd_BooleanArray::GetID()) {
911       // Boolean array is used for feature auxiliary attributes only, feature args are not modified
912       continue;
913     }
914     if (anAttr.Value()->Attribute()->ID() == TNaming_NamedShape::GetID()) {
915       anExcludedInt.Add(anAttr.Value()->Label());
916       // named shape evolution is changed in history update => skip them,
917       // they are not the features arguments
918       continue;
919     }
920     if (anAttr.Value()->Attribute()->ID() == TDataStd_Integer::GetID()) {
921       if (anExcludedInt.Contains(anAttr.Value()->Label()))
922         continue;
923     }
924       theDelta.Append(anAttr.Value()->Label());
925   }
926   TDF_ListIteratorOfLabelList aDeltaIter(theDelta);
927   for(; aDeltaIter.More(); aDeltaIter.Next()) {
928     if (anExcludedInt.Contains(aDeltaIter.Value())) {
929       theDelta.Remove(aDeltaIter);
930       if (!aDeltaIter.More())
931         break;
932     }
933   }
934 }
935
936 void Model_Document::abortOperation()
937 {
938   TDF_LabelList aDeltaLabels; // labels that are updated during "abort"
939   if (!myNestedNum.empty() && !myDoc->HasOpenCommand()) {  // abort all what was done in nested
940     compactNested();
941     // store undo-delta here as undo actually does in the method later
942     int a, aNumTransactions = myTransactions.rbegin()->myOCAFNum;
943     for(a = 0; a < aNumTransactions; a++) {
944       modifiedLabels(myDoc, aDeltaLabels);
945       myDoc->Undo();
946     }
947     for(a = 0; a < aNumTransactions; a++) {
948       myDoc->Redo();
949     }
950
951     undoInternal(false, false);
952     myDoc->ClearRedos();
953     myRedos.clear();
954   } else { // abort the current
955     int aNumTransactions = myTransactions.rbegin()->myOCAFNum;
956     myTransactions.pop_back();
957     if (!myNestedNum.empty())
958       (*myNestedNum.rbegin())--;
959     // roll back the needed number of transactions
960     //myDoc->AbortCommand();
961     // instead of abort, do commit and undo: to get the delta of modifications
962     if (myDoc->CommitCommand())  {
963       modifiedLabels(myDoc, aDeltaLabels);
964       myDoc->Undo();
965     }
966     for(int a = 0; a < aNumTransactions; a++) {
967       modifiedLabels(myDoc, aDeltaLabels);
968       myDoc->Undo();
969     }
970     myDoc->ClearRedos();
971   }
972   // abort for all subs, flushes will be later, in the end of root abort
973   const std::set<int> aSubs = subDocuments();
974   std::set<int>::iterator aSubIter = aSubs.begin();
975   for (; aSubIter != aSubs.end(); aSubIter++)
976     subDoc(*aSubIter)->abortOperation();
977   // references may be changed because they are set in attributes on the fly
978   myObjs->synchronizeFeatures(aDeltaLabels, true, false, false, isRoot());
979 }
980
981 bool Model_Document::isOperation() const
982 {
983   // operation is opened for all documents: no need to check subs
984   return myDoc->HasOpenCommand() == Standard_True ;
985 }
986
987 bool Model_Document::isModified()
988 {
989   // is modified if at least one operation was committed and not undone
990   return myTransactions.size() != myTransactionSave || isOperation();
991 }
992
993 bool Model_Document::canUndo()
994 {
995   // issue 406 : if transaction is opened, but nothing to undo behind, can not undo
996   int aCurrentNum = isOperation() ? 1 : 0;
997   if (myDoc->GetAvailableUndos() > 0 &&
998       // there is something to undo in nested
999       (myNestedNum.empty() || *myNestedNum.rbegin() - aCurrentNum > 0) &&
1000       myTransactions.size() - aCurrentNum > 0 /* for omitting the first useless transaction */)
1001     return true;
1002   // check other subs contains operation that can be undone
1003   const std::set<int> aSubs = subDocuments();
1004   std::set<int>::iterator aSubIter = aSubs.begin();
1005   for (; aSubIter != aSubs.end(); aSubIter++) {
1006     std::shared_ptr<Model_Document> aSub = subDoc(*aSubIter);
1007     if (aSub->myObjs) {// if it was not closed before
1008       if (aSub->canUndo())
1009         return true;
1010     }
1011   }
1012
1013   return false;
1014 }
1015
1016 void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchronize)
1017 {
1018   if (myTransactions.empty())
1019     return;
1020   int aNumTransactions = myTransactions.rbegin()->myOCAFNum;
1021   myRedos.push_back(*myTransactions.rbegin());
1022   myTransactions.pop_back();
1023   if (!myNestedNum.empty())
1024     (*myNestedNum.rbegin())--;
1025   // roll back the needed number of transactions
1026   TDF_LabelList aDeltaLabels;
1027   for(int a = 0; a < aNumTransactions; a++) {
1028     if (theSynchronize)
1029       modifiedLabels(myDoc, aDeltaLabels);
1030     myDoc->Undo();
1031   }
1032
1033   std::set<int> aSubs;
1034   if (theWithSubs) {
1035     // undo for all subs
1036     aSubs = subDocuments();
1037     std::set<int>::iterator aSubIter = aSubs.begin();
1038     for (; aSubIter != aSubs.end(); aSubIter++) {
1039       if (!subDoc(*aSubIter)->myObjs)
1040         continue;
1041       subDoc(*aSubIter)->undoInternal(theWithSubs, theSynchronize);
1042     }
1043   }
1044   // after undo of all sub-documents to avoid updates on not-modified data (issue 370)
1045   if (theSynchronize) {
1046     myObjs->synchronizeFeatures(aDeltaLabels, true, false, false, isRoot());
1047     // update the current features status
1048     setCurrentFeature(currentFeature(false), false);
1049
1050     if (theWithSubs) {
1051       // undo for all subs
1052       const std::set<int> aNewSubs = subDocuments();
1053       std::set<int>::iterator aNewSubIter = aNewSubs.begin();
1054       for (; aNewSubIter != aNewSubs.end(); aNewSubIter++) {
1055         // synchronize only newly appeared documents
1056         if (!subDoc(*aNewSubIter)->myObjs || aSubs.find(*aNewSubIter) != aSubs.end())
1057           continue;
1058         TDF_LabelList anEmptyDeltas;
1059         subDoc(*aNewSubIter)->myObjs->synchronizeFeatures(anEmptyDeltas, true, false, true, true);
1060       }
1061     }
1062   }
1063 }
1064
1065 void Model_Document::undo()
1066 {
1067   undoInternal(true, true);
1068 }
1069
1070 bool Model_Document::canRedo()
1071 {
1072   if (!myRedos.empty())
1073     return true;
1074   // check other subs contains operation that can be redone
1075   const std::set<int> aSubs = subDocuments();
1076   std::set<int>::iterator aSubIter = aSubs.begin();
1077   for (; aSubIter != aSubs.end(); aSubIter++) {
1078     if (!subDoc(*aSubIter)->myObjs)
1079       continue;
1080     if (subDoc(*aSubIter)->canRedo())
1081       return true;
1082   }
1083   return false;
1084 }
1085
1086 void Model_Document::redo()
1087 {
1088   if (!myNestedNum.empty())
1089     (*myNestedNum.rbegin())++;
1090   int aNumRedos = myRedos.rbegin()->myOCAFNum;
1091   myTransactions.push_back(*myRedos.rbegin());
1092   myRedos.pop_back();
1093   TDF_LabelList aDeltaLabels;
1094   for(int a = 0; a < aNumRedos; a++) {
1095     modifiedLabels(myDoc, aDeltaLabels, true);
1096     myDoc->Redo();
1097   }
1098
1099   // redo for all subs
1100   const std::set<int> aSubs = subDocuments();
1101   std::set<int>::iterator aSubIter = aSubs.begin();
1102   for (; aSubIter != aSubs.end(); aSubIter++)
1103     subDoc(*aSubIter)->redo();
1104
1105   // after redo of all sub-documents to avoid updates on not-modified data (issue 370)
1106   myObjs->synchronizeFeatures(aDeltaLabels, true, false, false, isRoot());
1107   // update the current features status
1108   setCurrentFeature(currentFeature(false), false);
1109 }
1110 // this is used for creation of undo/redo1-list by GUI
1111 // LCOV_EXCL_START
1112 std::list<std::string> Model_Document::undoList() const
1113 {
1114   std::list<std::string> aResult;
1115   // the number of skipped current operations (on undo they will be aborted)
1116   int aSkipCurrent = isOperation() ? 1 : 0;
1117   std::list<Transaction>::const_reverse_iterator aTrIter = myTransactions.crbegin();
1118   int aNumUndo = int(myTransactions.size());
1119   if (!myNestedNum.empty())
1120     aNumUndo = *myNestedNum.rbegin();
1121   for( ; aNumUndo > 0; aTrIter++, aNumUndo--) {
1122     if (aSkipCurrent == 0) aResult.push_back(aTrIter->myId);
1123     else aSkipCurrent--;
1124   }
1125   return aResult;
1126 }
1127
1128 std::list<std::string> Model_Document::redoList() const
1129 {
1130   std::list<std::string> aResult;
1131   std::list<Transaction>::const_reverse_iterator aTrIter = myRedos.crbegin();
1132   for( ; aTrIter != myRedos.crend(); aTrIter++) {
1133     aResult.push_back(aTrIter->myId);
1134   }
1135   return aResult;
1136 }
1137 // LCOV_EXCL_STOP
1138
1139 void Model_Document::operationId(const std::string& theId)
1140 {
1141   myTransactions.rbegin()->myId = theId;
1142 }
1143
1144 FeaturePtr Model_Document::addFeature(std::string theID, const bool theMakeCurrent)
1145 {
1146   std::shared_ptr<Model_Session> aSession =
1147     std::dynamic_pointer_cast<Model_Session>(ModelAPI_Session::get());
1148   FeaturePtr aFeature = aSession->createFeature(theID, this);
1149   if (!aFeature)
1150     return aFeature;
1151   aFeature->init();
1152   Model_Document* aDocToAdd;
1153   if (!aFeature->documentToAdd().empty()) { // use the customized document to add
1154     if (aFeature->documentToAdd() != kind()) { // the root document by default
1155       aDocToAdd = std::dynamic_pointer_cast<Model_Document>(aSession->moduleDocument()).get();
1156     } else {
1157       aDocToAdd = this;
1158     }
1159   } else { // if customized is not presented, add to "this" document
1160     aDocToAdd = this;
1161   }
1162   if (aFeature) {
1163     // searching for feature after which must be added the next feature: this is the current feature
1164     // but also all sub-features of this feature
1165     FeaturePtr aCurrent = aDocToAdd->currentFeature(false);
1166     bool isModified = true;
1167     for(CompositeFeaturePtr aComp = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aCurrent);
1168         aComp.get() && isModified;
1169         aComp = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aCurrent)) {
1170       isModified =  false;
1171       int aSubs = aComp->numberOfSubs(false);
1172       for(int a = 0; a < aSubs; a++) {
1173         FeaturePtr aSub = aComp->subFeature(a, false);
1174         if (aSub && myObjs->isLater(aSub, aCurrent)) {
1175           isModified =  true;
1176           aCurrent = aSub;
1177         }
1178       }
1179     }
1180     // #2861,3029: if the parameter is added, add it after parameters existing in the list
1181     if (aCurrent.get() &&
1182       (aFeature->getKind() == "Parameter" || aFeature->getKind() == "ParametersMgr")) {
1183       int anIndex = kUNDEFINED_FEATURE_INDEX;
1184       for(FeaturePtr aNextFeat = myObjs->nextFeature(aCurrent, anIndex);
1185         aNextFeat.get() && aNextFeat->getKind() == "Parameter";
1186         aNextFeat = myObjs->nextFeature(aCurrent, anIndex))
1187         aCurrent = aNextFeat;
1188     }
1189     aDocToAdd->myObjs->addFeature(aFeature, aCurrent);
1190     if (!aFeature->isAction()) {  // do not add action to the data model
1191       if (theMakeCurrent)  // after all this feature stays in the document, so make it current
1192         aDocToAdd->setCurrentFeature(aFeature, false);
1193     } else { // feature must be executed
1194        // no creation event => updater not working, problem with remove part
1195       aFeature->execute();
1196     }
1197   }
1198   return aFeature;
1199 }
1200
1201 void Model_Document::refsToFeature(FeaturePtr theFeature,
1202   std::set<std::shared_ptr<ModelAPI_Feature> >& theRefs, const bool isSendError)
1203 {
1204   myObjs->refsToFeature(theFeature, theRefs, isSendError);
1205 }
1206
1207 void Model_Document::removeFeature(FeaturePtr theFeature)
1208 {
1209   myObjs->removeFeature(theFeature);
1210   // fix for #2723: send signal that part is updated
1211   if (!isRoot() && isOperation()) {
1212     std::shared_ptr<Model_Document> aRoot =
1213       std::dynamic_pointer_cast<Model_Document>(ModelAPI_Session::get()->moduleDocument());
1214     std::list<ResultPtr> allParts;
1215     aRoot->objects()->allResults(ModelAPI_ResultPart::group(), allParts);
1216     std::list<ResultPtr>::iterator aParts = allParts.begin();
1217     for(; aParts != allParts.end(); aParts++) {
1218       ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aParts);
1219       if (aPart->partDoc().get() == this) {
1220         static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
1221         ModelAPI_EventCreator::get()->sendUpdated(aRoot->feature(aPart), anEvent);
1222         break;
1223       }
1224     }
1225   }
1226 }
1227
1228 // recursive function to check if theSub is a child of theMain composite feature
1229 // through all the hierarchy of parents
1230 static bool isSub(const CompositeFeaturePtr theMain, const FeaturePtr theSub) {
1231   CompositeFeaturePtr aParent = ModelAPI_Tools::compositeOwner(theSub);
1232   if (!aParent.get())
1233     return false;
1234   if (aParent == theMain)
1235     return true;
1236   return isSub(theMain, aParent);
1237 }
1238
1239 void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis)
1240 {
1241   bool aCurrentUp = theMoved == currentFeature(false);
1242   if (aCurrentUp) {
1243     setCurrentFeatureUp();
1244   }
1245   // if user adds after high-level feature with nested,
1246   // add it after all nested (otherwise the nested will be disabled)
1247   CompositeFeaturePtr aCompositeAfter =
1248     std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theAfterThis);
1249   FeaturePtr anAfterThisSub = theAfterThis;
1250   if (aCompositeAfter.get()) {
1251     FeaturePtr aSub = aCompositeAfter;
1252     int anIndex = kUNDEFINED_FEATURE_INDEX;
1253     do {
1254       FeaturePtr aNext = myObjs->nextFeature(aSub, anIndex);
1255       if (!isSub(aCompositeAfter, aNext)) {
1256         anAfterThisSub = aSub;
1257         break;
1258       }
1259       aSub = aNext;
1260     } while (aSub.get());
1261   }
1262
1263   myObjs->moveFeature(theMoved, anAfterThisSub);
1264   if (aCurrentUp) { // make the moved feature enabled or disabled due to the real status
1265     setCurrentFeature(currentFeature(false), false);
1266   } else if (theAfterThis == currentFeature(false) || anAfterThisSub == currentFeature(false)) {
1267     // must be after move to make enabled all features which are before theMoved
1268     setCurrentFeature(theMoved, true);
1269   }
1270 }
1271
1272 void Model_Document::updateHistory(const std::shared_ptr<ModelAPI_Object> theObject)
1273 {
1274   if (myObjs)
1275     myObjs->updateHistory(theObject);
1276 }
1277
1278 void Model_Document::updateHistory(const std::string theGroup)
1279 {
1280   if (myObjs)
1281     myObjs->updateHistory(theGroup);
1282 }
1283
1284 const std::set<int> Model_Document::subDocuments() const
1285 {
1286   std::set<int> aResult;
1287   std::list<ResultPtr> aPartResults;
1288   myObjs->allResults(ModelAPI_ResultPart::group(), aPartResults);
1289   std::list<ResultPtr>::iterator aPartRes = aPartResults.begin();
1290   for(; aPartRes != aPartResults.end(); aPartRes++) {
1291     ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aPartRes);
1292     if (aPart && aPart->isActivated()) {
1293       aResult.insert(aPart->original()->partDoc()->id());
1294     }
1295   }
1296   return aResult;
1297 }
1298
1299 std::shared_ptr<Model_Document> Model_Document::subDoc(int theDocID)
1300 {
1301   // just store sub-document identifier here to manage it later
1302   return std::dynamic_pointer_cast<Model_Document>(
1303     Model_Application::getApplication()->document(theDocID));
1304 }
1305
1306 ObjectPtr Model_Document::object(const std::string& theGroupID,
1307                                  const int theIndex,
1308                                  const bool theAllowFolder)
1309 {
1310   return myObjs->object(theGroupID, theIndex, theAllowFolder);
1311 }
1312
1313 std::shared_ptr<ModelAPI_Object> Model_Document::objectByName(
1314     const std::string& theGroupID, const std::string& theName)
1315 {
1316   return myObjs->objectByName(theGroupID, theName);
1317 }
1318
1319 const int Model_Document::index(std::shared_ptr<ModelAPI_Object> theObject,
1320                                 const bool theAllowFolder)
1321 {
1322   return myObjs->index(theObject, theAllowFolder);
1323 }
1324
1325 int Model_Document::size(const std::string& theGroupID, const bool theAllowFolder)
1326 {
1327   if (myObjs == 0) // may be on close
1328     return 0;
1329   return myObjs->size(theGroupID, theAllowFolder);
1330 }
1331
1332 std::shared_ptr<ModelAPI_Object> Model_Document::parent(
1333   const std::shared_ptr<ModelAPI_Object> theChild)
1334 {
1335   if(myObjs == 0) // may be on close
1336     return ObjectPtr();
1337   return myObjs->parent(theChild);
1338 }
1339
1340 std::shared_ptr<ModelAPI_Feature> Model_Document::currentFeature(const bool theVisible)
1341 {
1342   if (!myObjs) // on close document feature destruction it may call this method
1343     return std::shared_ptr<ModelAPI_Feature>();
1344   TDF_Label aRefLab = generalLabel().FindChild(TAG_CURRENT_FEATURE);
1345   Handle(TDF_Reference) aRef;
1346   if (aRefLab.FindAttribute(TDF_Reference::GetID(), aRef)) {
1347     TDF_Label aLab = aRef->Get();
1348     FeaturePtr aResult = myObjs->feature(aLab);
1349     if (theVisible) { // get nearest visible (in history) going up
1350       int anIndex = kUNDEFINED_FEATURE_INDEX;
1351       while(aResult.get() &&  !aResult->isInHistory()) {
1352         aResult = myObjs->nextFeature(aResult, anIndex, true);
1353       }
1354     }
1355     return aResult;
1356   }
1357   return std::shared_ptr<ModelAPI_Feature>(); // null feature means the higher than first
1358 }
1359
1360 void Model_Document::setCurrentFeature(
1361   std::shared_ptr<ModelAPI_Feature> theCurrent, const bool theVisible)
1362 {
1363   if (myIsSetCurrentFeature)
1364     return;
1365   myIsSetCurrentFeature = true;
1366   // blocks the flush signals to avoid each objects visualization in the viewer
1367   // they should not be shown once after all modifications are performed
1368   Events_Loop* aLoop = Events_Loop::loop();
1369   bool isActive = aLoop->activateFlushes(false);
1370
1371   TDF_Label aRefLab = generalLabel().FindChild(TAG_CURRENT_FEATURE);
1372   CompositeFeaturePtr aMain; // main feature that may nest the new current
1373   std::set<FeaturePtr> anOwners; // composites that contain theCurrent (with any level of nesting)
1374   if (theCurrent.get()) {
1375     aMain = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theCurrent);
1376     CompositeFeaturePtr anOwner = ModelAPI_Tools::compositeOwner(theCurrent);
1377     while(anOwner.get()) {
1378       if (!aMain.get()) {
1379         aMain = anOwner;
1380       }
1381       anOwners.insert(anOwner);
1382       anOwner = ModelAPI_Tools::compositeOwner(anOwner);
1383     }
1384   }
1385
1386   if (theVisible && !theCurrent.get()) {
1387     // needed to avoid disabling of PartSet initial constructions
1388     int anIndex = kUNDEFINED_FEATURE_INDEX;
1389     FeaturePtr aNext =
1390       theCurrent.get() ? myObjs->nextFeature(theCurrent, anIndex, false) : myObjs->firstFeature();
1391     for (; aNext.get(); aNext = myObjs->nextFeature(theCurrent, anIndex, false)) {
1392       if (aNext->isInHistory()) {
1393         break; // next in history is not needed
1394       } else { // next not in history is good for making current
1395         theCurrent = aNext;
1396       }
1397     }
1398   }
1399   if (theVisible) { // make RemoveResults feature be active even it is performed after the current
1400     int anIndex = kUNDEFINED_FEATURE_INDEX;
1401     FeaturePtr aNext =
1402       theCurrent.get() ? myObjs->nextFeature(theCurrent, anIndex, false) : myObjs->firstFeature();
1403     for (; aNext.get(); aNext = myObjs->nextFeature(theCurrent, anIndex, false)) {
1404       if (aNext->isInHistory()) {
1405         break; // next in history is not needed
1406       } else if (aNext->getKind() == "RemoveResults"){
1407         theCurrent = aNext;
1408       }
1409     }
1410   }
1411   if (theCurrent.get()) {
1412     std::shared_ptr<Model_Data> aData = std::static_pointer_cast<Model_Data>(theCurrent->data());
1413     if (!aData.get() || !aData->isValid()) {
1414       aLoop->activateFlushes(isActive);
1415       myIsSetCurrentFeature = false;
1416       return;
1417     }
1418     TDF_Label aFeatureLabel = aData->label().Father();
1419
1420     Handle(TDF_Reference) aRef;
1421     if (aRefLab.FindAttribute(TDF_Reference::GetID(), aRef)) {
1422       aRef->Set(aFeatureLabel);
1423     } else {
1424       aRef = TDF_Reference::Set(aRefLab, aFeatureLabel);
1425     }
1426   } else { // remove reference for the null feature
1427     aRefLab.ForgetAttribute(TDF_Reference::GetID());
1428   }
1429   // make all features after this feature disabled in reversed order
1430   // (to remove results without dependencies)
1431   static Events_ID aRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
1432
1433   bool aPassed = false; // flag that the current object is already passed in cycle
1434   FeaturePtr anIter = myObjs->lastFeature();
1435   bool aWasChanged = false;
1436   bool isCurrentParameter = theCurrent.get() && theCurrent->getKind() == "Parameter";
1437   int anIndex = kUNDEFINED_FEATURE_INDEX;
1438   for(; anIter.get(); anIter = myObjs->nextFeature(anIter, anIndex, true)) {
1439     // check this before passed become enabled: the current feature is enabled!
1440     if (anIter == theCurrent) aPassed = true;
1441
1442     bool aDisabledFlag = !aPassed;
1443     if (aMain.get()) {
1444       if (isSub(aMain, anIter)) // sub-elements of not-disabled feature are not disabled
1445         aDisabledFlag = false;
1446       else if (anOwners.find(anIter) != anOwners.end())
1447         // disable the higher-level feature if the nested is the current
1448         if (aMain->getKind() != "Import") // exception for the import XAO feature with Group (2430)
1449           aDisabledFlag = true;
1450     }
1451
1452     if (anIter->getKind() == "Parameter") {
1453       // parameters are always out of the history of features, but not parameters
1454       // due to the issue 1491 all parameters are kept enabled any time
1455       //if (!isCurrentParameter)
1456         aDisabledFlag = false;
1457     } else if (isCurrentParameter) {
1458       // if parameter is active, all other features become enabled (issue 1307)
1459       aDisabledFlag = false;
1460     }
1461
1462     if (anIter->setDisabled(aDisabledFlag)) {
1463       static Events_ID anUpdateEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED);
1464       // state of feature is changed => so inform that it must be updated if it has such state
1465       if (!aDisabledFlag &&
1466           (anIter->data()->execState() == ModelAPI_StateMustBeUpdated ||
1467            anIter->data()->execState() == ModelAPI_StateInvalidArgument))
1468         ModelAPI_EventCreator::get()->sendUpdated(anIter, anUpdateEvent);
1469       // flush is in the end of this method
1470       ModelAPI_EventCreator::get()->sendUpdated(anIter, aRedispEvent /*, false*/);
1471       aWasChanged = true;
1472     }
1473     // update for everyone concealment flag immediately: on edit feature in the middle of history
1474     if (aWasChanged) {
1475       std::list<ResultPtr> aResults;
1476       ModelAPI_Tools::allResults(anIter, aResults);
1477       std::list<ResultPtr>::const_iterator aRes = aResults.begin();
1478       for(; aRes != aResults.end(); aRes++) {
1479         if ((*aRes).get() && (*aRes)->data()->isValid() && !(*aRes)->isDisabled())
1480           std::dynamic_pointer_cast<Model_Data>((*aRes)->data())->updateConcealmentFlag();
1481       }
1482       // update the concealment status for display in isConcealed of ResultBody
1483       for(aRes = aResults.begin(); aRes != aResults.end(); aRes++) {
1484         if ((*aRes).get() && (*aRes)->data()->isValid() && !(*aRes)->isDisabled())
1485           (*aRes)->isConcealed();
1486       }
1487     }
1488   }
1489   myIsSetCurrentFeature = false;
1490   // unblock  the flush signals and up them after this
1491   aLoop->activateFlushes(isActive);
1492 }
1493
1494 void Model_Document::setCurrentFeatureUp()
1495 {
1496   // on remove just go up for minimum step: highlight external objects in sketch causes
1497   // problems if it is true: here and in "setCurrentFeature"
1498   FeaturePtr aCurrent = currentFeature(false);
1499   if (aCurrent.get()) { // if not, do nothing because null is the upper
1500     int anIndex = kUNDEFINED_FEATURE_INDEX;
1501     FeaturePtr aPrev = myObjs->nextFeature(aCurrent, anIndex, true);
1502     // make the higher level composite as current (sketch becomes disabled if line is enabled)
1503     if (aPrev.get()) {
1504       FeaturePtr aComp = ModelAPI_Tools::compositeOwner(aPrev);
1505       // without cycle (issue 1555): otherwise extrusion fuse
1506       // will be enabled and displayed when inside sketch
1507       if (aComp.get())
1508           aPrev = aComp;
1509     }
1510     // do not flush: it is called only on remove, it will be flushed in the end of transaction
1511     setCurrentFeature(aPrev, false);
1512   }
1513 }
1514
1515 TDF_Label Model_Document::generalLabel() const
1516 {
1517   return myDoc->Main().FindChild(TAG_GENERAL);
1518 }
1519
1520 std::shared_ptr<ModelAPI_ResultConstruction> Model_Document::createConstruction(
1521     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1522 {
1523   return myObjs->createConstruction(theFeatureData, theIndex);
1524 }
1525
1526 std::shared_ptr<ModelAPI_ResultBody> Model_Document::createBody(
1527     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1528 {
1529   return myObjs->createBody(theFeatureData, theIndex);
1530 }
1531
1532 std::shared_ptr<ModelAPI_ResultPart> Model_Document::createPart(
1533     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1534 {
1535   return myObjs->createPart(theFeatureData, theIndex);
1536 }
1537
1538 std::shared_ptr<ModelAPI_ResultPart> Model_Document::copyPart(
1539       const std::shared_ptr<ModelAPI_ResultPart>& theOrigin,
1540       const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1541 {
1542   return myObjs->copyPart(theOrigin, theFeatureData, theIndex);
1543 }
1544
1545 std::shared_ptr<ModelAPI_ResultGroup> Model_Document::createGroup(
1546     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1547 {
1548   return myObjs->createGroup(theFeatureData, theIndex);
1549 }
1550
1551 std::shared_ptr<ModelAPI_ResultField> Model_Document::createField(
1552     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1553 {
1554   return myObjs->createField(theFeatureData, theIndex);
1555 }
1556
1557 std::shared_ptr<ModelAPI_ResultParameter> Model_Document::createParameter(
1558       const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1559 {
1560   return myObjs->createParameter(theFeatureData, theIndex);
1561 }
1562
1563 std::shared_ptr<ModelAPI_Folder> Model_Document::addFolder(
1564     std::shared_ptr<ModelAPI_Feature> theAddBefore)
1565 {
1566   return myObjs->createFolder(theAddBefore);
1567 }
1568
1569 void Model_Document::removeFolder(std::shared_ptr<ModelAPI_Folder> theFolder)
1570 {
1571   if (theFolder)
1572     myObjs->removeFolder(theFolder);
1573 }
1574
1575 std::shared_ptr<ModelAPI_Folder> Model_Document::findFolderAbove(
1576       const std::list<std::shared_ptr<ModelAPI_Feature> >& theFeatures)
1577 {
1578   return myObjs->findFolder(theFeatures, false);
1579 }
1580
1581 std::shared_ptr<ModelAPI_Folder> Model_Document::findFolderBelow(
1582       const std::list<std::shared_ptr<ModelAPI_Feature> >& theFeatures)
1583 {
1584   return myObjs->findFolder(theFeatures, true);
1585 }
1586
1587 std::shared_ptr<ModelAPI_Folder> Model_Document::findContainingFolder(
1588       const std::shared_ptr<ModelAPI_Feature>& theFeature,
1589       int& theIndexInFolder)
1590 {
1591   return myObjs->findContainingFolder(theFeature, theIndexInFolder);
1592 }
1593
1594 bool Model_Document::moveToFolder(
1595       const std::list<std::shared_ptr<ModelAPI_Feature> >& theFeatures,
1596       const std::shared_ptr<ModelAPI_Folder>& theFolder)
1597 {
1598   return myObjs->moveToFolder(theFeatures, theFolder);
1599 }
1600
1601 bool Model_Document::removeFromFolder(
1602       const std::list<std::shared_ptr<ModelAPI_Feature> >& theFeatures,
1603       const bool theBefore)
1604 {
1605   return myObjs->removeFromFolder(theFeatures, theBefore);
1606 }
1607
1608 std::shared_ptr<ModelAPI_Feature> Model_Document::feature(
1609     const std::shared_ptr<ModelAPI_Result>& theResult)
1610 {
1611   if (myObjs == 0) // may be on close
1612     return std::shared_ptr<ModelAPI_Feature>();
1613   return myObjs->feature(theResult);
1614 }
1615
1616 FeaturePtr Model_Document::featureByLab(const TDF_Label& theLab) {
1617   TDF_Label aCurrentLab = theLab;
1618   while(aCurrentLab.Depth() > 3)
1619     aCurrentLab = aCurrentLab.Father();
1620   return myObjs->feature(aCurrentLab);
1621 }
1622
1623 ResultPtr Model_Document::resultByLab(const TDF_Label& theLab)
1624 {
1625   TDF_Label aCurrentLab = theLab;
1626   while(aCurrentLab.Depth() > 3) {
1627     ObjectPtr aResultObj = myObjs->object(aCurrentLab);
1628     if (aResultObj.get()) {
1629       return std::dynamic_pointer_cast<ModelAPI_Result>(aResultObj); // this may be null if feature
1630     }
1631     aCurrentLab = aCurrentLab.Father();
1632   }
1633   return ResultPtr(); // not found
1634 }
1635
1636 void Model_Document::addNamingName(const TDF_Label theLabel, std::string theName)
1637 {
1638   std::map<std::string, std::list<TDF_Label> >::iterator aFind = myNamingNames.find(theName);
1639
1640   if (aFind != myNamingNames.end()) { // to avoid duplicate-labels
1641     // to keep correct order in spite of history line management
1642     std::list<TDF_Label>::iterator anAddAfterThis = aFind->second.end();
1643     FeaturePtr anAddedFeature = featureByLab(theLabel);
1644     std::list<TDF_Label>::iterator aLabIter = aFind->second.begin();
1645     while(aLabIter != aFind->second.end()) {
1646       if (theLabel.IsEqual(*aLabIter)) {
1647         std::list<TDF_Label>::iterator aTmpIter = aLabIter;
1648         aLabIter++;
1649         aFind->second.erase(aTmpIter);
1650       } else {
1651         FeaturePtr aCurFeature = featureByLab(*aLabIter);
1652         if (aCurFeature.get() && anAddedFeature.get() &&
1653             myObjs->isLater(anAddedFeature, aCurFeature))
1654           anAddAfterThis = aLabIter;
1655
1656         aLabIter++;
1657       }
1658     }
1659     if (anAddAfterThis != aFind->second.end()) {
1660       anAddAfterThis++;
1661       if (anAddAfterThis != aFind->second.end()) {
1662         myNamingNames[theName].insert(anAddAfterThis, theLabel); // inserts before anAddAfterThis
1663         return;
1664       }
1665     }
1666   }
1667   myNamingNames[theName].push_back(theLabel);
1668 }
1669
1670 void Model_Document::changeNamingName(const std::string theOldName,
1671                                       const std::string theNewName,
1672                                       const TDF_Label& theLabel)
1673 {
1674   std::map<std::string, std::list<TDF_Label> >::iterator aFind = myNamingNames.find(theOldName);
1675   if (aFind != myNamingNames.end()) {
1676     std::list<TDF_Label>::iterator aLabIter = aFind->second.begin();
1677     for(; aLabIter != aFind->second.end(); aLabIter++) {
1678       if (theLabel.IsEqual(*aLabIter)) { // found the label
1679         myNamingNames[theNewName].push_back(theLabel);
1680         if (aFind->second.size() == 1) { // only one element, so, just change the name
1681           myNamingNames.erase(theOldName);
1682         } else { // remove from the list
1683           aFind->second.erase(aLabIter);
1684         }
1685         // check the sketch vertex name located under renamed sketch line
1686         TDF_ChildIDIterator aChild(theLabel, TDataStd_Name::GetID());
1687         for(; aChild.More(); aChild.Next()) {
1688           Handle(TDataStd_Name) aSubName = Handle(TDataStd_Name)::DownCast(aChild.Value());
1689           std::string aName = TCollection_AsciiString(aSubName->Get()).ToCString();
1690           if (aName.find(theOldName) == 0) { // started from parent name
1691             std::string aNewSubName = theNewName + aName.substr(theOldName.size());
1692             changeNamingName(aName, aNewSubName, aSubName->Label());
1693             aSubName->Set(aNewSubName.c_str());
1694           }
1695         }
1696         return;
1697       }
1698     }
1699   }
1700 }
1701
1702 TDF_Label Model_Document::findNamingName(std::string theName, ResultPtr theContext)
1703 {
1704   std::map<std::string, std::list<TDF_Label> >::iterator aFind = myNamingNames.find(theName);
1705   if (aFind != myNamingNames.end()) {
1706       std::list<TDF_Label>::reverse_iterator aLabIter = aFind->second.rbegin();
1707       for(; aLabIter != aFind->second.rend(); aLabIter++) {
1708         if (theContext.get()) {
1709           // context is defined and not like this, so, skip
1710           if (theContext == myObjs->object(aLabIter->Father()))
1711             return *aLabIter;
1712         }
1713       }
1714       return *(aFind->second.rbegin()); // no more variants, so, return the last
1715   }
1716   // not found exact name, try to find by sub-components
1717   std::string::size_type aSlash = theName.rfind('/');
1718   if (aSlash != std::string::npos) {
1719     std::string anObjName = theName.substr(0, aSlash);
1720     aFind = myNamingNames.find(anObjName);
1721     if (aFind != myNamingNames.end()) {
1722       TCollection_ExtendedString aSubName(theName.substr(aSlash + 1).c_str());
1723       // iterate all possible same-named labels starting from the last one (the recent)
1724       std::list<TDF_Label>::reverse_iterator aLabIter = aFind->second.rbegin();
1725       for(; aLabIter != aFind->second.rend(); aLabIter++) {
1726         if (theContext.get()) {
1727           // context is defined and not like this, so, skip
1728           if (theContext != myObjs->object(aLabIter->Father()))
1729             continue;
1730         }
1731         // copy aSubName to avoid incorrect further processing after its suffix cutting
1732         TCollection_ExtendedString aSubNameCopy(aSubName);
1733         // searching sub-labels with this name
1734         TDF_ChildIDIterator aNamesIter(*aLabIter, TDataStd_Name::GetID(), Standard_True);
1735         for(; aNamesIter.More(); aNamesIter.Next()) {
1736           Handle(TDataStd_Name) aName = Handle(TDataStd_Name)::DownCast(aNamesIter.Value());
1737           if (aName->Get() == aSubNameCopy)
1738             return aName->Label();
1739         }
1740         // If not found child label with the exact sub-name, then try to find compound with
1741         // such sub-name without suffix.
1742         Standard_Integer aSuffixPos = aSubNameCopy.SearchFromEnd('_');
1743         if (aSuffixPos != -1 && aSuffixPos != aSubNameCopy.Length()) {
1744           TCollection_ExtendedString anIndexStr = aSubNameCopy.Split(aSuffixPos);
1745           aSubNameCopy.Remove(aSuffixPos);
1746           aNamesIter.Initialize(*aLabIter, TDataStd_Name::GetID(), Standard_True);
1747           for(; aNamesIter.More(); aNamesIter.Next()) {
1748             Handle(TDataStd_Name) aName = Handle(TDataStd_Name)::DownCast(aNamesIter.Value());
1749             if (aName->Get() == aSubNameCopy) {
1750               return aName->Label();
1751             }
1752           }
1753           // check also "this" label
1754           Handle(TDataStd_Name) aName;
1755           if (aLabIter->FindAttribute(TDataStd_Name::GetID(), aName)) {
1756             if (aName->Get() == aSubNameCopy) {
1757               return aName->Label();
1758             }
1759           }
1760         }
1761       }
1762       // verify context's name is same as sub-component's and use context's label
1763       if (aSubName.IsEqual(anObjName.c_str()))
1764         return *(aFind->second.rbegin());
1765     }
1766   }
1767   return TDF_Label(); // not found
1768 }
1769
1770 bool Model_Document::isLaterByDep(FeaturePtr theThis, FeaturePtr theOther) {
1771   // check dependencies first: if theOther depends on theThis, theThis is not later
1772   std::list<std::pair<std::string, std::list<std::shared_ptr<ModelAPI_Object> > > > aRefs;
1773   theOther->data()->referencesToObjects(aRefs);
1774   std::list<std::pair<std::string, std::list<std::shared_ptr<ModelAPI_Object> > > >::iterator
1775     aRefIt = aRefs.begin();
1776   for(; aRefIt != aRefs.end(); aRefIt++) {
1777     std::list<ObjectPtr>::iterator aRefObjIt = aRefIt->second.begin();
1778     for(; aRefObjIt != aRefIt->second.end(); aRefObjIt++) {
1779       ObjectPtr aRefObj = *aRefObjIt;
1780       if (aRefObj.get()) {
1781         FeaturePtr aRefFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(aRefObj);
1782         if (!aRefFeat.get()) { // take feature of the result
1783           aRefFeat = feature(std::dynamic_pointer_cast<ModelAPI_Result>(aRefObj));
1784         }
1785         if (aRefFeat.get()) {
1786           if (aRefFeat == theThis)
1787             return false; // other references to this, so other later than this
1788           //if (std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aRefFeat)) {
1789           //  if (!isLaterByDep(theThis, aRefFeat)) // nested composites: recursion
1790           //    return false;
1791           //}
1792         }
1793       }
1794     }
1795   }
1796   FeaturePtr aThisOwner = ModelAPI_Tools::compositeOwner(theThis);
1797   if (aThisOwner.get()) {
1798     if (aThisOwner == theOther)
1799       return true; // composite owner is later that its sub
1800     if (!isLaterByDep(aThisOwner, theOther))
1801       return false;
1802   }
1803   return myObjs->isLater(theThis, theOther);
1804 }
1805
1806 int Model_Document::numberOfNameInHistory(
1807   const ObjectPtr& theNameObject, const TDF_Label& theStartFrom)
1808 {
1809   std::map<std::string, std::list<TDF_Label> >::iterator aFind =
1810     myNamingNames.find(theNameObject->data()->name());
1811   if (aFind == myNamingNames.end() || aFind->second.size() < 2) {
1812     return 1; // no need to specify the name by additional identifiers
1813   }
1814   // get the feature of the object for relative compare
1815   FeaturePtr aStart = myObjs->feature(theStartFrom);
1816   if (!aStart.get()) // strange, but can not find feature by the label
1817     return 1;
1818   // feature that contain result with this name
1819   FeaturePtr aNameFeature;
1820   ResultPtr aNameResult = std::dynamic_pointer_cast<ModelAPI_Result>(theNameObject);
1821   if (aNameResult)
1822     aNameFeature = myObjs->feature(aNameResult);
1823   else
1824     aNameFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theNameObject);
1825   // iterate all labels with this name to find the nearest just before or equal relative
1826   std::list<TDF_Label>::reverse_iterator aLabIter = aFind->second.rbegin();
1827   for(; aLabIter != aFind->second.rend(); aLabIter++) {
1828     FeaturePtr aLabFeat = featureByLab(*aLabIter);
1829     if (!aLabFeat.get())
1830       continue;
1831     if (isLaterByDep(aStart, aLabFeat)) // skip also start: its result don't used
1832       break;
1833   }
1834   int aResIndex = 1;
1835   for(; aLabIter != aFind->second.rend(); aLabIter++) {
1836     FeaturePtr aLabFeat = featureByLab(*aLabIter);
1837     if (!aLabFeat.get())
1838       continue;
1839     if (aLabFeat == aNameFeature || isLaterByDep(aNameFeature, aLabFeat))
1840       return aResIndex;
1841     aResIndex++;
1842   }
1843   return aResIndex; // strange
1844 }
1845
1846 ResultPtr Model_Document::findByName(
1847   std::string& theName, std::string& theSubShapeName, bool& theUniqueContext)
1848 {
1849   int aNumInHistory = 0;
1850   std::string aName = theName;
1851   ResultPtr aRes = myObjs->findByName(aName);
1852   theUniqueContext = !(aRes.get() && myNamingNames.find(aName) != myNamingNames.end());
1853   while(!aRes.get() && aName[0] == '_') { // this may be theContext with the history index
1854     aNumInHistory++;
1855     aName = aName.substr(1);
1856     aRes = myObjs->findByName(aName);
1857   }
1858   if (aNumInHistory) {
1859     std::map<std::string, std::list<TDF_Label> >::iterator aFind = myNamingNames.find(aName);
1860     if (aFind != myNamingNames.end() && aFind->second.size() > aNumInHistory) {
1861       std::list<TDF_Label>::reverse_iterator aLibIt = aFind->second.rbegin();
1862       for(; aNumInHistory != 0; aNumInHistory--)
1863         aLibIt++;
1864       const TDF_Label& aResultLab = *aLibIt;
1865       aRes = std::dynamic_pointer_cast<ModelAPI_Result>(myObjs->object(aResultLab.Father()));
1866       if (aRes) { // modify the incoming names
1867         if (!theSubShapeName.empty())
1868           theSubShapeName = theSubShapeName.substr(theName.size() - aName.size());
1869         theName = aName;
1870       }
1871     }
1872   }
1873   return aRes;
1874 }
1875
1876 std::list<std::shared_ptr<ModelAPI_Feature> > Model_Document::allFeatures()
1877 {
1878   return myObjs->allFeatures();
1879 }
1880
1881 std::list<std::shared_ptr<ModelAPI_Object> > Model_Document::allObjects()
1882 {
1883   return myObjs->allObjects();
1884 }
1885
1886 void Model_Document::setActive(const bool theFlag)
1887 {
1888   if (theFlag != myIsActive) {
1889     myIsActive = theFlag;
1890     // redisplay all the objects of this part
1891     static Events_Loop* aLoop = Events_Loop::loop();
1892     static Events_ID aRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
1893
1894     for(int a = size(ModelAPI_Feature::group()) - 1; a >= 0; a--) {
1895       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(
1896         object(ModelAPI_Feature::group(), a));
1897       if (aFeature.get() && aFeature->data()->isValid()) {
1898         std::list<ResultPtr> aResults;
1899         ModelAPI_Tools::allResults(aFeature, aResults);
1900         for (std::list<ResultPtr>::iterator aRes = aResults.begin();
1901                                                 aRes != aResults.end(); aRes++) {
1902           ModelAPI_EventCreator::get()->sendUpdated(*aRes, aRedispEvent);
1903         }
1904       }
1905     }
1906   }
1907 }
1908
1909 bool Model_Document::isActive() const
1910 {
1911   return myIsActive;
1912 }
1913
1914 int Model_Document::transactionID()
1915 {
1916   Handle(TDataStd_Integer) anIndex;
1917   if (!generalLabel().FindChild(TAG_CURRENT_TRANSACTION).
1918       FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
1919     anIndex = TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), 1);
1920   }
1921   return anIndex->Get();
1922 }
1923
1924 void Model_Document::incrementTransactionID()
1925 {
1926   int aNewVal = transactionID() + 1;
1927   TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), aNewVal);
1928 }
1929
1930 TDF_Label Model_Document::extConstructionsLabel() const
1931 {
1932   return myDoc->Main().FindChild(TAG_EXTERNAL_CONSTRUCTIONS);
1933 }
1934
1935 bool Model_Document::isOpened()
1936 {
1937   return myObjs && !myDoc.IsNull();
1938 }
1939
1940 int Model_Document::numInternalFeatures()
1941 {
1942   return myObjs->numInternalFeatures();
1943 }
1944
1945 std::shared_ptr<ModelAPI_Feature> Model_Document::internalFeature(const int theIndex)
1946 {
1947   return myObjs->internalFeature(theIndex);
1948 }
1949
1950 void Model_Document::synchronizeTransactions()
1951 {
1952   Model_Document* aRoot =
1953     std::dynamic_pointer_cast<Model_Document>(ModelAPI_Session::get()->moduleDocument()).get();
1954   if (aRoot == this)
1955     return; // don't need to synchronize root with root
1956
1957   std::shared_ptr<Model_Session> aSession =
1958     std::dynamic_pointer_cast<Model_Session>(Model_Session::get());
1959   while(myRedos.size() > aRoot->myRedos.size()) { // remove redo in this
1960     aSession->setCheckTransactions(false);
1961     redo();
1962     aSession->setCheckTransactions(true);
1963   }
1964   /* this case can not be reproduced in any known case for the current moment, so, just comment
1965   while(myRedos.size() < aRoot->myRedos.size()) { // add more redo in this
1966     undoInternal(false, true);
1967   }*/
1968 }
1969
1970 /// Feature that is used for selection in the Part document by the external request
1971 class Model_SelectionInPartFeature : public ModelAPI_Feature {
1972 public:
1973   /// Nothing to do in constructor
1974   Model_SelectionInPartFeature() : ModelAPI_Feature() {}
1975
1976   /// Returns the unique kind of a feature
1977   virtual const std::string& getKind() {
1978     static std::string MY_KIND("InternalSelectionInPartFeature");
1979     return MY_KIND;
1980   }
1981   /// Request for initialization of data model of the object: adding all attributes
1982   virtual void initAttributes() {
1983     data()->addAttribute("selection", ModelAPI_AttributeSelectionList::typeId());
1984   }
1985   /// Nothing to do in the execution function
1986   virtual void execute() {}
1987
1988 };
1989
1990 //! Returns the feature that is used for calculation of selection externally from the document
1991 AttributeSelectionListPtr Model_Document::selectionInPartFeature()
1992 {
1993   // return already created, otherwise create
1994   if (!mySelectionFeature.get() || !mySelectionFeature->data()->isValid()) {
1995     // create a new one
1996     mySelectionFeature = FeaturePtr(new Model_SelectionInPartFeature);
1997
1998     TDF_Label aFeatureLab = generalLabel().FindChild(TAG_SELECTION_FEATURE);
1999     std::shared_ptr<Model_Data> aData(new Model_Data);
2000     aData->setLabel(aFeatureLab.FindChild(1));
2001     aData->setObject(mySelectionFeature);
2002     mySelectionFeature->setDoc(myObjs->owner());
2003     mySelectionFeature->setData(aData);
2004     std::string aName = id() + "_Part";
2005     mySelectionFeature->data()->setName(aName);
2006     mySelectionFeature->setDoc(myObjs->owner());
2007     mySelectionFeature->initAttributes();
2008     mySelectionFeature->init(); // to make it enabled and Update correctly
2009     // this update may cause recomputation of the part after selection on it, that is not needed
2010     mySelectionFeature->data()->blockSendAttributeUpdated(true);
2011   }
2012   return mySelectionFeature->selectionList("selection");
2013 }
2014
2015 FeaturePtr Model_Document::lastFeature()
2016 {
2017   if (myObjs)
2018     return myObjs->lastFeature();
2019   return FeaturePtr();
2020 }
2021
2022 static Handle(TNaming_NamedShape) searchForOriginalShape(TopoDS_Shape theShape, TDF_Label aMain) {
2023   Handle(TNaming_NamedShape) aResult;
2024   while(!theShape.IsNull()) { // searching for the very initial shape that produces this one
2025     TopoDS_Shape aShape = theShape;
2026     theShape.Nullify();
2027     // to avoid crash of TNaming_SameShapeIterator if pure shape does not exists
2028     if (!TNaming_Tool::HasLabel(aMain, aShape))
2029       break;
2030     for(TNaming_SameShapeIterator anIter(aShape, aMain); anIter.More(); anIter.Next()) {
2031       TDF_Label aNSLab = anIter.Label();
2032       Handle(TNaming_NamedShape) aNS;
2033       if (aNSLab.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
2034         for(TNaming_Iterator aShapesIter(aNS); aShapesIter.More(); aShapesIter.Next()) {
2035           if (aShapesIter.Evolution() == TNaming_SELECTED ||
2036               aShapesIter.Evolution() == TNaming_DELETE)
2037             continue; // don't use the selection evolution
2038           if (aShapesIter.NewShape().IsSame(aShape)) { // found the original shape
2039             aResult = aNS;
2040             if (aResult->Evolution() == TNaming_MODIFY)
2041               theShape = aShapesIter.OldShape();
2042             // otherwise may me searching for another item of this shape with longer history
2043             if (!theShape.IsNull())
2044               break;
2045           }
2046         }
2047       }
2048     }
2049   }
2050   return aResult;
2051 }
2052
2053 std::shared_ptr<ModelAPI_Feature> Model_Document::producedByFeature(
2054     std::shared_ptr<ModelAPI_Result> theResult,
2055     const std::shared_ptr<GeomAPI_Shape>& theShape)
2056 {
2057   ResultBodyPtr aBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(theResult);
2058   if (!aBody.get()) {
2059     return feature(theResult); // for not-body just returns the feature that produced this result
2060   }
2061   // otherwise get the shape and search the very initial label for it
2062   TopoDS_Shape aShape = theShape->impl<TopoDS_Shape>();
2063   if (aShape.IsNull())
2064     return FeaturePtr();
2065
2066   // for compsolids and compounds all the naming is located in the main object, so, try to use
2067   // it first
2068   ResultBodyPtr aMain = ModelAPI_Tools::bodyOwner(theResult);
2069   while (aMain.get()) { // get the top-most main
2070     ResultBodyPtr aNextMain = ModelAPI_Tools::bodyOwner(aMain);
2071     if (aNextMain.get())
2072       aMain = aNextMain;
2073     else break;
2074   }
2075   if (aMain.get()) {
2076     FeaturePtr aMainRes = producedByFeature(aMain, theShape);
2077     if (aMainRes)
2078       return aMainRes;
2079   }
2080
2081   std::shared_ptr<Model_Data> aBodyData = std::dynamic_pointer_cast<Model_Data>(theResult->data());
2082   if (!aBodyData.get() || !aBodyData->isValid())
2083     return FeaturePtr();
2084
2085   TopoDS_Shape anOldShape; // old shape in the pair old shape->theShape in the named shape
2086   TopoDS_Shape aShapeContainer; // old shape of the shape that contains aShape as sub-element
2087   Handle(TNaming_NamedShape) aCandidatInThis, aCandidatContainer;
2088   TDF_Label aBodyLab = aBodyData->shapeLab();
2089   // use child and this label (the lowest priority)
2090   TDF_ChildIDIterator aNSIter(aBodyLab, TNaming_NamedShape::GetID(), Standard_True);
2091   bool aUseThis = !aNSIter.More();
2092   while(anOldShape.IsNull() && (aNSIter.More() || aUseThis)) {
2093     Handle(TNaming_NamedShape) aNS;
2094     if (aUseThis) {
2095       if (!aBodyLab.FindAttribute(TNaming_NamedShape::GetID(), aNS))
2096         break;
2097     } else {
2098       aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value());
2099     }
2100     for(TNaming_Iterator aShapesIter(aNS); aShapesIter.More(); aShapesIter.Next()) {
2101       if (aShapesIter.Evolution() == TNaming_SELECTED || aShapesIter.Evolution() == TNaming_DELETE)
2102         continue; // don't use the selection evolution
2103       if (aShapesIter.NewShape().IsSame(aShape)) { // found the original shape
2104         aCandidatInThis = aNS;
2105         if (aCandidatInThis->Evolution() == TNaming_MODIFY)
2106           anOldShape = aShapesIter.OldShape();
2107         // otherwise may me searching for another item of this shape with longer history
2108         if (!anOldShape.IsNull())
2109           break;
2110       }
2111       // check that the shape contains aShape as sub-shape to fill container
2112       if (aShapesIter.NewShape().ShapeType() < aShape.ShapeType() && aCandidatContainer.IsNull()) {
2113         TopExp_Explorer anExp(aShapesIter.NewShape(), aShape.ShapeType());
2114         for(; anExp.More(); anExp.Next()) {
2115           if (aShape.IsSame(anExp.Current())) {
2116             aCandidatContainer = aNS;
2117             aShapeContainer = aShapesIter.NewShape();
2118           }
2119         }
2120       }
2121     }
2122     // iterate to the next label or to the body label in the end
2123     if (!aUseThis)
2124       aNSIter.Next();
2125     if (!aNSIter.More()) {
2126       if (aUseThis)
2127         break;
2128       aUseThis = true;
2129     }
2130   }
2131   if (aCandidatInThis.IsNull()) {
2132     // to fix 1512: searching for original shape of this shape
2133     // if modification of it is not in this result
2134     aCandidatInThis = searchForOriginalShape(aShape, myDoc->Main());
2135     if (aCandidatInThis.IsNull()) {
2136       if (aCandidatContainer.IsNull())
2137         return FeaturePtr();
2138       // with the lower priority use the higher level shape that contains aShape
2139       aCandidatInThis = aCandidatContainer;
2140       anOldShape = aShapeContainer;
2141     } else {
2142       // to stop the searching by the following searchForOriginalShape
2143       anOldShape.Nullify();
2144     }
2145   }
2146
2147   Handle(TNaming_NamedShape) aNS = searchForOriginalShape(anOldShape, myDoc->Main());
2148   if (!aNS.IsNull())
2149     aCandidatInThis = aNS;
2150
2151   FeaturePtr aResult;
2152   TDF_Label aResultLab = aCandidatInThis->Label();
2153   while(aResultLab.Depth() > 3)
2154     aResultLab = aResultLab.Father();
2155   FeaturePtr aFeature = myObjs->feature(aResultLab);
2156   if (aFeature.get()) {
2157     if (!aResult.get() || myObjs->isLater(aResult, aFeature)) {
2158       aResult = aFeature;
2159     }
2160   }
2161   return aResult;
2162 }
2163
2164 bool Model_Document::isLater(FeaturePtr theLater, FeaturePtr theCurrent) const
2165 {
2166   return myObjs->isLater(theLater, theCurrent);
2167 }
2168
2169 // Object Browser nodes states
2170 // LCOV_EXCL_START
2171 void Model_Document::storeNodesState(const std::list<bool>& theStates)
2172 {
2173   TDF_Label aLab = generalLabel().FindChild(TAG_NODES_STATE);
2174   aLab.ForgetAllAttributes();
2175   if (!theStates.empty()) {
2176     Handle(TDataStd_BooleanArray) anArray =
2177       TDataStd_BooleanArray::Set(aLab, 0, int(theStates.size()) - 1);
2178     std::list<bool>::const_iterator aState = theStates.begin();
2179     for(int anIndex = 0; aState != theStates.end(); aState++, anIndex++) {
2180       anArray->SetValue(anIndex, *aState);
2181     }
2182   }
2183 }
2184
2185 void Model_Document::restoreNodesState(std::list<bool>& theStates) const
2186 {
2187   TDF_Label aLab = generalLabel().FindChild(TAG_NODES_STATE);
2188   Handle(TDataStd_BooleanArray) anArray;
2189   if (aLab.FindAttribute(TDataStd_BooleanArray::GetID(), anArray)) {
2190     int anUpper = anArray->Upper();
2191     for(int anIndex = 0; anIndex <= anUpper; anIndex++) {
2192       theStates.push_back(anArray->Value(anIndex) == Standard_True);
2193     }
2194   }
2195 }
2196 // LCOV_EXCL_STOP
2197
2198 void Model_Document::eraseAllFeatures()
2199 {
2200   if (myObjs)
2201     myObjs->eraseAllFeatures();
2202 }
2203
2204 std::shared_ptr<ModelAPI_Feature> Model_Document::nextFeature(
2205   std::shared_ptr<ModelAPI_Feature> theCurrent, const bool theReverse) const
2206 {
2207   if (theCurrent.get() && myObjs) {
2208     int anIndex = kUNDEFINED_FEATURE_INDEX;
2209     return myObjs->nextFeature(theCurrent, anIndex, theReverse);
2210   }
2211   return FeaturePtr(); // nothing by default
2212 }
2213
2214 void Model_Document::setExecuteFeatures(const bool theFlag)
2215 {
2216   myExecuteFeatures = theFlag;
2217   const std::set<int> aSubs = subDocuments();
2218   std::set<int>::iterator aSubIter = aSubs.begin();
2219   for (; aSubIter != aSubs.end(); aSubIter++) {
2220     if (!subDoc(*aSubIter)->myObjs)
2221       continue;
2222     subDoc(*aSubIter)->setExecuteFeatures(theFlag);
2223   }
2224 }
2225
2226 void Model_Document::appendTransactionToPrevious()
2227 {
2228   Transaction anAppended =  myTransactions.back();
2229   myTransactions.pop_back();
2230   if (!myTransactions.empty()) { // if it is empty, just forget the appended
2231     myTransactions.back().myOCAFNum += anAppended.myOCAFNum;
2232   }
2233   // propagate the same action to sub-documents
2234   const std::set<int> aSubs = subDocuments();
2235   for (std::set<int>::iterator aSubIter = aSubs.begin(); aSubIter != aSubs.end(); aSubIter++) {
2236     subDoc(*aSubIter)->appendTransactionToPrevious();
2237   }
2238 }
2239
2240 /// GUID for keeping information about the auto-recomputation state
2241 static const Standard_GUID kAutoRecomputationID("8493fb74-0674-4912-a100-1cf46c7cfab3");
2242
2243 void Model_Document::setAutoRecomutationState(const bool theState)
2244 {
2245   if (theState)
2246     generalLabel().FindChild(TAG_CURRENT_TRANSACTION).ForgetAttribute(kAutoRecomputationID);
2247   else
2248     TDataStd_UAttribute::Set(
2249       generalLabel().FindChild(TAG_CURRENT_TRANSACTION), kAutoRecomputationID);
2250 }
2251
2252 bool Model_Document::autoRecomutationState() const
2253 {
2254   return !generalLabel().FindChild(TAG_CURRENT_TRANSACTION).IsAttribute(kAutoRecomputationID);
2255 }