Salome HOME
Minor: too long line
[modules/shaper.git] / src / Model / Model_AttributeSelection.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_AttributeSelection.h"
22 #include "Model_Application.h"
23 #include "Model_Events.h"
24 #include "Model_Data.h"
25 #include "Model_Document.h"
26 #include "Model_SelectionNaming.h"
27 #include <Model_Objects.h>
28 #include <Model_AttributeSelectionList.h>
29 #include <Model_ResultConstruction.h>
30 #include <ModelAPI_Feature.h>
31 #include <ModelAPI_ResultBody.h>
32 #include <ModelAPI_ResultCompSolid.h>
33 #include <ModelAPI_ResultConstruction.h>
34 #include <ModelAPI_ResultPart.h>
35 #include <ModelAPI_CompositeFeature.h>
36 #include <ModelAPI_Tools.h>
37 #include <ModelAPI_Session.h>
38 #include <Events_InfoMessage.h>
39
40 #include <TNaming_Selector.hxx>
41 #include <TNaming_NamedShape.hxx>
42 #include <TNaming_Tool.hxx>
43 #include <TNaming_Builder.hxx>
44 #include <TNaming_SameShapeIterator.hxx>
45 #include <TNaming_Iterator.hxx>
46 #include <TDataStd_Integer.hxx>
47 #include <TDataStd_UAttribute.hxx>
48 #include <TDataStd_Name.hxx>
49 #include <TopTools_ListOfShape.hxx>
50 #include <TopTools_DataMapOfShapeShape.hxx>
51 #include <TopTools_MapOfShape.hxx>
52 #include <TopExp_Explorer.hxx>
53 #include <BRep_Tool.hxx>
54 #include <TopoDS.hxx>
55 #include <TopExp.hxx>
56 #include <TDF_ChildIterator.hxx>
57 #include <TDF_ChildIDIterator.hxx>
58 #include <TopoDS_Iterator.hxx>
59 #include <TDF_ChildIDIterator.hxx>
60
61 //#define DEB_NAMING 1
62 #ifdef DEB_NAMING
63 #include <BRepTools.hxx>
64 #endif
65 /// added to the index in the packed map to signalize that the vertex of edge is selected
66 /// (multiplied by the index of the edge)
67 static const int kSTART_VERTEX_DELTA = 1000000;
68 // identifier that there is simple reference: selection equals to context
69 Standard_GUID kSIMPLE_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb29");
70 // reference to Part sub-object
71 Standard_GUID kPART_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb27");
72 // selection is invalid after recomputation
73 Standard_GUID kINVALID_SELECTION("bce47fd7-80fa-4462-9d63-2f58acddd49d");
74
75 // on this label is stored:
76 // TNaming_NamedShape - selected shape
77 // TNaming_Naming - topological selection information (for the body)
78 // TDataStd_IntPackedMap - indexes of edges in composite element (for construction)
79 // TDataStd_Integer - type of the selected shape (for construction)
80 // TDF_Reference - from ReferenceAttribute, the context
81 void Model_AttributeSelection::setValue(const ResultPtr& theContext,
82   const std::shared_ptr<GeomAPI_Shape>& theSubShape, const bool theTemporarily)
83 {
84   if (theTemporarily) { // just keep the stored without DF update
85     myTmpContext = theContext;
86     myTmpSubShape = theSubShape;
87     owner()->data()->sendAttributeUpdated(this);
88     return;
89   } else {
90     myTmpContext.reset();
91     myTmpSubShape.reset();
92   }
93
94   const std::shared_ptr<GeomAPI_Shape>& anOldShape = value();
95   bool isOldContext = theContext == myRef.value();
96   bool isOldShape = isOldContext &&
97     (theSubShape == anOldShape || (theSubShape && anOldShape && theSubShape->isEqual(anOldShape)));
98   if (isOldShape) return; // shape is the same, so context is also unchanged
99   // update the referenced object if needed
100   if (!isOldContext) {
101       myRef.setValue(theContext);
102   }
103
104   // do noth use naming if selected shape is result shape itself, but not sub-shape
105   TDF_Label aSelLab = selectionLabel();
106   aSelLab.ForgetAttribute(kSIMPLE_REF_ID);
107   aSelLab.ForgetAttribute(kINVALID_SELECTION);
108
109   bool isDegeneratedEdge = false;
110   // do not use the degenerated edge as a shape, a null context and shape is used in the case
111   if (theSubShape.get() && !theSubShape->isNull() && theSubShape->isEdge()) {
112     const TopoDS_Shape& aSubShape = theSubShape->impl<TopoDS_Shape>();
113     if (aSubShape.ShapeType() == TopAbs_EDGE)
114       isDegeneratedEdge = BRep_Tool::Degenerated(TopoDS::Edge(aSubShape)) == Standard_True;
115   }
116   if (!theContext.get() || isDegeneratedEdge) {
117     // to keep the reference attribute label
118     TDF_Label aRefLab = myRef.myRef->Label();
119     aSelLab.ForgetAllAttributes(true);
120     myRef.myRef = TDF_Reference::Set(aSelLab.Father(), aSelLab.Father());
121     return;
122   }
123   if (theContext->groupName() == ModelAPI_ResultBody::group()) {
124     // do not select the whole shape for body:it is already must be in the data framework
125     // equal and null selected objects mean the same: object is equal to context,
126     if (theContext->shape().get() &&
127         (theContext->shape()->isEqual(theSubShape) || !theSubShape.get())) {
128       aSelLab.ForgetAllAttributes(true);
129       TDataStd_UAttribute::Set(aSelLab, kSIMPLE_REF_ID);
130     } else {
131       selectBody(theContext, theSubShape);
132     }
133   } else if (theContext->groupName() == ModelAPI_ResultConstruction::group()) {
134     aSelLab.ForgetAllAttributes(true); // to remove old selection data
135     std::shared_ptr<Model_ResultConstruction> aConstruction =
136       std::dynamic_pointer_cast<Model_ResultConstruction>(theContext);
137     std::shared_ptr<GeomAPI_Shape> aSubShape;
138     if (theSubShape.get() && !theContext->shape()->isEqual(theSubShape))
139       aSubShape = theSubShape; // the whole context
140     int anIndex = aConstruction->select(theSubShape, owner()->document());
141     TDataStd_Integer::Set(aSelLab, anIndex);
142   } else if (theContext->groupName() == ModelAPI_ResultPart::group()) {
143     aSelLab.ForgetAllAttributes(true);
144     TDataStd_UAttribute::Set(aSelLab, kPART_REF_ID);
145     selectPart(theContext, theSubShape);
146   }
147
148   owner()->data()->sendAttributeUpdated(this);
149 }
150
151 void Model_AttributeSelection::removeTemporaryValues()
152 {
153   if (myTmpContext.get() || myTmpSubShape.get()) {
154     myTmpContext.reset();
155     myTmpSubShape.reset();
156   }
157 }
158
159 std::shared_ptr<GeomAPI_Shape> Model_AttributeSelection::value()
160 {
161   GeomShapePtr aResult;
162   if (myTmpContext.get() || myTmpSubShape.get()) {
163     ResultConstructionPtr aResulConstruction =
164       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(myTmpContext);
165     if(aResulConstruction.get()) {
166       // it is just reference to construction.
167       return myTmpSubShape;
168     }
169     return myTmpSubShape.get() ? myTmpSubShape : myTmpContext->shape();
170   }
171
172   TDF_Label aSelLab = selectionLabel();
173   if (aSelLab.IsAttribute(kINVALID_SELECTION))
174     return aResult;
175
176   if (myRef.isInitialized()) {
177     if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape
178       ResultPtr aContext = context();
179       if (!aContext.get())
180         return aResult; // empty result
181       return aContext->shape();
182     }
183     if (aSelLab.IsAttribute(kPART_REF_ID)) {
184       ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(context());
185       if (!aPart.get() || !aPart->isActivated())
186         return std::shared_ptr<GeomAPI_Shape>(); // postponed naming needed
187       Handle(TDataStd_Integer) anIndex;
188       if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
189         if (anIndex->Get()) { // special selection attribute was created, use it
190           return aPart->selectionValue(anIndex->Get());
191         } else { // face with name is already in the data model, so try to take it by name
192           Handle(TDataStd_Name) aName;
193           if (aSelLab.FindAttribute(TDataStd_Name::GetID(), aName)) {
194             std::string aSubShapeName(TCollection_AsciiString(aName->Get()).ToCString());
195             std::size_t aPartEnd = aSubShapeName.find('/');
196             if (aPartEnd != std::string::npos && aPartEnd != aSubShapeName.rfind('/')) {
197               std::string aNameInPart = aSubShapeName.substr(aPartEnd + 1);
198               int anIndex;
199               std::string aType; // to reuse already existing selection the type is not needed
200               return aPart->shapeInPart(aNameInPart, aType, anIndex);
201             }
202           }
203         }
204       }
205     }
206
207     Handle(TNaming_NamedShape) aSelection;
208     if (aSelLab.FindAttribute(TNaming_NamedShape::GetID(), aSelection)) {
209       TopoDS_Shape aSelShape = aSelection->Get();
210       aResult = std::shared_ptr<GeomAPI_Shape>(new GeomAPI_Shape);
211       aResult->setImpl(new TopoDS_Shape(aSelShape));
212     } else { // for simple construction element: just shape of this construction element
213       std::shared_ptr<Model_ResultConstruction> aConstr =
214         std::dynamic_pointer_cast<Model_ResultConstruction>(context());
215       if (aConstr) {
216         Handle(TDataStd_Integer) anIndex;
217         if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
218           if (anIndex->Get() == 0) // it is just reference to construction, nothing is in value
219             return aResult;
220           return aConstr->shape(anIndex->Get(), owner()->document());
221         }
222       }
223     }
224   }
225   return aResult;
226 }
227
228 bool Model_AttributeSelection::isInvalid()
229 {
230   return selectionLabel().IsAttribute(kINVALID_SELECTION) == Standard_True;
231 }
232
233 bool Model_AttributeSelection::isInitialized()
234 {
235   if (ModelAPI_AttributeSelection::isInitialized()) { // additional checks if it is initialized
236     std::shared_ptr<GeomAPI_Shape> aResult;
237     if (myRef.isInitialized()) {
238       TDF_Label aSelLab = selectionLabel();
239       if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape
240         ResultPtr aContext = context();
241         return aContext.get() != NULL;
242       }
243       Handle(TNaming_NamedShape) aSelection;
244       if (selectionLabel().FindAttribute(TNaming_NamedShape::GetID(), aSelection)) {
245         return !aSelection->Get().IsNull();
246       } else { // for simple construction element: just shape of this construction element
247         std::shared_ptr<Model_ResultConstruction> aConstr =
248           std::dynamic_pointer_cast<Model_ResultConstruction>(context());
249         if (aConstr.get()) {
250           Handle(TDataStd_Integer) anIndex;
251           if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
252             // for the whole shape it may return null, so, if index exists, returns true
253             return true;
254           }
255         }
256       }
257     }
258   }
259   return false;
260 }
261
262 Model_AttributeSelection::Model_AttributeSelection(TDF_Label& theLabel)
263   : myRef(theLabel)
264 {
265   myIsInitialized = myRef.isInitialized();
266   myParent = NULL;
267 }
268
269 void Model_AttributeSelection::setID(const std::string theID)
270 {
271   myRef.setID(theID);
272   ModelAPI_AttributeSelection::setID(theID);
273 }
274
275 ResultPtr Model_AttributeSelection::context() {
276   if (myTmpContext.get() || myTmpSubShape.get()) {
277     return myTmpContext;
278   }
279
280   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(myRef.value());
281   // for parts there could be same-data result, so take the last enabled
282   if (aResult.get() && aResult->groupName() == ModelAPI_ResultPart::group()) {
283     int aSize = aResult->document()->size(ModelAPI_ResultPart::group());
284     for(int a = aSize - 1; a >= 0; a--) {
285       ObjectPtr aPart = aResult->document()->object(ModelAPI_ResultPart::group(), a);
286       if (aPart.get() && aPart->data() == aResult->data()) {
287         ResultPtr aPartResult = std::dynamic_pointer_cast<ModelAPI_Result>(aPart);
288         FeaturePtr anOwnerFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
289         // check that this result is not this-feature result (it is forbidden t oselect itself)
290         if (anOwnerFeature.get() && anOwnerFeature->firstResult() != aPartResult) {
291           return aPartResult;
292         }
293       }
294     }
295   }
296   return aResult;
297 }
298
299
300 void Model_AttributeSelection::setObject(const std::shared_ptr<ModelAPI_Object>& theObject)
301 {
302   ModelAPI_AttributeSelection::setObject(theObject);
303   myRef.setObject(theObject);
304 }
305
306 TDF_LabelMap& Model_AttributeSelection::scope()
307 {
308   if (myScope.IsEmpty()) { // create a new scope if not yet done
309     // gets all features with named shapes that are before this feature label (before in history)
310     DocumentPtr aMyDoc = owner()->document();
311     std::list<std::shared_ptr<ModelAPI_Feature> > allFeatures = aMyDoc->allFeatures();
312     std::list<std::shared_ptr<ModelAPI_Feature> >::iterator aFIter = allFeatures.begin();
313     bool aMePassed = false;
314     CompositeFeaturePtr aComposite =
315       std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(owner());
316     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
317     CompositeFeaturePtr aCompositeOwner, aCompositeOwnerOwner;
318     if (aFeature.get()) {
319       aCompositeOwner = ModelAPI_Tools::compositeOwner(aFeature);
320       if (aCompositeOwner.get()) {
321          aCompositeOwnerOwner = ModelAPI_Tools::compositeOwner(aCompositeOwner);
322       }
323     }
324     // for group Scope is not limitet: this is always up to date objects
325     bool isGroup = aFeature.get() && aFeature->getKind() == "Group";
326     for(; aFIter != allFeatures.end(); aFIter++) {
327       if (*aFIter == owner()) {  // the left features are created later (except subs of composite)
328         aMePassed = true;
329         continue;
330       }
331       if (isGroup) aMePassed = false;
332       bool isInScope = !aMePassed;
333       if (!isInScope && aComposite.get()) {
334         // try to add sub-elements of composite if this is composite
335         if (aComposite->isSub(*aFIter))
336           isInScope = true;
337       }
338       // remove the composite-owner of this feature (sketch in extrusion-cut)
339       if (isInScope && (aCompositeOwner == *aFIter || aCompositeOwnerOwner == *aFIter))
340         isInScope = false;
341
342       if (isInScope && aFIter->get() && (*aFIter)->data()->isValid()) {
343         TDF_Label aFeatureLab = std::dynamic_pointer_cast<Model_Data>(
344           (*aFIter)->data())->label().Father();
345         TDF_ChildIDIterator aNSIter(aFeatureLab, TNaming_NamedShape::GetID(), true);
346         for(; aNSIter.More(); aNSIter.Next()) {
347           Handle(TNaming_NamedShape) aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value());
348           if (!aNS.IsNull() && aNS->Evolution() != TNaming_SELECTED) {
349             myScope.Add(aNS->Label());
350           }
351         }
352       }
353     }
354     // also add all naming labels created for external constructions
355     std::shared_ptr<Model_Document> aDoc = std::dynamic_pointer_cast<Model_Document>(aMyDoc);
356     TDF_Label anExtDocLab = aDoc->extConstructionsLabel();
357     TDF_ChildIDIterator aNSIter(anExtDocLab, TNaming_NamedShape::GetID(), true);
358     for(; aNSIter.More(); aNSIter.Next()) {
359       Handle(TNaming_NamedShape) aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value());
360       if (!aNS.IsNull() && aNS->Evolution() != TNaming_SELECTED) {
361         myScope.Add(aNS->Label());
362       }
363     }
364   }
365   return myScope;
366 }
367
368 /// Sets the invalid flag if flag is false, or removes it if "true"
369 /// Returns theFlag
370 static bool setInvalidIfFalse(TDF_Label& theLab, const bool theFlag) {
371   if (theFlag) {
372     theLab.ForgetAttribute(kINVALID_SELECTION);
373   } else {
374     TDataStd_UAttribute::Set(theLab, kINVALID_SELECTION);
375   }
376   return theFlag;
377 }
378
379 void Model_AttributeSelection::split(
380   ResultPtr theContext, TopoDS_Shape theNewShape, TopAbs_ShapeEnum theType)
381 {
382   TopTools_ListOfShape aSubs;
383   for(TopoDS_Iterator anExplorer(theNewShape); anExplorer.More(); anExplorer.Next()) {
384     if (!anExplorer.Value().IsNull() &&
385       anExplorer.Value().ShapeType() == theType) {
386         aSubs.Append(anExplorer.Value());
387     } else { // invalid case; bad result shape, so, impossible to split easily
388       aSubs.Clear();
389       break;
390     }
391   }
392   if (aSubs.Extent() > 1) { // ok to split
393     TopTools_ListIteratorOfListOfShape aSub(aSubs);
394     GeomShapePtr aSubSh(new GeomAPI_Shape);
395     aSubSh->setImpl(new TopoDS_Shape(aSub.Value()));
396     setValue(theContext, aSubSh);
397     for(aSub.Next(); aSub.More(); aSub.Next()) {
398       GeomShapePtr aSubSh(new GeomAPI_Shape);
399       aSubSh->setImpl(new TopoDS_Shape(aSub.Value()));
400       myParent->append(theContext, aSubSh);
401     }
402   }
403 }
404
405 bool Model_AttributeSelection::update()
406 {
407   TDF_Label aSelLab = selectionLabel();
408   ResultPtr aContext = context();
409   if (!aContext.get())
410     return setInvalidIfFalse(aSelLab, false);
411   if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape
412     return setInvalidIfFalse(aSelLab, aContext->shape() && !aContext->shape()->isNull());
413   }
414
415   if (aSelLab.IsAttribute(kPART_REF_ID)) { // it is reference to the part object
416     std::shared_ptr<GeomAPI_Shape> aNoSelection;
417     bool aResult = selectPart(aContext, aNoSelection, true);
418     aResult = setInvalidIfFalse(aSelLab, aResult);
419     if (aResult) {
420       owner()->data()->sendAttributeUpdated(this);
421     }
422     return aResult;
423   }
424
425   if (aContext->groupName() == ModelAPI_ResultBody::group()) {
426     // body: just a named shape, use selection mechanism from OCCT
427     TNaming_Selector aSelector(aSelLab);
428     TopoDS_Shape anOldShape;
429     if (!aSelector.NamedShape().IsNull()) {
430       anOldShape = aSelector.NamedShape()->Get();
431     }
432     bool aResult = aSelector.Solve(scope()) == Standard_True;
433     // must be before sending of updated attribute (1556)
434     aResult = setInvalidIfFalse(aSelLab, aResult);
435     TopoDS_Shape aNewShape;
436     if (!aSelector.NamedShape().IsNull()) {
437       aNewShape = aSelector.NamedShape()->Get();
438     }
439     if (anOldShape.IsNull() || aNewShape.IsNull() ||
440         !anOldShape.IsEqual(aSelector.NamedShape()->Get())) {
441       // shape type shoud not not changed: if shape becomes compound of such shapes, then split
442       if (myParent && !anOldShape.IsNull() && !aNewShape.IsNull() &&
443           anOldShape.ShapeType() != aNewShape.ShapeType() &&
444           (aNewShape.ShapeType() == TopAbs_COMPOUND || aNewShape.ShapeType() == TopAbs_COMPSOLID))
445       {
446         split(aContext, aNewShape, anOldShape.ShapeType());
447       }
448       owner()->data()->sendAttributeUpdated(this);  // send updated if shape is changed
449     }
450     return aResult;
451   }
452
453   if (aContext->groupName() == ModelAPI_ResultConstruction::group()) {
454     Handle(TDataStd_Integer) anIndex;
455     if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
456       std::shared_ptr<Model_ResultConstruction> aConstructionContext =
457         std::dynamic_pointer_cast<Model_ResultConstruction>(aContext);
458       bool aModified = true;
459       bool aValid = aConstructionContext->update(anIndex->Get(), owner()->document(), aModified);
460       setInvalidIfFalse(aSelLab, aValid);
461       if (aModified)
462         owner()->data()->sendAttributeUpdated(this);
463       return aValid;
464     }
465   }
466   return setInvalidIfFalse(aSelLab, false); // unknown case
467 }
468
469 void Model_AttributeSelection::selectBody(
470   const ResultPtr& theContext, const std::shared_ptr<GeomAPI_Shape>& theSubShape)
471 {
472   // perform the selection
473   TNaming_Selector aSel(selectionLabel());
474   TopoDS_Shape aContext;
475
476   ResultBodyPtr aBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(theContext);//myRef.value()
477   if (aBody) {
478     aContext = aBody->shape()->impl<TopoDS_Shape>();
479   } else {
480     ResultPtr aResult =
481       std::dynamic_pointer_cast<ModelAPI_Result>(myRef.value());
482     if (aResult) {
483       aContext = aResult->shape()->impl<TopoDS_Shape>();
484     } else {
485       Events_InfoMessage("Model_AttributeSelection", "A result with shape is expected").send();
486       return;
487     }
488   }
489
490   // with "recover" feature the selected context may be not up to date (issue 1710)
491   Handle(TNaming_NamedShape) aResult;
492   TDF_Label aSelLab = selectionLabel();
493   TopoDS_Shape aNewContext = aContext;
494   bool isUpdated = true;
495   while(!aNewContext.IsNull() && isUpdated) {
496     // searching for the very last shape that was produced from this one
497     isUpdated = false;
498     if (!TNaming_Tool::HasLabel(aSelLab, aNewContext))
499       // to avoid crash of TNaming_SameShapeIterator if pure shape does not exists
500       break;
501     for(TNaming_SameShapeIterator anIter(aNewContext, aSelLab); anIter.More(); anIter.Next()) {
502       TDF_Label aNSLab = anIter.Label();
503       if (!scope().Contains(aNSLab))
504         continue;
505       Handle(TNaming_NamedShape) aNS;
506       if (aNSLab.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
507         for(TNaming_Iterator aShapesIter(aNS); aShapesIter.More(); aShapesIter.Next()) {
508           if (aShapesIter.Evolution() == TNaming_SELECTED)
509             continue; // don't use the selection evolution
510           if (!aShapesIter.OldShape().IsNull() && aShapesIter.OldShape().IsSame(aNewContext)) {
511              // found the original shape
512             aNewContext = aShapesIter.NewShape(); // go to the newer shape
513             isUpdated = true;
514             break;
515           }
516         }
517       }
518     }
519   }
520   if (aNewContext.IsNull()) { // a context is already deleted
521     setInvalidIfFalse(aSelLab, false);
522     Events_InfoMessage("Model_AttributeSelection", "Failed to select shape already deleted").send();
523     return;
524   }
525
526   TopoDS_Shape aNewSub = theSubShape ? theSubShape->impl<TopoDS_Shape>() : aContext;
527   if (!aNewSub.IsEqual(aContext)) { // searching for subshape in the new context
528     bool isFound = false;
529     TopExp_Explorer anExp(aNewContext, aNewSub.ShapeType());
530     for(; anExp.More(); anExp.Next()) {
531       if (anExp.Current().IsEqual(aNewSub)) {
532         isFound = true;
533         break;
534       }
535     }
536     if (!isFound) { // sub-shape is not found in the up-to-date instance of the context shape
537       // if context is sub-result of compound/compsolid, selection of sub-shape better propagate to
538       // the main result (which is may be modified), case is in 1799
539       ResultCompSolidPtr aMain = ModelAPI_Tools::compSolidOwner(theContext);
540       if (aMain.get()) {
541         selectBody(aMain, theSubShape);
542         return;
543       }
544       setInvalidIfFalse(aSelLab, false);
545       Events_InfoMessage("Model_AttributeSelection",
546         "Failed to select sub-shape already modified").send();
547       return;
548     }
549   }
550
551   /// fix for issue 411: result modified shapes must not participate in this selection mechanism
552   if (!aContext.IsNull()) {
553     FeaturePtr aFeatureOwner = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
554     bool aEraseResults = false;
555     if (aFeatureOwner.get()) {
556       aEraseResults = !aFeatureOwner->results().empty();
557       if (aEraseResults) // erase results without flash deleted and redisplay: do it after Select
558         aFeatureOwner->removeResults(0, false, false);
559     }
560     aSel.Select(aNewSub, aNewContext);
561
562     if (aEraseResults) { // flash after Select : in Groups it makes selection with shift working
563       static Events_Loop* aLoop = Events_Loop::loop();
564       static const Events_ID kDeletedEvent = aLoop->eventByName(EVENT_OBJECT_DELETED);
565       aLoop->flush(kDeletedEvent);
566     }
567   }
568 }
569
570 bool Model_AttributeSelection::selectPart(
571   const ResultPtr& theContext, const std::shared_ptr<GeomAPI_Shape>& theSubShape,
572   const bool theUpdate)
573 {
574   ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(theContext);
575   if (!aPart.get() || !aPart->isActivated())
576     return true; // postponed naming
577   if (theUpdate) {
578     Handle(TDataStd_Integer) anIndex;
579     if (selectionLabel().FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
580       // by internal selection
581       if (anIndex->Get() > 0) {
582         // update the selection by index
583         return aPart->updateInPart(anIndex->Get());
584       } else {
585         return true; // nothing to do, referencing just by name
586       }
587     }
588     return true; // nothing to do, referencing just by name
589   }
590   // store the shape (in case part is not loaded it should be useful
591   TopoDS_Shape aShape;
592   std::string aName = theContext->data()->name();
593   if (!theSubShape.get() || theSubShape->isNull()) {// the whole part shape is selected
594     aShape = theContext->shape()->impl<TopoDS_Shape>();
595   } else {
596     aShape = theSubShape->impl<TopoDS_Shape>();
597     int anIndex;
598     aName += "/" + aPart->nameInPart(theSubShape, anIndex);
599     TDataStd_Integer::Set(selectionLabel(), anIndex);
600   }
601   TNaming_Builder aBuilder(selectionLabel());
602   aBuilder.Select(aShape, aShape);
603   // identify by name in the part
604   TDataStd_Name::Set(selectionLabel(), aName.c_str());
605   return !aName.empty();
606 }
607
608 TDF_Label Model_AttributeSelection::selectionLabel()
609 {
610   return myRef.myRef->Label().FindChild(1);
611 }
612
613 std::string Model_AttributeSelection::namingName(const std::string& theDefaultName)
614 {
615   std::string aName("");
616   if(!this->isInitialized())
617     return !theDefaultName.empty() ? theDefaultName : aName;
618
619   std::shared_ptr<GeomAPI_Shape> aSubSh = value();
620   ResultPtr aCont = context();
621
622   if (!aCont.get()) // in case of selection of removed result
623     return "";
624
625   Model_SelectionNaming aSelNaming(selectionLabel());
626   return aSelNaming.namingName(
627     aCont, aSubSh, theDefaultName, owner()->document() != aCont->document());
628 }
629
630 // type ::= COMP | COMS | SOLD | SHEL | FACE | WIRE | EDGE | VERT
631 void Model_AttributeSelection::selectSubShape(
632   const std::string& theType, const std::string& theSubShapeName)
633 {
634   if(theSubShapeName.empty() || theType.empty()) return;
635
636   // check this is Part-name: 2 delimiters in the name
637   std::size_t aPartEnd = theSubShapeName.find('/');
638   if (aPartEnd != std::string::npos && aPartEnd != theSubShapeName.rfind('/')) {
639     std::string aPartName = theSubShapeName.substr(0, aPartEnd);
640     ObjectPtr aFound = owner()->document()->objectByName(ModelAPI_ResultPart::group(), aPartName);
641     if (aFound.get()) { // found such part, so asking it for the name
642       ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aFound);
643       std::string aNameInPart = theSubShapeName.substr(aPartEnd + 1);
644       int anIndex;
645       std::shared_ptr<GeomAPI_Shape> aSelected = aPart->shapeInPart(aNameInPart, theType, anIndex);
646       if (aSelected.get()) {
647         setValue(aPart, aSelected);
648         TDataStd_Integer::Set(selectionLabel(), anIndex);
649         return;
650       }
651     }
652   }
653
654   Model_SelectionNaming aSelNaming(selectionLabel());
655   std::shared_ptr<Model_Document> aDoc =
656     std::dynamic_pointer_cast<Model_Document>(owner()->document());
657   std::shared_ptr<GeomAPI_Shape> aShapeToBeSelected;
658   ResultPtr aCont;
659   if (aSelNaming.selectSubShape(theType, theSubShapeName, aDoc, aShapeToBeSelected, aCont)) {
660     // try to find the last context to find the up to date shape
661     if (aCont->shape().get() && !aCont->shape()->isNull() &&
662       aCont->groupName() == ModelAPI_ResultBody::group() && aDoc == owner()->document()) {
663       const TopoDS_Shape aConShape = aCont->shape()->impl<TopoDS_Shape>();
664       if (!aConShape.IsNull()) {
665         Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(aConShape, selectionLabel());
666         if (!aNS.IsNull()) {
667           aNS = TNaming_Tool::CurrentNamedShape(aNS);
668           if (!aNS.IsNull()) {
669             TDF_Label aLab = aNS->Label();
670             while(aLab.Depth() != 7 && aLab.Depth() > 5)
671               aLab = aLab.Father();
672             ObjectPtr anObj = aDoc->objects()->object(aLab);
673             if (anObj.get()) {
674               ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(anObj);
675               if (aRes)
676                 aCont = aRes;
677             }
678           }
679         }
680       }
681     }
682     setValue(aCont, aShapeToBeSelected);
683     return;
684   }
685
686   TDF_Label aSelLab = selectionLabel();
687   setInvalidIfFalse(aSelLab, false);
688   reset();
689 }
690
691 int Model_AttributeSelection::Id()
692 {
693   int anID = 0;
694   std::shared_ptr<GeomAPI_Shape> aSelection = value();
695   std::shared_ptr<GeomAPI_Shape> aContext = context()->shape();
696   // support for compsolids:
697   if (context().get() && ModelAPI_Tools::compSolidOwner(context()).get())
698     aContext = ModelAPI_Tools::compSolidOwner(context())->shape();
699
700
701   TopoDS_Shape aMainShape = aContext->impl<TopoDS_Shape>();
702   const TopoDS_Shape& aSubShape = aSelection->impl<TopoDS_Shape>();
703   // searching for the latest main shape
704   if (aSelection && !aSelection->isNull() &&
705     aContext   && !aContext->isNull())
706   {
707     std::shared_ptr<Model_Document> aDoc =
708       std::dynamic_pointer_cast<Model_Document>(context()->document());
709     if (aDoc.get()) {
710       Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(aMainShape, aDoc->generalLabel());
711       if (!aNS.IsNull()) {
712         aMainShape = TNaming_Tool::CurrentShape(aNS);
713       }
714     }
715
716     TopTools_IndexedMapOfShape aSubShapesMap;
717     TopExp::MapShapes(aMainShape, aSubShapesMap);
718     anID = aSubShapesMap.FindIndex(aSubShape);
719   }
720   return anID;
721 }
722
723 void Model_AttributeSelection::setId(int theID)
724 {
725   const ResultPtr& aContext = context();
726   std::shared_ptr<GeomAPI_Shape> aSelection;
727
728   std::shared_ptr<GeomAPI_Shape> aContextShape = aContext->shape();
729   // support for compsolids:
730   if (aContext.get() && ModelAPI_Tools::compSolidOwner(aContext).get())
731     aContextShape = ModelAPI_Tools::compSolidOwner(aContext)->shape();
732
733   TopoDS_Shape aMainShape = aContextShape->impl<TopoDS_Shape>();
734   // searching for the latest main shape
735   if (theID > 0 &&
736       aContextShape && !aContextShape->isNull())
737   {
738     std::shared_ptr<Model_Document> aDoc =
739       std::dynamic_pointer_cast<Model_Document>(aContext->document());
740     if (aDoc.get()) {
741       Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(aMainShape, aDoc->generalLabel());
742       if (!aNS.IsNull()) {
743         aMainShape = TNaming_Tool::CurrentShape(aNS);
744       }
745     }
746
747     TopTools_IndexedMapOfShape aSubShapesMap;
748     TopExp::MapShapes(aMainShape, aSubShapesMap);
749     const TopoDS_Shape& aSelShape = aSubShapesMap.FindKey(theID);
750
751     std::shared_ptr<GeomAPI_Shape> aResult(new GeomAPI_Shape);
752     aResult->setImpl(new TopoDS_Shape(aSelShape));
753
754     aSelection = aResult;
755   }
756
757   setValue(aContext, aSelection);
758 }
759
760 std::string Model_AttributeSelection::contextName(const ResultPtr& theContext) const
761 {
762   std::string aResult;
763   if (owner()->document() != theContext->document()) {
764     if (theContext->document() == ModelAPI_Session::get()->moduleDocument()) {
765       aResult = theContext->document()->kind() + "/";
766     } else {
767       ResultPtr aDocRes = ModelAPI_Tools::findPartResult(
768         ModelAPI_Session::get()->moduleDocument(), theContext->document());
769       if (aDocRes.get()) {
770         aResult = aDocRes->data()->name() + "/";
771       }
772     }
773   }
774   aResult += theContext->data()->name();
775   return aResult;
776 }
777
778 void Model_AttributeSelection::computeValues(
779   ResultPtr theOldContext, ResultPtr theNewContext, TopoDS_Shape theValShape,
780   TopTools_ListOfShape& theShapes)
781 {
782   TopoDS_Shape anOldContShape = theOldContext->shape()->impl<TopoDS_Shape>();
783   TopoDS_Shape aNewContShape = theNewContext->shape()->impl<TopoDS_Shape>();
784   if (anOldContShape.IsSame(theValShape)) { // full context shape substituted by new full context
785     theShapes.Append(aNewContShape);
786     return;
787   }
788   if (theValShape.IsNull()) {
789     theShapes.Append(theValShape);
790     return;
791   }
792   // if a new value is unchanged in the new context, do nothing: value is correct
793   TopExp_Explorer aSubExp(aNewContShape, theValShape.ShapeType());
794   for(; aSubExp.More(); aSubExp.Next()) {
795     if (aSubExp.Current().IsSame(theValShape)) {
796       theShapes.Append(theValShape);
797       return;
798     }
799   }
800   // if new context becomes compsolid, the resulting sub may be in sub-solids
801   std::list<ResultPtr> aNewToIterate;
802   aNewToIterate.push_back(theNewContext);
803   ResultCompSolidPtr aComp = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(theNewContext);
804   if (aComp.get()) {
805     for(int a = 0; a < aComp->numberOfSubs(); a++)
806       aNewToIterate.push_back(aComp->subResult(a, false));
807   }
808
809   // first iteration: searching for the whole shape appearance (like face of the box)
810   // second iteration: searching for sub-shapes that contain the sub (like vertex on faces)
811   int aToFindPart = 0;
812   TopTools_DataMapOfShapeShape aNewToOld; // map from new containers to old containers (with val)
813   TopTools_MapOfShape anOlds; // to know how many olds produced new containers
814   for(; aToFindPart != 2 && theShapes.IsEmpty(); aToFindPart++) {
815     std::list<ResultPtr>::iterator aNewContIter = aNewToIterate.begin();
816     for(; aNewContIter != aNewToIterate.end(); aNewContIter++) {
817       std::shared_ptr<Model_Data> aNewData =
818         std::dynamic_pointer_cast<Model_Data>((*aNewContIter)->data());
819       TDF_Label aNewLab = aNewData->label();
820       // searching for produced sub-shape fully on some label
821       TDF_ChildIDIterator aNSIter(aNewLab, TNaming_NamedShape::GetID(), Standard_True);
822       for(; aNSIter.More(); aNSIter.Next()) {
823         Handle(TNaming_NamedShape) aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value());
824         for(TNaming_Iterator aPairIter(aNS); aPairIter.More(); aPairIter.Next()) {
825           if (aToFindPart == 0) { // search shape is fully inside
826             if (aPairIter.OldShape().IsSame(theValShape)) {
827               if (aPairIter.NewShape().IsNull()) {// value was removed
828                 theShapes.Clear();
829                 return;
830               }
831               theShapes.Append(aPairIter.NewShape());
832             }
833           } else if (!aPairIter.OldShape().IsNull()) { // search shape that contains this sub
834             TopExp_Explorer anExp(aPairIter.OldShape(), theValShape.ShapeType());
835             for(; anExp.More(); anExp.Next()) {
836               if (anExp.Current().IsSame(theValShape)) { // found a new container
837                 if (aPairIter.NewShape().IsNull()) {// value was removed
838                   theShapes.Clear();
839                   return;
840                 }
841                 aNewToOld.Bind(aPairIter.NewShape(), aPairIter.OldShape());
842                 anOlds.Add(aPairIter.OldShape());
843                 break;
844               }
845             }
846           }
847         }
848       }
849     }
850   }
851   if (aToFindPart == 2 && !aNewToOld.IsEmpty()) {
852     // map of sub-shapes -> number of occurences of these shapes in containers
853     NCollection_DataMap<TopoDS_Shape, TopTools_MapOfShape, TopTools_ShapeMapHasher> aSubs;
854     TopTools_DataMapOfShapeShape::Iterator aContIter(aNewToOld);
855     for(; aContIter.More(); aContIter.Next()) {
856       TopExp_Explorer aSubExp(aContIter.Key(), theValShape.ShapeType());
857       for(; aSubExp.More(); aSubExp.Next()) {
858         if (!aSubs.IsBound(aSubExp.Current())) {
859           aSubs.Bind(aSubExp.Current(), TopTools_MapOfShape());
860         }
861         // store old to know how many olds produced this shape
862         aSubs.ChangeFind(aSubExp.Current()).Add(aContIter.Value());
863       }
864     }
865     // if sub is appeared same times in containers as the number of old shapes that contain it
866     int aCountInOld = anOlds.Size();
867     NCollection_DataMap<TopoDS_Shape, TopTools_MapOfShape, TopTools_ShapeMapHasher>::Iterator
868       aSubsIter(aSubs);
869     for(; aSubsIter.More(); aSubsIter.Next()) {
870       if (aSubsIter.Value().Size() == aCountInOld) {
871         theShapes.Append(aSubsIter.Key());
872       }
873     }
874   }
875   if (theShapes.IsEmpty()) // nothing was changed
876     theShapes.Append(theValShape);
877 }
878
879 bool Model_AttributeSelection::searchNewContext(std::shared_ptr<Model_Document> theDoc,
880   const TopoDS_Shape theContShape, ResultPtr theContext, TopoDS_Shape theValShape,
881   TDF_Label theAccessLabel,
882   std::list<ResultPtr>& theResults, TopTools_ListOfShape& theValShapes)
883 {
884   std::set<ResultPtr> aResults; // to avoid duplicates
885   TopTools_ListOfShape aResContShapes;
886   TNaming_SameShapeIterator aModifIter(theContShape, theAccessLabel);
887   for(; aModifIter.More(); aModifIter.Next()) {
888     ResultPtr aModifierObj = std::dynamic_pointer_cast<ModelAPI_Result>
889       (theDoc->objects()->object(aModifIter.Label().Father()));
890     if (!aModifierObj.get())
891       break;
892     FeaturePtr aModifierFeat = theDoc->feature(aModifierObj);
893     if (!aModifierFeat.get())
894       break;
895     FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
896     if (aModifierFeat == aThisFeature || theDoc->objects()->isLater(aModifierFeat, aThisFeature))
897       continue; // the modifier feature is later than this, so, should not be used
898     FeaturePtr aCurrentModifierFeat = theDoc->feature(theContext);
899     if (aCurrentModifierFeat == aModifierFeat ||
900       theDoc->objects()->isLater(aCurrentModifierFeat, aModifierFeat))
901       continue; // the current modifier is later than the found, so, useless
902     Handle(TNaming_NamedShape) aNewNS;
903     aModifIter.Label().FindAttribute(TNaming_NamedShape::GetID(), aNewNS);
904     if (aNewNS->Evolution() == TNaming_MODIFY || aNewNS->Evolution() == TNaming_GENERATED) {
905       aResults.insert(aModifierObj);
906       TNaming_Iterator aPairIter(aNewNS);
907       aResContShapes.Append(aPairIter.NewShape());
908     } else if (aNewNS->Evolution() == TNaming_DELETE) { // a shape was deleted => result is empty
909       aResults.insert(ResultPtr());
910       aResContShapes.Append(TopoDS_Shape());
911     } else { // not-precessed modification => don't support it
912       continue;
913     }
914   }
915   if (aResults.empty())
916     return false; // no modifications found, must stay the same
917   // iterate all results to find futher modifications
918   std::set<ResultPtr>::iterator aResIter = aResults.begin();
919   TopTools_ListOfShape::Iterator aResShapes(aResContShapes);
920   for(; aResIter != aResults.end(); aResIter++, aResShapes.Next()) {
921     if (aResIter->get() != NULL) {
922       // compute new values by two contextes: the old and the new
923       TopTools_ListOfShape aValShapes;
924       computeValues(theContext, *aResIter, theValShape, aValShapes);
925
926       TopTools_ListIteratorOfListOfShape aNewVal(aValShapes);
927       for(; aNewVal.More(); aNewVal.Next()) {
928         std::list<ResultPtr> aNewRes;
929         TopTools_ListOfShape aNewUpdatedVal;
930         if (searchNewContext(theDoc, aResShapes.Value(), *aResIter, aNewVal.Value(),
931                              theAccessLabel, aNewRes, aNewUpdatedVal))
932         {
933           // appeand new results instead of the current ones
934           std::list<ResultPtr>::iterator aNewIter = aNewRes.begin();
935           TopTools_ListIteratorOfListOfShape aNewUpdVal(aNewUpdatedVal);
936           for(; aNewIter != aNewRes.end(); aNewIter++, aNewUpdVal.Next()) {
937             theResults.push_back(*aNewIter);
938             theValShapes.Append(aNewUpdVal.Value());
939           }
940         } else { // the current result is good
941           theResults.push_back(*aResIter);
942           theValShapes.Append(aNewVal.Value());
943         }
944       }
945     }
946   }
947   return true; // theResults must be empty: everything is deleted
948 }
949
950 void Model_AttributeSelection::updateInHistory()
951 {
952   ResultPtr aContext = std::dynamic_pointer_cast<ModelAPI_Result>(myRef.value());
953   // only bodies may be modified later in the history, don't do anything otherwise
954   if (!aContext.get() || aContext->groupName() != ModelAPI_ResultBody::group())
955     return;
956   std::shared_ptr<Model_Data> aContData = std::dynamic_pointer_cast<Model_Data>(aContext->data());
957   if (!aContData.get() || !aContData->isValid())
958     return;
959   TDF_Label aContLab = aContData->label(); // named shape where the selected context is located
960   Handle(TNaming_NamedShape) aContNS;
961   if (!aContLab.FindAttribute(TNaming_NamedShape::GetID(), aContNS))
962     return;
963   std::shared_ptr<Model_Document> aDoc =
964     std::dynamic_pointer_cast<Model_Document>(aContext->document());
965   FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
966   FeaturePtr aCurrentModifierFeat = aDoc->feature(aContext);
967   // iterate the context shape modifications in order to find a feature that is upper in history
968   // that this one and is really modifies the referenced result to refer to it
969   ResultPtr aModifierResFound;
970   TNaming_Iterator aPairIter(aContNS);
971   if (!aPairIter.More())
972     return;
973   TopoDS_Shape aNewCShape = aPairIter.NewShape();
974   bool anIterate = true;
975   // trying to update also the sub-shape selected
976   GeomShapePtr aSubShape = value();
977   if (aSubShape.get() && aSubShape->isEqual(aContext->shape()))
978     aSubShape.reset();
979   TopoDS_Shape aValShape;
980   if (aSubShape.get()) {
981     aValShape = aSubShape->impl<TopoDS_Shape>();
982   }
983
984   std::list<ResultPtr> aNewContexts;
985   TopTools_ListOfShape aValShapes;
986   if (searchNewContext(aDoc, aNewCShape, aContext, aValShape, aContLab, aNewContexts, aValShapes))
987   {
988     // update scope to reset to a new one
989     myScope.Clear();
990
991     std::list<ResultPtr>::iterator aNewCont = aNewContexts.begin();
992     TopTools_ListIteratorOfListOfShape aNewValues(aValShapes);
993     if (aNewCont == aNewContexts.end()) { // all results were deleted
994       ResultPtr anEmptyContext;
995       std::shared_ptr<GeomAPI_Shape> anEmptyShape;
996       setValue(anEmptyContext, anEmptyShape); // nullify the selection
997       return;
998     }
999
1000     GeomShapePtr aValueShape;
1001     if (!aNewValues.Value().IsNull()) {
1002       aValueShape = std::make_shared<GeomAPI_Shape>();
1003       aValueShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(aNewValues.Value()));
1004     }
1005     setValue(*aNewCont, aValueShape);
1006     // if there are more than one result, put them by "append" into "parent" list
1007     if (myParent) {
1008       for(aNewCont++, aNewValues.Next(); aNewCont != aNewContexts.end();
1009           aNewCont++, aNewValues.Next()) {
1010         GeomShapePtr aValueShape;
1011         if (!aNewValues.Value().IsNull()) {
1012           aValueShape = std::make_shared<GeomAPI_Shape>();
1013           aValueShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(aNewValues.Value()));
1014         }
1015         myParent->append(*aNewCont, aValueShape);
1016       }
1017     }
1018   }
1019 }
1020
1021 void Model_AttributeSelection::setParent(Model_AttributeSelectionList* theParent)
1022 {
1023   myParent = theParent;
1024 }