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