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