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