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