Salome HOME
Issue #2602: Check selection with validators before its using.
[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_ResultBody.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 <ModelAPI_Validator.h>
39 #include <Events_InfoMessage.h>
40 #include <GeomAPI_Edge.h>
41 #include <GeomAPI_Vertex.h>
42 #include <GeomAlgoAPI_CompoundBuilder.h>
43
44 #include <TNaming_Selector.hxx>
45 #include <TNaming_NamedShape.hxx>
46 #include <TNaming_Tool.hxx>
47 #include <TNaming_Builder.hxx>
48 #include <TNaming_SameShapeIterator.hxx>
49 #include <TNaming_Iterator.hxx>
50 #include <TDataStd_Integer.hxx>
51 #include <TDataStd_UAttribute.hxx>
52 #include <TDataStd_Name.hxx>
53 #include <TopTools_ListOfShape.hxx>
54 #include <TopTools_DataMapOfShapeShape.hxx>
55 #include <TopTools_MapOfShape.hxx>
56 #include <TopExp_Explorer.hxx>
57 #include <BRep_Tool.hxx>
58 #include <TopoDS.hxx>
59 #include <TopExp.hxx>
60 #include <TDF_ChildIterator.hxx>
61 #include <TDF_ChildIDIterator.hxx>
62 #include <TopoDS_Iterator.hxx>
63 #include <TDF_ChildIDIterator.hxx>
64 #include <Geom_Circle.hxx>
65 #include <Geom_Ellipse.hxx>
66 #include <BRep_Builder.hxx>
67
68 //#define DEB_NAMING 1
69 #ifdef DEB_NAMING
70 #include <BRepTools.hxx>
71 #endif
72 /// added to the index in the packed map to signalize that the vertex of edge is selected
73 /// (multiplied by the index of the edge)
74 static const int kSTART_VERTEX_DELTA = 1000000;
75 // identifier that there is simple reference: selection equals to context
76 Standard_GUID kSIMPLE_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb29");
77 // reference to Part sub-object
78 Standard_GUID kPART_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb27");
79 // selection is invalid after recomputation
80 Standard_GUID kINVALID_SELECTION("bce47fd7-80fa-4462-9d63-2f58acddd49d");
81
82 // identifier of the selection of the center of circle on edge
83 Standard_GUID kCIRCLE_CENTER("d0d0e0f1-217a-4b95-8fbb-0c4132f23718");
84 // identifier of the selection of the first focus point of ellipse on edge
85 Standard_GUID kELLIPSE_CENTER1("f70df04c-3168-4dc9-87a4-f1f840c1275d");
86 // identifier of the selection of the second focus point of ellipse on edge
87 Standard_GUID kELLIPSE_CENTER2("1395ae73-8e02-4cf8-b204-06ff35873a32");
88
89 // prefix for the whole feature context identification
90 const static std::string kWHOLE_FEATURE = "all-in-";
91
92 // on this label is stored:
93 // TNaming_NamedShape - selected shape
94 // TNaming_Naming - topological selection information (for the body)
95 // TDataStd_IntPackedMap - indexes of edges in composite element (for construction)
96 // TDataStd_Integer - type of the selected shape (for construction)
97 // TDF_Reference - from ReferenceAttribute, the context
98 bool Model_AttributeSelection::setValue(const ObjectPtr& theContext,
99   const std::shared_ptr<GeomAPI_Shape>& theSubShape, const bool theTemporarily)
100 {
101   if (theTemporarily &&
102       (!theContext.get() || theContext->groupName() != ModelAPI_Feature::group())) {
103     // just keep the stored without DF update
104     myTmpContext = std::dynamic_pointer_cast<ModelAPI_Result>(theContext);
105     myTmpSubShape = theSubShape;
106     owner()->data()->sendAttributeUpdated(this);
107     return true;
108   } else {
109     myTmpContext.reset();
110     myTmpSubShape.reset();
111     myTmpCenterType = NOT_CENTER;
112   }
113
114   CenterType aType;
115   const std::shared_ptr<GeomAPI_Shape>& anOldShape = internalValue(aType);
116   bool isOldContext = theContext == myRef.value();
117   bool isOldShape = isOldContext &&
118     (theSubShape == anOldShape || (theSubShape && anOldShape && theSubShape->isEqual(anOldShape)));
119   if (isOldShape) return false; // shape is the same, so context is also unchanged
120   bool aToUnblock = false;
121   // update the referenced object if needed
122   if (!isOldContext) {
123     aToUnblock = !owner()->data()->blockSendAttributeUpdated(true);
124     myRef.setValue(theContext);
125   }
126
127   // do noth use naming if selected shape is result shape itself, but not sub-shape
128   TDF_Label aSelLab = selectionLabel();
129   aSelLab.ForgetAttribute(kSIMPLE_REF_ID);
130   aSelLab.ForgetAttribute(kINVALID_SELECTION);
131   aSelLab.ForgetAttribute(kCIRCLE_CENTER);
132   aSelLab.ForgetAttribute(kELLIPSE_CENTER1);
133   aSelLab.ForgetAttribute(kELLIPSE_CENTER2);
134
135   bool isDegeneratedEdge = false;
136   // do not use the degenerated edge as a shape, a null context and shape is used in the case
137   if (theSubShape.get() && !theSubShape->isNull() && theSubShape->isEdge()) {
138     const TopoDS_Shape& aSubShape = theSubShape->impl<TopoDS_Shape>();
139     if (aSubShape.ShapeType() == TopAbs_EDGE)
140       isDegeneratedEdge = BRep_Tool::Degenerated(TopoDS::Edge(aSubShape)) == Standard_True;
141   }
142   if (!theContext.get() || isDegeneratedEdge) {
143     // to keep the reference attribute label
144     TDF_Label aRefLab = myRef.myRef->Label();
145     aSelLab.ForgetAllAttributes(true);
146     myRef.myRef = TDF_Reference::Set(aSelLab.Father(), aSelLab.Father());
147     if (aToUnblock)
148       owner()->data()->blockSendAttributeUpdated(false);
149     return false;
150   }
151   if (theContext->groupName() == ModelAPI_ResultBody::group()) {
152     ResultBodyPtr aContextBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(theContext);
153     // do not select the whole shape for body:it is already must be in the data framework
154     // equal and null selected objects mean the same: object is equal to context,
155     if (aContextBody->shape().get() &&
156         (aContextBody->shape()->isEqual(theSubShape) || !theSubShape.get())) {
157       aSelLab.ForgetAllAttributes(true);
158       TDataStd_UAttribute::Set(aSelLab, kSIMPLE_REF_ID);
159     } else {
160       selectBody(aContextBody, theSubShape);
161     }
162   } else if (theContext->groupName() == ModelAPI_ResultConstruction::group()) {
163     ResultConstructionPtr aContextConstruction =
164       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(theContext);
165     aSelLab.ForgetAllAttributes(true); // to remove old selection data
166     std::shared_ptr<Model_ResultConstruction> aConstruction =
167       std::dynamic_pointer_cast<Model_ResultConstruction>(theContext);
168     std::shared_ptr<GeomAPI_Shape> aSubShape;
169     if (theSubShape.get() && !aContextConstruction->shape()->isEqual(theSubShape))
170       aSubShape = theSubShape; // the whole context
171     if (aConstruction->isInfinite()) {
172       // For correct naming selection, put the shape into the naming structure.
173       // It seems sub-shapes are not needed: only this shape is (and can be ) selected.
174       TNaming_Builder aBuilder(aSelLab);
175       aBuilder.Generated(aContextConstruction->shape()->impl<TopoDS_Shape>());
176     }
177     int anIndex = aConstruction->select(theSubShape, owner()->document());
178     TDataStd_Integer::Set(aSelLab, anIndex);
179   } else if (theContext->groupName() == ModelAPI_ResultPart::group()) {
180     aSelLab.ForgetAllAttributes(true);
181     TDataStd_UAttribute::Set(aSelLab, kPART_REF_ID);
182     selectPart(std::dynamic_pointer_cast<ModelAPI_Result>(theContext), theSubShape);
183   } else { // check the feature context: parent-Part of this feature should not be used
184     FeaturePtr aFeatureContext = std::dynamic_pointer_cast<ModelAPI_Feature>(theContext);
185     if (aFeatureContext.get()) {
186       if (owner()->document() != aFeatureContext->document()) {
187         aSelLab.ForgetAllAttributes(true);
188         myRef.setValue(ObjectPtr());
189         if (aToUnblock)
190           owner()->data()->blockSendAttributeUpdated(false);
191         return false;
192       }
193     }
194   }
195
196   owner()->data()->sendAttributeUpdated(this);
197
198   if (aToUnblock)
199     owner()->data()->blockSendAttributeUpdated(false);
200
201   return true;
202 }
203
204 void Model_AttributeSelection::setValueCenter(
205     const ObjectPtr& theContext, const std::shared_ptr<GeomAPI_Edge>& theEdge,
206     const CenterType theCenterType, const bool theTemporarily)
207 {
208   bool anUpdated = setValue(theContext, theEdge, theTemporarily);
209   if (theTemporarily) {
210     myTmpCenterType = theCenterType;
211   } else { // store in the data structure
212     TDF_Label aSelLab = selectionLabel();
213     switch(theCenterType) {
214     case CIRCLE_CENTER:
215       if (!anUpdated)
216         anUpdated = !aSelLab.IsAttribute(kCIRCLE_CENTER);
217       TDataStd_UAttribute::Set(aSelLab, kCIRCLE_CENTER);
218       break;
219     case ELLIPSE_FIRST_FOCUS:
220       if (!anUpdated)
221         anUpdated = !aSelLab.IsAttribute(kELLIPSE_CENTER1);
222       TDataStd_UAttribute::Set(aSelLab, kELLIPSE_CENTER1);
223       break;
224     case ELLIPSE_SECOND_FOCUS:
225       if (!anUpdated)
226         anUpdated = !aSelLab.IsAttribute(kELLIPSE_CENTER2);
227       TDataStd_UAttribute::Set(aSelLab, kELLIPSE_CENTER2);
228       break;
229     }
230     if (anUpdated)
231       owner()->data()->sendAttributeUpdated(this);
232   }
233 }
234
235 void Model_AttributeSelection::selectValue(
236     const std::shared_ptr<ModelAPI_AttributeSelection>& theSource)
237 {
238   CenterType aType;
239   std::shared_ptr<GeomAPI_Shape> aValue =
240     std::dynamic_pointer_cast<Model_AttributeSelection>(theSource)->internalValue(aType);
241   if (!aValue.get() || aType == NOT_CENTER) {
242     setValue(theSource->context(), aValue);
243   } else {
244     std::shared_ptr<GeomAPI_Edge> anEdge(new GeomAPI_Edge);
245     anEdge->setImpl(new TopoDS_Shape(aValue->impl<TopoDS_Shape>()));
246     setValueCenter(theSource->context(), anEdge, aType);
247   }
248 }
249
250 void Model_AttributeSelection::removeTemporaryValues()
251 {
252   if (myTmpContext.get() || myTmpSubShape.get()) {
253     myTmpContext.reset();
254     myTmpSubShape.reset();
255   }
256 }
257
258 // returns the center of the edge: circular or elliptical
259 GeomShapePtr centerByEdge(GeomShapePtr theEdge, ModelAPI_AttributeSelection::CenterType theType)
260 {
261   if (theType != ModelAPI_AttributeSelection::NOT_CENTER && theEdge.get() != NULL) {
262     TopoDS_Shape aShape = theEdge->impl<TopoDS_Shape>();
263     if (!aShape.IsNull() && aShape.ShapeType() == TopAbs_EDGE) {
264       TopoDS_Edge anEdge = TopoDS::Edge(aShape);
265       double aFirst, aLast;
266       Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
267       if (!aCurve.IsNull()) {
268         TopoDS_Vertex aVertex;
269         BRep_Builder aBuilder;
270         if (theType == ModelAPI_AttributeSelection::CIRCLE_CENTER) {
271           Handle(Geom_Circle) aCirc = Handle(Geom_Circle)::DownCast(aCurve);
272           if (!aCirc.IsNull()) {
273             aBuilder.MakeVertex(aVertex, aCirc->Location(), Precision::Confusion());
274           }
275         } else { // ellipse
276           Handle(Geom_Ellipse) anEll = Handle(Geom_Ellipse)::DownCast(aCurve);
277           if (!anEll.IsNull()) {
278             aBuilder.MakeVertex(aVertex,
279               theType == ModelAPI_AttributeSelection::ELLIPSE_FIRST_FOCUS ?
280               anEll->Focus1() : anEll->Focus2(), Precision::Confusion());
281           }
282         }
283         if (!aVertex.IsNull()) {
284           std::shared_ptr<GeomAPI_Vertex> aResult(new GeomAPI_Vertex);
285           aResult->setImpl(new TopoDS_Vertex(aVertex));
286           return aResult;
287         }
288       }
289     }
290   }
291   return theEdge; // no vertex, so, return the initial edge
292 }
293
294 std::shared_ptr<GeomAPI_Shape> Model_AttributeSelection::value()
295 {
296   if (!ModelAPI_AttributeSelection::isInitialized() && !myTmpContext.get() && !myTmpSubShape.get())
297     return std::shared_ptr<GeomAPI_Shape>();
298   CenterType aType = NOT_CENTER;
299   std::shared_ptr<GeomAPI_Shape> aResult = internalValue(aType);
300   return centerByEdge(aResult, aType);
301 }
302
303 std::shared_ptr<GeomAPI_Shape> Model_AttributeSelection::internalValue(CenterType& theType)
304 {
305   theType = NOT_CENTER;
306   GeomShapePtr aResult;
307   if (myTmpContext.get() || myTmpSubShape.get()) {
308     theType = myTmpCenterType;
309     ResultConstructionPtr aResulConstruction =
310       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(myTmpContext);
311     if(aResulConstruction.get()) {
312       // it is just reference to construction.
313       return myTmpSubShape;
314     }
315     return myTmpSubShape.get() ? myTmpSubShape : myTmpContext->shape();
316   }
317
318   TDF_Label aSelLab = selectionLabel();
319   if (aSelLab.IsAttribute(kINVALID_SELECTION))
320     return aResult;
321
322   if (aSelLab.IsAttribute(kCIRCLE_CENTER))
323     theType = CIRCLE_CENTER;
324   else if (aSelLab.IsAttribute(kELLIPSE_CENTER1))
325     theType = ELLIPSE_FIRST_FOCUS;
326   else if (aSelLab.IsAttribute(kELLIPSE_CENTER2))
327     theType = ELLIPSE_SECOND_FOCUS;
328
329
330   if (myRef.isInitialized()) {
331     if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape
332       ResultPtr aContext = context();
333       if (!aContext.get())
334         return aResult; // empty result
335       return aContext->shape();
336     }
337     if (aSelLab.IsAttribute(kPART_REF_ID)) {
338       ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(context());
339       if (!aPart.get() || !aPart->isActivated())
340         return std::shared_ptr<GeomAPI_Shape>(); // postponed naming needed
341       Handle(TDataStd_Integer) anIndex;
342       if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
343         if (anIndex->Get()) { // special selection attribute was created, use it
344           return aPart->selectionValue(anIndex->Get());
345         } else { // face with name is already in the data model, so try to take it by name
346           Handle(TDataStd_Name) aName;
347           if (aSelLab.FindAttribute(TDataStd_Name::GetID(), aName)) {
348             std::string aSubShapeName(TCollection_AsciiString(aName->Get()).ToCString());
349             std::size_t aPartEnd = aSubShapeName.find('/');
350             if (aPartEnd != std::string::npos && aPartEnd != aSubShapeName.rfind('/')) {
351               std::string aNameInPart = aSubShapeName.substr(aPartEnd + 1);
352               int anIndex;
353               std::string aType; // to reuse already existing selection the type is not needed
354               return aPart->shapeInPart(aNameInPart, aType, anIndex);
355             }
356           }
357         }
358       }
359     }
360
361     std::shared_ptr<Model_ResultConstruction> aConstr =
362       std::dynamic_pointer_cast<Model_ResultConstruction>(context());
363     if (aConstr) {
364       if (aConstr->isInfinite())
365         return aResult; // empty result
366     }
367     if (!aConstr.get()) { // for construction context, return empty result as usual even
368       // the whole feature is selected
369       FeaturePtr aFeature = contextFeature();
370       if (aFeature.get()) {
371         std::list<GeomShapePtr> allShapes;
372         std::list<ResultPtr>::const_iterator aRes = aFeature->results().cbegin();
373         for (; aRes != aFeature->results().cend(); aRes++) {
374           if (aRes->get() && !(*aRes)->isDisabled()) {
375             GeomShapePtr aShape = (*aRes)->shape();
376             if (aShape.get() && !aShape->isNull()) {
377               allShapes.push_back(aShape);
378             }
379           }
380         }
381         return GeomAlgoAPI_CompoundBuilder::compound(allShapes);
382       }
383     }
384
385     Handle(TNaming_NamedShape) aSelection;
386     if (aSelLab.FindAttribute(TNaming_NamedShape::GetID(), aSelection)) {
387       TopoDS_Shape aSelShape = aSelection->Get();
388       aResult = std::shared_ptr<GeomAPI_Shape>(new GeomAPI_Shape);
389       aResult->setImpl(new TopoDS_Shape(aSelShape));
390     } else if (aConstr) { // simple construction element: just shape of this construction element
391       Handle(TDataStd_Integer) anIndex;
392       if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
393         if (anIndex->Get() == 0) // it is just reference to construction, nothing is in value
394           return aResult;
395         return aConstr->shape(anIndex->Get(), owner()->document());
396       }
397     }
398   }
399   return aResult;
400 }
401
402 bool Model_AttributeSelection::isInvalid()
403 {
404   return selectionLabel().IsAttribute(kINVALID_SELECTION) == Standard_True;
405 }
406
407 bool Model_AttributeSelection::isInitialized()
408 {
409   if (ModelAPI_AttributeSelection::isInitialized()) { // additional checks if it is initialized
410     std::shared_ptr<GeomAPI_Shape> aResult;
411     if (myRef.isInitialized()) {
412       TDF_Label aSelLab = selectionLabel();
413       if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape
414         ResultPtr aContext = context();
415         return aContext.get() != NULL;
416       }
417       Handle(TNaming_NamedShape) aSelection;
418       if (selectionLabel().FindAttribute(TNaming_NamedShape::GetID(), aSelection)) {
419         return !aSelection->Get().IsNull();
420       } else { // for simple construction element: just shape of this construction element
421         std::shared_ptr<Model_ResultConstruction> aConstr =
422           std::dynamic_pointer_cast<Model_ResultConstruction>(context());
423         if (aConstr.get()) {
424           Handle(TDataStd_Integer) anIndex;
425           if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
426             // for the whole shape it may return null, so, if index exists, returns true
427             return true;
428           }
429         }
430         // for the whole feature, a feature object
431         FeaturePtr aFeat = contextFeature();
432         if (aFeat.get())
433           return true;
434       }
435     }
436   }
437   return false;
438 }
439
440 Model_AttributeSelection::Model_AttributeSelection(TDF_Label& theLabel)
441   : myRef(theLabel)
442 {
443   myIsInitialized = myRef.isInitialized();
444   myParent = NULL;
445 }
446
447 void Model_AttributeSelection::setID(const std::string theID)
448 {
449   myRef.setID(theID);
450   ModelAPI_AttributeSelection::setID(theID);
451 }
452
453 ResultPtr Model_AttributeSelection::context()
454 {
455   if (!ModelAPI_AttributeSelection::isInitialized() && !myTmpContext.get() && !myTmpSubShape.get())
456     return ResultPtr();
457
458   if (myTmpContext.get() || myTmpSubShape.get()) {
459     return myTmpContext;
460   }
461
462   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(myRef.value());
463   // for parts there could be same-data result, so take the last enabled
464   if (aResult.get()) {
465     if(aResult->groupName() == ModelAPI_ResultPart::group()) {
466       int aSize = aResult->document()->size(ModelAPI_ResultPart::group());
467       for(int a = aSize - 1; a >= 0; a--) {
468         ObjectPtr aPart = aResult->document()->object(ModelAPI_ResultPart::group(), a);
469         if(aPart.get() && aPart->data() == aResult->data()) {
470           ResultPtr aPartResult = std::dynamic_pointer_cast<ModelAPI_Result>(aPart);
471           FeaturePtr anOwnerFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
472           // check that this result is not this-feature result (it is forbidden t oselect itself)
473           if(anOwnerFeature.get() && anOwnerFeature->firstResult() != aPartResult) {
474             return aPartResult;
475           }
476         }
477       }
478     }
479   } else { // if feature - construction is selected, it has only one result, return this result
480     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(myRef.value());
481     if (aFeature.get() && aFeature->results().size() == 1 &&
482         aFeature->firstResult()->groupName() == ModelAPI_ResultConstruction::group())
483       return aFeature->firstResult();
484   }
485   return aResult;
486 }
487
488 FeaturePtr Model_AttributeSelection::contextFeature() {
489   if (myTmpContext.get() || myTmpSubShape.get()) {
490     return FeaturePtr(); // feature can not be selected temporarily
491   }
492   return std::dynamic_pointer_cast<ModelAPI_Feature>(myRef.value());
493 }
494 ObjectPtr Model_AttributeSelection::contextObject() {
495   ResultPtr aRes = context();
496   if (aRes.get())
497     return aRes;
498   return contextFeature();
499 }
500
501
502 void Model_AttributeSelection::setObject(const std::shared_ptr<ModelAPI_Object>& theObject)
503 {
504   ModelAPI_AttributeSelection::setObject(theObject);
505   myRef.setObject(theObject);
506 }
507
508 TDF_LabelMap& Model_AttributeSelection::scope()
509 {
510   if (myScope.IsEmpty()) { // create a new scope if not yet done
511     // gets all features with named shapes that are before this feature label (before in history)
512     DocumentPtr aMyDoc = owner()->document();
513     std::list<std::shared_ptr<ModelAPI_Feature> > allFeatures = aMyDoc->allFeatures();
514     std::list<std::shared_ptr<ModelAPI_Feature> >::iterator aFIter = allFeatures.begin();
515     bool aMePassed = false;
516     CompositeFeaturePtr aComposite =
517       std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(owner());
518     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
519     CompositeFeaturePtr aCompositeOwner, aCompositeOwnerOwner;
520     if (aFeature.get()) {
521       aCompositeOwner = ModelAPI_Tools::compositeOwner(aFeature);
522       if (aCompositeOwner.get()) {
523          aCompositeOwnerOwner = ModelAPI_Tools::compositeOwner(aCompositeOwner);
524       }
525     }
526     // for group Scope is not limitet: this is always up to date objects
527     // this causes problem in galeries.py
528     //bool isGroup = aFeature.get() && aFeature->getKind() == "Group";
529     for(; aFIter != allFeatures.end(); aFIter++) {
530       if (*aFIter == owner()) {  // the left features are created later (except subs of composite)
531         aMePassed = true;
532         continue;
533       }
534       //if (isGroup) aMePassed = false;
535       bool isInScope = !aMePassed;
536       if (!isInScope && aComposite.get()) {
537         // try to add sub-elements of composite if this is composite
538         if (aComposite->isSub(*aFIter))
539           isInScope = true;
540       }
541       // remove the composite-owner of this feature (sketch in extrusion-cut)
542       if (isInScope && (aCompositeOwner == *aFIter || aCompositeOwnerOwner == *aFIter))
543         isInScope = false;
544
545       if (isInScope && aFIter->get() && (*aFIter)->data()->isValid()) {
546         TDF_Label aFeatureLab = std::dynamic_pointer_cast<Model_Data>(
547           (*aFIter)->data())->label().Father();
548         TDF_ChildIDIterator aNSIter(aFeatureLab, TNaming_NamedShape::GetID(), true);
549         for(; aNSIter.More(); aNSIter.Next()) {
550           Handle(TNaming_NamedShape) aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value());
551           if (!aNS.IsNull() && aNS->Evolution() != TNaming_SELECTED) {
552             myScope.Add(aNS->Label());
553           }
554         }
555       }
556     }
557     // also add all naming labels created for external constructions
558     std::shared_ptr<Model_Document> aDoc = std::dynamic_pointer_cast<Model_Document>(aMyDoc);
559     TDF_Label anExtDocLab = aDoc->extConstructionsLabel();
560     TDF_ChildIDIterator aNSIter(anExtDocLab, TNaming_NamedShape::GetID(), true);
561     for(; aNSIter.More(); aNSIter.Next()) {
562       Handle(TNaming_NamedShape) aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value());
563       if (!aNS.IsNull() && aNS->Evolution() != TNaming_SELECTED) {
564         myScope.Add(aNS->Label());
565       }
566     }
567   }
568   return myScope;
569 }
570
571 /// Sets the invalid flag if flag is false, or removes it if "true"
572 /// Returns theFlag
573 static bool setInvalidIfFalse(TDF_Label& theLab, const bool theFlag) {
574   if (theFlag) {
575     theLab.ForgetAttribute(kINVALID_SELECTION);
576   } else {
577     TDataStd_UAttribute::Set(theLab, kINVALID_SELECTION);
578   }
579   return theFlag;
580 }
581
582 void Model_AttributeSelection::split(
583   ResultPtr theContext, TopoDS_Shape theNewShape, TopAbs_ShapeEnum theType)
584 {
585   TopTools_ListOfShape aSubs;
586   for(TopoDS_Iterator anExplorer(theNewShape); anExplorer.More(); anExplorer.Next()) {
587     if (!anExplorer.Value().IsNull() &&
588       anExplorer.Value().ShapeType() == theType) {
589         aSubs.Append(anExplorer.Value());
590     } else { // invalid case; bad result shape, so, impossible to split easily
591       aSubs.Clear();
592       break;
593     }
594   }
595   if (aSubs.Extent() > 1) { // ok to split
596     TopTools_ListIteratorOfListOfShape aSub(aSubs);
597     GeomShapePtr aSubSh(new GeomAPI_Shape);
598     aSubSh->setImpl(new TopoDS_Shape(aSub.Value()));
599     setValue(theContext, aSubSh);
600     for(aSub.Next(); aSub.More(); aSub.Next()) {
601       GeomShapePtr aSubSh(new GeomAPI_Shape);
602       aSubSh->setImpl(new TopoDS_Shape(aSub.Value()));
603       myParent->append(theContext, aSubSh);
604     }
605   }
606 }
607
608 bool Model_AttributeSelection::update()
609 {
610   FeaturePtr aContextFeature = contextFeature();
611   if (aContextFeature.get()) {
612     return true;
613   }
614   TDF_Label aSelLab = selectionLabel();
615   ResultPtr aContext = context();
616   if (!aContext.get())
617     return setInvalidIfFalse(aSelLab, false);
618   if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape
619     return setInvalidIfFalse(aSelLab, aContext->shape() && !aContext->shape()->isNull());
620   }
621
622   if (aSelLab.IsAttribute(kPART_REF_ID)) { // it is reference to the part object
623     std::shared_ptr<GeomAPI_Shape> aNoSelection;
624     bool aResult = selectPart(aContext, aNoSelection, true);
625     aResult = setInvalidIfFalse(aSelLab, aResult);
626     if (aResult) {
627       owner()->data()->sendAttributeUpdated(this);
628     }
629     return aResult;
630   }
631
632   if (aContext->groupName() == ModelAPI_ResultBody::group()) {
633     // body: just a named shape, use selection mechanism from OCCT
634     TNaming_Selector aSelector(aSelLab);
635     TopoDS_Shape anOldShape;
636     if (!aSelector.NamedShape().IsNull()) {
637       anOldShape = aSelector.NamedShape()->Get();
638     }
639     bool aResult = aSelector.Solve(scope()) == Standard_True;
640     // must be before sending of updated attribute (1556)
641     aResult = setInvalidIfFalse(aSelLab, aResult);
642     TopoDS_Shape aNewShape;
643     if (!aSelector.NamedShape().IsNull()) {
644       aNewShape = aSelector.NamedShape()->Get();
645     }
646     if (anOldShape.IsNull() || aNewShape.IsNull() ||
647         !anOldShape.IsEqual(aSelector.NamedShape()->Get())) {
648       // shape type shoud not not changed: if shape becomes compound of such shapes, then split
649       if (myParent && !anOldShape.IsNull() && !aNewShape.IsNull() &&
650           anOldShape.ShapeType() != aNewShape.ShapeType() &&
651           (aNewShape.ShapeType() == TopAbs_COMPOUND || aNewShape.ShapeType() == TopAbs_COMPSOLID))
652       {
653         split(aContext, aNewShape, anOldShape.ShapeType());
654       }
655       owner()->data()->sendAttributeUpdated(this);  // send updated if shape is changed
656     }
657     return aResult;
658   }
659
660   if (aContext->groupName() == ModelAPI_ResultConstruction::group()) {
661     Handle(TDataStd_Integer) anIndex;
662     if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
663       std::shared_ptr<Model_ResultConstruction> aConstructionContext =
664         std::dynamic_pointer_cast<Model_ResultConstruction>(aContext);
665       bool aModified = true;
666       bool aValid = aConstructionContext->update(anIndex->Get(), owner()->document(), aModified);
667       setInvalidIfFalse(aSelLab, aValid);
668       if (aConstructionContext->isInfinite()) {
669         // Update the selected shape.
670         TNaming_Builder aBuilder(aSelLab);
671         aBuilder.Generated(aConstructionContext->shape()->impl<TopoDS_Shape>());
672       }
673       if (aModified)
674         owner()->data()->sendAttributeUpdated(this);
675       return aValid;
676     }
677   }
678   return setInvalidIfFalse(aSelLab, false); // unknown case
679 }
680
681 void Model_AttributeSelection::selectBody(
682   const ResultPtr& theContext, const std::shared_ptr<GeomAPI_Shape>& theSubShape)
683 {
684   // perform the selection
685   TNaming_Selector aSel(selectionLabel());
686   TopoDS_Shape aContext;
687
688   ResultBodyPtr aBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(theContext);//myRef.value()
689   if (aBody) {
690     aContext = aBody->shape()->impl<TopoDS_Shape>();
691   } else {
692     ResultPtr aResult =
693       std::dynamic_pointer_cast<ModelAPI_Result>(myRef.value());
694     if (aResult) {
695       aContext = aResult->shape()->impl<TopoDS_Shape>();
696     } else {
697       Events_InfoMessage("Model_AttributeSelection", "A result with shape is expected").send();
698       return;
699     }
700   }
701
702   // with "recover" feature the selected context may be not up to date (issue 1710)
703   Handle(TNaming_NamedShape) aResult;
704   TDF_Label aSelLab = selectionLabel();
705   TopoDS_Shape aNewContext = aContext;
706   bool isUpdated = true;
707   while(!aNewContext.IsNull() && isUpdated) {
708     // searching for the very last shape that was produced from this one
709     isUpdated = false;
710     if (!TNaming_Tool::HasLabel(aSelLab, aNewContext))
711       // to avoid crash of TNaming_SameShapeIterator if pure shape does not exists
712       break;
713     for(TNaming_SameShapeIterator anIter(aNewContext, aSelLab); anIter.More(); anIter.Next()) {
714       TDF_Label aNSLab = anIter.Label();
715       if (!scope().Contains(aNSLab))
716         continue;
717       Handle(TNaming_NamedShape) aNS;
718       if (aNSLab.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
719         for(TNaming_Iterator aShapesIter(aNS); aShapesIter.More(); aShapesIter.Next()) {
720           if (aShapesIter.Evolution() == TNaming_SELECTED)
721             continue; // don't use the selection evolution
722           if (!aShapesIter.OldShape().IsNull() && aShapesIter.OldShape().IsSame(aNewContext)) {
723              // found the original shape
724             aNewContext = aShapesIter.NewShape(); // go to the newer shape
725             isUpdated = true;
726             break;
727           }
728         }
729       }
730     }
731   }
732   if (aNewContext.IsNull()) { // a context is already deleted
733     setInvalidIfFalse(aSelLab, false);
734     Events_InfoMessage("Model_AttributeSelection", "Failed to select shape already deleted").send();
735     return;
736   }
737
738   TopoDS_Shape aNewSub = theSubShape ? theSubShape->impl<TopoDS_Shape>() : aContext;
739   if (!aNewSub.IsEqual(aContext)) { // searching for subshape in the new context
740     bool isFound = false;
741     TopExp_Explorer anExp(aNewContext, aNewSub.ShapeType());
742     for(; anExp.More(); anExp.Next()) {
743       if (anExp.Current().IsSame(aNewSub)) {
744         isFound = true;
745         break;
746       }
747     }
748     if (!isFound) { // sub-shape is not found in the up-to-date instance of the context shape
749       // if context is sub-result of compound/compsolid, selection of sub-shape better propagate to
750       // the main result (which is may be modified); the case is in 1799
751       ResultBodyPtr aMain = ModelAPI_Tools::bodyOwner(theContext);
752       while(ModelAPI_Tools::bodyOwner(aMain).get())
753         aMain = ModelAPI_Tools::bodyOwner(theContext);
754       if (aMain.get()) {
755         selectBody(aMain, theSubShape);
756         return;
757       }
758       setInvalidIfFalse(aSelLab, false);
759       Events_InfoMessage("Model_AttributeSelection",
760         "Failed to select sub-shape already modified").send();
761       return;
762     }
763   }
764
765   /// fix for issue 411: result modified shapes must not participate in this selection mechanism
766   if (!aContext.IsNull()) {
767     FeaturePtr aFeatureOwner = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
768     bool aEraseResults = false;
769     if (aFeatureOwner.get()) {
770       aEraseResults = !aFeatureOwner->results().empty();
771       if (aEraseResults) // erase results without flash deleted and redisplay: do it after Select
772         aFeatureOwner->removeResults(0, false, false);
773     }
774     aSel.Select(aNewSub, aNewContext);
775
776     if (aEraseResults) { // flash after Select : in Groups it makes selection with shift working
777       static Events_Loop* aLoop = Events_Loop::loop();
778       static const Events_ID kDeletedEvent = aLoop->eventByName(EVENT_OBJECT_DELETED);
779       aLoop->flush(kDeletedEvent);
780     }
781   }
782 }
783
784 bool Model_AttributeSelection::selectPart(
785   const ResultPtr& theContext, const std::shared_ptr<GeomAPI_Shape>& theSubShape,
786   const bool theUpdate)
787 {
788   ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(theContext);
789   if (!aPart.get() || !aPart->isActivated())
790     return true; // postponed naming
791   if (theUpdate) {
792     Handle(TDataStd_Integer) anIndex;
793     if (selectionLabel().FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
794       // by internal selection
795       if (anIndex->Get() > 0) {
796         // update the selection by index
797         return aPart->updateInPart(anIndex->Get());
798       } else {
799         return true; // nothing to do, referencing just by name
800       }
801     }
802     return true; // nothing to do, referencing just by name
803   }
804   // store the shape (in case part is not loaded it should be useful
805   TopoDS_Shape aShape;
806   std::string aName = theContext->data()->name();
807   if (!theSubShape.get() || theSubShape->isNull()) {// the whole part shape is selected
808     aShape = theContext->shape()->impl<TopoDS_Shape>();
809   } else {
810     aShape = theSubShape->impl<TopoDS_Shape>();
811     int anIndex;
812     aName += "/" + aPart->nameInPart(theSubShape, anIndex);
813     TDataStd_Integer::Set(selectionLabel(), anIndex);
814   }
815   TNaming_Builder aBuilder(selectionLabel());
816   aBuilder.Select(aShape, aShape);
817   // identify by name in the part
818   TDataStd_Name::Set(selectionLabel(), aName.c_str());
819   return !aName.empty();
820 }
821
822 TDF_Label Model_AttributeSelection::selectionLabel()
823 {
824   return myRef.myRef->Label().FindChild(1);
825 }
826
827 /// prefixes of the shape names with centers defined
828 static std::map<ModelAPI_AttributeSelection::CenterType, std::string> kCENTERS_PREFIX;
829
830 /// returns the map that contains all possible prefixes of the center-names
831 static std::map<ModelAPI_AttributeSelection::CenterType, std::string>& centersMap()
832 {
833   if (kCENTERS_PREFIX.empty()) { // fill map by initial values
834     kCENTERS_PREFIX[ModelAPI_AttributeSelection::CIRCLE_CENTER] = "__cc";
835     kCENTERS_PREFIX[ModelAPI_AttributeSelection::ELLIPSE_FIRST_FOCUS] = "__eff";
836     kCENTERS_PREFIX[ModelAPI_AttributeSelection::ELLIPSE_SECOND_FOCUS] = "__esf";
837   }
838   return kCENTERS_PREFIX;
839 }
840
841 std::string Model_AttributeSelection::namingName(const std::string& theDefaultName)
842 {
843   std::string aName("");
844   if(!this->isInitialized())
845     return !theDefaultName.empty() ? theDefaultName : aName;
846
847   CenterType aCenterType = NOT_CENTER;
848   std::shared_ptr<GeomAPI_Shape> aSubSh = internalValue(aCenterType);
849   ResultPtr aCont = context();
850
851   if (!aCont.get() ||
852       (aCont->groupName() == ModelAPI_ResultConstruction::group() && contextFeature().get())) {
853     // selection of a full feature
854     FeaturePtr aFeatureCont = contextFeature();
855     if (aFeatureCont.get()) {
856       return kWHOLE_FEATURE + aFeatureCont->name();
857     }
858     // in case of selection of removed result
859     return "";
860   }
861
862   Model_SelectionNaming aSelNaming(selectionLabel());
863   std::string aResult = aSelNaming.namingName(
864     aCont, aSubSh, theDefaultName, owner()->document() != aCont->document());
865   if (aCenterType != NOT_CENTER) {
866     aResult += centersMap()[aCenterType];
867   }
868   return aResult;
869 }
870
871 // returns the center type and modifies the shape name if this name is center-name
872 static ModelAPI_AttributeSelection::CenterType centerTypeByName(std::string& theShapeName)
873 {
874   std::map<ModelAPI_AttributeSelection::CenterType, std::string>::iterator aPrefixIter =
875     centersMap().begin();
876   for(; aPrefixIter != centersMap().end(); aPrefixIter++) {
877     std::size_t aFound = theShapeName.find(aPrefixIter->second);
878     if (aFound != std::string::npos &&
879         aFound == theShapeName.size() - aPrefixIter->second.size()) {
880       theShapeName = theShapeName.substr(0, aFound);
881       return aPrefixIter->first;
882     }
883   }
884   return ModelAPI_AttributeSelection::NOT_CENTER;
885 }
886
887 // type ::= COMP | COMS | SOLD | SHEL | FACE | WIRE | EDGE | VERT
888 void Model_AttributeSelection::selectSubShape(
889   const std::string& theType, const std::string& theSubShapeName)
890 {
891   if(theSubShapeName.empty() || theType.empty()) return;
892
893   std::string aSubShapeName = theSubShapeName;
894   CenterType aCenterType = theType[0] == 'v' || theType[0] == 'V' ? // only for vertex-type
895     centerTypeByName(aSubShapeName) : NOT_CENTER;
896   std::string aType = aCenterType == NOT_CENTER ? theType : "EDGE"; // search for edge now
897
898   // first iteration is selection by name without center prefix, second - in case of problem,
899   // try with initial name
900   for(int aUseCenter = 1; aUseCenter >= 0; aUseCenter--) {
901     if (aUseCenter == 0 && aCenterType != NOT_CENTER) {
902       aSubShapeName = theSubShapeName;
903       aCenterType = NOT_CENTER;
904       aType = theType;
905     } else if (aUseCenter != 1) continue;
906
907     // check this is Part-name: 2 delimiters in the name
908     std::size_t aPartEnd = aSubShapeName.find('/');
909     if (aPartEnd != std::string::npos && aPartEnd != aSubShapeName.rfind('/')) {
910       std::string aPartName = aSubShapeName.substr(0, aPartEnd);
911       ObjectPtr aFound = owner()->document()->objectByName(ModelAPI_ResultPart::group(), aPartName);
912       if (aFound.get()) { // found such part, so asking it for the name
913         ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aFound);
914         std::string aNameInPart = aSubShapeName.substr(aPartEnd + 1);
915         int anIndex;
916         std::shared_ptr<GeomAPI_Shape> aSelected = aPart->shapeInPart(aNameInPart, aType, anIndex);
917         if (aSelected.get()) {
918           if (aCenterType != NOT_CENTER) {
919             if (!aSelected->isEdge())
920               continue;
921             std::shared_ptr<GeomAPI_Edge> aSelectedEdge(new GeomAPI_Edge(aSelected));
922             setValueCenter(aPart, aSelectedEdge, aCenterType);
923           } else
924             setValue(aPart, aSelected);
925           TDataStd_Integer::Set(selectionLabel(), anIndex);
926           return;
927         }
928       }
929     }
930
931     std::shared_ptr<Model_Document> aDoc =
932       std::dynamic_pointer_cast<Model_Document>(owner()->document());
933     // check this is a whole feature context
934     if (aSubShapeName.size() > kWHOLE_FEATURE.size() &&
935       aSubShapeName.substr(0, kWHOLE_FEATURE.size()) == kWHOLE_FEATURE) {
936       std::string aFeatureName = aSubShapeName.substr(kWHOLE_FEATURE.size());
937       ObjectPtr anObj = aDoc->objectByName(ModelAPI_Feature::group(), aFeatureName);
938       if (anObj.get()) {
939         static const GeomShapePtr anEmptyShape;
940         setValue(anObj, anEmptyShape);
941         return;
942       }
943     }
944
945     Model_SelectionNaming aSelNaming(selectionLabel());
946     std::shared_ptr<GeomAPI_Shape> aShapeToBeSelected;
947     ResultPtr aCont;
948     if (aSelNaming.selectSubShape(aType, aSubShapeName, aDoc, aShapeToBeSelected, aCont)) {
949       // try to find the last context to find the up to date shape
950       if (aCont->shape().get() && !aCont->shape()->isNull() &&
951         aCont->groupName() == ModelAPI_ResultBody::group() && aDoc == owner()->document()) {
952         const TopoDS_Shape aConShape = aCont->shape()->impl<TopoDS_Shape>();
953         if (!aConShape.IsNull()) {
954           Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(aConShape, selectionLabel());
955           if (!aNS.IsNull()) {
956             aNS = TNaming_Tool::CurrentNamedShape(aNS);
957             if (!aNS.IsNull() && scope().Contains(aNS->Label())) { // scope check is for 2228
958               TDF_Label aLab = aNS->Label();
959               if (aLab.Depth() % 2 == 0)
960                 aLab = aLab.Father();
961               ObjectPtr anObj = aDoc->objects()->object(aLab);
962               while(!anObj.get() && aLab.Depth() > 5) {
963                 aLab = aLab.Father().Father();
964                 anObj = aDoc->objects()->object(aLab);
965               }
966
967               if (anObj.get()) {
968                 ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(anObj);
969                 if (aRes)
970                   aCont = aRes;
971               }
972             }
973           }
974         }
975       }
976       // if compsolid is context, try to take sub-solid as context: like in GUI and scripts
977       if (aCont.get() && aShapeToBeSelected.get()) {
978         ResultBodyPtr aComp = std::dynamic_pointer_cast<ModelAPI_ResultBody>(aCont);
979         if (aComp && aComp->numberOfSubs()) {
980           std::list<ResultPtr> allSubs;
981           ModelAPI_Tools::allSubs(aComp, allSubs);
982           std::list<ResultPtr>::iterator aS = allSubs.begin();
983           for(; aS != allSubs.end(); aS++) {
984             ResultBodyPtr aSub = std::dynamic_pointer_cast<ModelAPI_ResultBody>(*aS);
985             if (aSub && aSub->numberOfSubs() == 0 && aSub->shape().get() &&
986                 aSub->shape()->isSubShape(aShapeToBeSelected)) {
987               aCont = aSub;
988               break;
989             }
990           }
991         }
992       }
993       // try to find the latest active result that must be used instead of the selected
994       // to set the active context (like in GUI selection), not concealed one
995       bool aFindNewContext = true;
996       while(aFindNewContext && aCont.get()) {
997         aFindNewContext = false;
998         // take references to all results: root one, any sub
999         ResultBodyPtr aCompContext = ModelAPI_Tools::bodyOwner(aCont, true);
1000         std::list<ResultPtr> allRes;
1001         if (aCompContext.get()) {
1002           ModelAPI_Tools::allSubs(aCompContext, allRes);
1003           allRes.push_back(aCompContext);
1004         } else {
1005           allRes.push_back(aCont);
1006         }
1007         for(std::list<ResultPtr>::iterator aSub = allRes.begin(); aSub != allRes.end(); aSub++) {
1008           ResultPtr aResCont = *aSub;
1009           ResultBodyPtr aResBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(aResCont);
1010           // only lower and higher level subs are counted
1011           if (aResBody.get() && aResBody->numberOfSubs() > 0 && aResBody != aCompContext)
1012             continue;
1013           const std::set<AttributePtr>& aRefs = aResCont->data()->refsToMe();
1014           std::set<AttributePtr>::const_iterator aRef = aRefs.begin();
1015           for(; !aFindNewContext && aRef != aRefs.end(); aRef++) {
1016             if (!aRef->get() || !(*aRef)->owner().get())
1017               continue;
1018             // concealed attribute only
1019             FeaturePtr aRefFeat = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRef)->owner());
1020             if (!ModelAPI_Session::get()->validators()->isConcealed(
1021               aRefFeat->getKind(), (*aRef)->id()))
1022               continue;
1023             // search the feature result that contains sub-shape selected
1024             std::list<std::shared_ptr<ModelAPI_Result> > aResults;
1025
1026             // take all sub-results or one result
1027             std::list<ResultPtr> aRefFeatResults;
1028             ModelAPI_Tools::allResults(aRefFeat, aRefFeatResults);
1029             std::list<ResultPtr>::iterator aRefResIter = aRefFeatResults.begin();
1030             for(; aRefResIter != aRefFeatResults.end(); aRefResIter++) {
1031               ResultBodyPtr aBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(*aRefResIter);
1032               if (aBody.get() && aBody->numberOfSubs() == 0) // add only lower level subs
1033                 aResults.push_back(aBody);
1034             }
1035             std::list<std::shared_ptr<ModelAPI_Result> >::iterator aResIter = aResults.begin();
1036             for(; aResIter != aResults.end(); aResIter++) {
1037               if (!aResIter->get() || !(*aResIter)->data()->isValid() || (*aResIter)->isDisabled())
1038                 continue;
1039               GeomShapePtr aShape = (*aResIter)->shape();
1040               GeomShapePtr aSelectedShape =
1041                 aShapeToBeSelected.get() ? aShapeToBeSelected : aCont->shape();
1042               if (aShape.get() && aShape->isSubShape(aSelectedShape, false)) {
1043                 aCont = *aResIter; // found new context (produced from this) with same subshape
1044                 aFindNewContext = true; // continue searching futher
1045                 break;
1046               }
1047             }
1048           }
1049         }
1050       }
1051
1052       if (aCenterType != NOT_CENTER) {
1053         if (!aShapeToBeSelected->isEdge())
1054           continue;
1055         std::shared_ptr<GeomAPI_Edge> aSelectedEdge(new GeomAPI_Edge(aShapeToBeSelected));
1056         setValueCenter(aCont, aSelectedEdge, aCenterType);
1057       } else
1058         setValue(aCont, aShapeToBeSelected);
1059       return;
1060     }
1061   }
1062
1063   TDF_Label aSelLab = selectionLabel();
1064   setInvalidIfFalse(aSelLab, false);
1065   reset();
1066 }
1067
1068 int Model_AttributeSelection::Id()
1069 {
1070   int anID = 0;
1071   std::shared_ptr<GeomAPI_Shape> aSelection = value();
1072   ResultPtr aContextRes = context();
1073   // support for compsolids:
1074   while(ModelAPI_Tools::bodyOwner(aContextRes).get()) {
1075     aContextRes = ModelAPI_Tools::bodyOwner(aContextRes);
1076   }
1077   std::shared_ptr<GeomAPI_Shape> aContext = aContextRes->shape();
1078
1079
1080   TopoDS_Shape aMainShape = aContext->impl<TopoDS_Shape>();
1081   const TopoDS_Shape& aSubShape = aSelection->impl<TopoDS_Shape>();
1082   // searching for the latest main shape
1083   if (aSelection && !aSelection->isNull() && aContext && !aContext->isNull())
1084   {
1085     std::shared_ptr<Model_Document> aDoc =
1086       std::dynamic_pointer_cast<Model_Document>(context()->document());
1087     if (aDoc.get()) {
1088       Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(aMainShape, aDoc->generalLabel());
1089       if (!aNS.IsNull()) {
1090         aMainShape = TNaming_Tool::CurrentShape(aNS);
1091       }
1092     }
1093
1094     TopTools_IndexedMapOfShape aSubShapesMap;
1095     TopExp::MapShapes(aMainShape, aSubShapesMap);
1096     anID = aSubShapesMap.FindIndex(aSubShape);
1097   }
1098   return anID;
1099 }
1100
1101 void Model_AttributeSelection::setId(int theID)
1102 {
1103   std::shared_ptr<GeomAPI_Shape> aSelection;
1104
1105   ResultPtr aContextRes = context();
1106   // support for compsolids:
1107   while(ModelAPI_Tools::bodyOwner(aContextRes).get()) {
1108     aContextRes = ModelAPI_Tools::bodyOwner(aContextRes);
1109   }
1110   std::shared_ptr<GeomAPI_Shape> aContext = aContextRes->shape();
1111
1112   TopoDS_Shape aMainShape = aContext->impl<TopoDS_Shape>();
1113   // searching for the latest main shape
1114   if (theID > 0 && aContext && !aContext->isNull())
1115   {
1116     std::shared_ptr<Model_Document> aDoc =
1117       std::dynamic_pointer_cast<Model_Document>(aContextRes->document());
1118     if (aDoc.get()) {
1119       Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(aMainShape, aDoc->generalLabel());
1120       if (!aNS.IsNull()) {
1121         aMainShape = TNaming_Tool::CurrentShape(aNS);
1122       }
1123     }
1124
1125     TopTools_IndexedMapOfShape aSubShapesMap;
1126     TopExp::MapShapes(aMainShape, aSubShapesMap);
1127     const TopoDS_Shape& aSelShape = aSubShapesMap.FindKey(theID);
1128
1129     std::shared_ptr<GeomAPI_Shape> aResult(new GeomAPI_Shape);
1130     aResult->setImpl(new TopoDS_Shape(aSelShape));
1131
1132     aSelection = aResult;
1133   }
1134
1135   setValue(aContextRes, aSelection);
1136 }
1137
1138 std::string Model_AttributeSelection::contextName(const ResultPtr& theContext) const
1139 {
1140   std::string aResult;
1141   if (owner()->document() != theContext->document()) {
1142     if (theContext->document() == ModelAPI_Session::get()->moduleDocument()) {
1143       aResult = theContext->document()->kind() + "/";
1144     } else {
1145       ResultPtr aDocRes = ModelAPI_Tools::findPartResult(
1146         ModelAPI_Session::get()->moduleDocument(), theContext->document());
1147       if (aDocRes.get()) {
1148         aResult = aDocRes->data()->name() + "/";
1149       }
1150     }
1151   }
1152   aResult += theContext->data()->name();
1153   return aResult;
1154 }
1155
1156 void Model_AttributeSelection::computeValues(
1157   ResultPtr theOldContext, ResultPtr theNewContext, TopoDS_Shape theValShape,
1158   TopTools_ListOfShape& theShapes)
1159 {
1160   bool aWasWholeContext = theValShape.IsNull();
1161   if (aWasWholeContext) {
1162     //theShapes.Append(theValShape);
1163     //return;
1164     theValShape = theOldContext->shape()->impl<TopoDS_Shape>();
1165   }
1166   //TopoDS_Shape anOldContShape = theOldContext->shape()->impl<TopoDS_Shape>();
1167   TopoDS_Shape aNewContShape = theNewContext->shape()->impl<TopoDS_Shape>();
1168   //if (anOldContShape.IsSame(theValShape)) { // full context shape substituted by new full context
1169     //theShapes.Append(aNewContShape);
1170     //return;
1171   //}
1172   // if a new value is unchanged in the new context, do nothing: value is correct
1173   TopExp_Explorer aSubExp(aNewContShape, theValShape.ShapeType());
1174   for(; aSubExp.More(); aSubExp.Next()) {
1175     if (aSubExp.Current().IsSame(theValShape)) {
1176       theShapes.Append(theValShape);
1177       return;
1178     }
1179   }
1180   // if new context becomes compsolid, the resulting sub may be in sub-solids
1181   std::list<ResultPtr> aNewToIterate;
1182   aNewToIterate.push_back(theNewContext);
1183   ResultBodyPtr aComp = std::dynamic_pointer_cast<ModelAPI_ResultBody>(theNewContext);
1184   if (aComp.get()) {
1185     std::list<ResultPtr> allNewContextSubs;
1186     ModelAPI_Tools::allSubs(aComp, allNewContextSubs);
1187     std::list<ResultPtr>::iterator aSub = allNewContextSubs.begin();
1188     for(; aSub != allNewContextSubs.end(); aSub++) {
1189       ResultBodyPtr aBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(*aSub);
1190       if (aBody.get() && aBody->numberOfSubs() == 0) // add only lower level subs
1191         aNewToIterate.push_back(aBody);
1192     }
1193   }
1194
1195   // first iteration: searching for the whole shape appearance (like face of the box)
1196   // second iteration: searching for sub-shapes that contain the sub (like vertex on faces)
1197   int aToFindPart = 0;
1198   TopTools_DataMapOfShapeShape aNewToOld; // map from new containers to old containers (with val)
1199   TopTools_MapOfShape anOlds; // to know how many olds produced new containers
1200   for(; aToFindPart != 2 && theShapes.IsEmpty(); aToFindPart++) {
1201     std::list<ResultPtr>::iterator aNewContIter = aNewToIterate.begin();
1202     for(; aNewContIter != aNewToIterate.end(); aNewContIter++) {
1203       std::shared_ptr<Model_Data> aNewData =
1204         std::dynamic_pointer_cast<Model_Data>((*aNewContIter)->data());
1205       TDF_Label aNewLab = aNewData->label();
1206       // searching for produced sub-shape fully on some label
1207       TDF_ChildIDIterator aNSIter(aNewLab, TNaming_NamedShape::GetID(), Standard_True);
1208       for(; aNSIter.More(); aNSIter.Next()) {
1209         Handle(TNaming_NamedShape) aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value());
1210         for(TNaming_Iterator aPairIter(aNS); aPairIter.More(); aPairIter.Next()) {
1211           if (aToFindPart == 0) { // search shape is fully inside
1212             if (aPairIter.OldShape().IsSame(theValShape)) {
1213               if (aPairIter.NewShape().IsNull()) {// value was removed
1214                 theShapes.Clear();
1215                 return;
1216               }
1217               theShapes.Append(aPairIter.NewShape());
1218             }
1219           } else if (!aPairIter.OldShape().IsNull()) { // search shape that contains this sub
1220             TopExp_Explorer anExp(aPairIter.OldShape(), theValShape.ShapeType());
1221             for(; anExp.More(); anExp.Next()) {
1222               if (anExp.Current().IsSame(theValShape)) { // found a new container
1223                 if (aPairIter.NewShape().IsNull()) {// value was removed
1224                   theShapes.Clear();
1225                   return;
1226                 }
1227                 aNewToOld.Bind(aPairIter.NewShape(), aPairIter.OldShape());
1228                 anOlds.Add(aPairIter.OldShape());
1229                 break;
1230               }
1231             }
1232           }
1233         }
1234       }
1235     }
1236   }
1237   if (aToFindPart == 2 && !aNewToOld.IsEmpty()) {
1238     // map of sub-shapes -> number of occurences of these shapes in containers
1239     NCollection_DataMap<TopoDS_Shape, TopTools_MapOfShape, TopTools_ShapeMapHasher> aSubs;
1240     TopTools_DataMapOfShapeShape::Iterator aContIter(aNewToOld);
1241     for(; aContIter.More(); aContIter.Next()) {
1242       TopExp_Explorer aSubExp(aContIter.Key(), theValShape.ShapeType());
1243       for(; aSubExp.More(); aSubExp.Next()) {
1244         if (!aSubs.IsBound(aSubExp.Current())) {
1245           aSubs.Bind(aSubExp.Current(), TopTools_MapOfShape());
1246         }
1247         // store old to know how many olds produced this shape
1248         aSubs.ChangeFind(aSubExp.Current()).Add(aContIter.Value());
1249       }
1250     }
1251     // if sub is appeared same times in containers as the number of old shapes that contain it
1252     int aCountInOld = anOlds.Size();
1253     NCollection_DataMap<TopoDS_Shape, TopTools_MapOfShape, TopTools_ShapeMapHasher>::Iterator
1254       aSubsIter(aSubs);
1255     for(; aSubsIter.More(); aSubsIter.Next()) {
1256       if (aSubsIter.Value().Size() == aCountInOld) {
1257         theShapes.Append(aSubsIter.Key());
1258       }
1259     }
1260   }
1261   if (theShapes.IsEmpty()) { // nothing was changed
1262     theShapes.Append(aWasWholeContext ? TopoDS_Shape() : theValShape);
1263   }
1264 }
1265
1266 bool Model_AttributeSelection::searchNewContext(std::shared_ptr<Model_Document> theDoc,
1267   const TopoDS_Shape theContShape, ResultPtr theContext, TopoDS_Shape theValShape,
1268   TDF_Label theAccessLabel,
1269   std::list<ResultPtr>& theResults, TopTools_ListOfShape& theValShapes)
1270 {
1271   std::set<ResultPtr> aResults; // to avoid duplicates, new context, null if deleted
1272   TopTools_ListOfShape aResContShapes;
1273   // iterate context and shape, but also if it is sub-shape of main shape, check also it
1274   TopTools_ListOfShape aContextList;
1275   aContextList.Append(theContShape);
1276   if (theContext.get()) {
1277     ResultPtr aComposite = ModelAPI_Tools::bodyOwner(theContext);
1278     if (aComposite.get() && aComposite->shape().get() && !aComposite->shape()->isNull())
1279       aContextList.Append(aComposite->shape()->impl<TopoDS_Shape>());
1280   }
1281   for(TopTools_ListOfShape::Iterator aContIter(aContextList); aContIter.More(); aContIter.Next()) {
1282     TNaming_SameShapeIterator aModifIter(aContIter.ChangeValue(), theAccessLabel);
1283     for(; aModifIter.More(); aModifIter.Next()) {
1284       TDF_Label anObjLab = aModifIter.Label().Father();
1285       ResultPtr aModifierObj = std::dynamic_pointer_cast<ModelAPI_Result>
1286         (theDoc->objects()->object(anObjLab));
1287       if (!aModifierObj.get()) {
1288         // #2241: shape may be sub-element of new object, not main (shell created from faces)
1289         if (!anObjLab.IsRoot())
1290           aModifierObj = std::dynamic_pointer_cast<ModelAPI_Result>
1291           (theDoc->objects()->object(anObjLab.Father()));
1292         if (!aModifierObj.get())
1293           continue;
1294       }
1295       FeaturePtr aModifierFeat = theDoc->feature(aModifierObj);
1296       if (!aModifierFeat.get())
1297         continue;
1298       FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
1299       if (aModifierFeat == aThisFeature || theDoc->objects()->isLater(aModifierFeat, aThisFeature))
1300         continue; // the modifier feature is later than this, so, should not be used
1301       FeaturePtr aCurrentModifierFeat = theDoc->feature(theContext);
1302       if (aCurrentModifierFeat == aModifierFeat ||
1303         theDoc->objects()->isLater(aCurrentModifierFeat, aModifierFeat))
1304         continue; // the current modifier is later than the found, so, useless
1305       Handle(TNaming_NamedShape) aNewNS;
1306       aModifIter.Label().FindAttribute(TNaming_NamedShape::GetID(), aNewNS);
1307       if (aNewNS->Evolution() == TNaming_MODIFY || aNewNS->Evolution() == TNaming_GENERATED) {
1308         aResults.insert(aModifierObj);
1309         //TNaming_Iterator aPairIter(aNewNS);
1310         //aResContShapes.Append(aPairIter.NewShape());
1311         aResContShapes.Append(aModifierObj->shape()->impl<TopoDS_Shape>());
1312       } else if (aNewNS->Evolution() == TNaming_DELETE) { // a shape was deleted => result is empty
1313         aResults.insert(ResultPtr());
1314       } else { // not-precessed modification => don't support it
1315         continue;
1316       }
1317     }
1318   }
1319   if (aResults.empty())
1320     return false; // no modifications found, must stay the same
1321   // iterate all results to find futher modifications
1322   std::set<ResultPtr>::iterator aResIter = aResults.begin();
1323   for(; aResIter != aResults.end(); aResIter++) {
1324     if (aResIter->get() != NULL) {
1325       // compute new values by two contextes: the old and the new
1326       TopTools_ListOfShape aValShapes;
1327       computeValues(theContext, *aResIter, theValShape, aValShapes);
1328
1329       TopTools_ListIteratorOfListOfShape aNewVal(aValShapes);
1330       for(; aNewVal.More(); aNewVal.Next()) {
1331         std::list<ResultPtr> aNewRes;
1332         TopTools_ListOfShape aNewUpdatedVal;
1333         TopoDS_Shape aNewValSh = aNewVal.Value();
1334         TopoDS_Shape aNewContShape = (*aResIter)->shape()->impl<TopoDS_Shape>();
1335         if (theValShape.IsNull() && aNewContShape.IsSame(aNewValSh))
1336           aNewValSh.Nullify();
1337         if (searchNewContext(theDoc, aNewContShape, *aResIter, aNewValSh,
1338                              theAccessLabel, aNewRes, aNewUpdatedVal))
1339         {
1340           // appeand new results instead of the current ones
1341           std::list<ResultPtr>::iterator aNewIter = aNewRes.begin();
1342           TopTools_ListIteratorOfListOfShape aNewUpdVal(aNewUpdatedVal);
1343           for(; aNewIter != aNewRes.end(); aNewIter++, aNewUpdVal.Next()) {
1344             theResults.push_back(*aNewIter);
1345             theValShapes.Append(aNewUpdVal.Value());
1346           }
1347         } else { // the current result is good
1348           theResults.push_back(*aResIter);
1349           theValShapes.Append(aNewValSh);
1350         }
1351       }
1352     }
1353   }
1354   return true; // theResults must be empty: everything is deleted
1355 }
1356
1357 void Model_AttributeSelection::updateInHistory()
1358 {
1359   ResultPtr aContext = std::dynamic_pointer_cast<ModelAPI_Result>(myRef.value());
1360   // only bodies and parts may be modified later in the history, don't do anything otherwise
1361   if (!aContext.get() || (aContext->groupName() != ModelAPI_ResultBody::group() &&
1362       aContext->groupName() != ModelAPI_ResultPart::group()))
1363     return;
1364   std::shared_ptr<Model_Document> aDoc =
1365     std::dynamic_pointer_cast<Model_Document>(aContext->document());
1366   std::shared_ptr<Model_Data> aContData = std::dynamic_pointer_cast<Model_Data>(aContext->data());
1367   if (!aContData.get() || !aContData->isValid())
1368     return;
1369   TDF_Label aContLab = aContData->label(); // named shape where the selected context is located
1370   Handle(TNaming_NamedShape) aContNS;
1371   if (!aContLab.FindAttribute(TNaming_NamedShape::GetID(), aContNS)) {
1372     bool aFoundNewContext = true;
1373     ResultPtr aNewContext = aContext;
1374     while(aFoundNewContext) {
1375       aFoundNewContext = false;
1376       // parts have no shape in result, so, trace references using the Part info
1377       if (aNewContext->groupName() == ModelAPI_ResultPart::group()) {
1378         ResultPartPtr aPartContext = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aNewContext);
1379         if (aPartContext.get()) { // searching for the up to date references to the referenced cont
1380           const std::set<AttributePtr>& aRefs = aPartContext->data()->refsToMe();
1381           std::set<AttributePtr>::const_iterator aRef = aRefs.begin();
1382           for(; aRef != aRefs.end(); aRef++) {
1383             // to avoid detection of part changes by local selection only
1384             AttributeSelectionPtr aSel =
1385               std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(*aRef);
1386             if (aSel.get() && !aSel->value()->isSame(aSel->context()->shape()))
1387               continue;
1388
1389             FeaturePtr aRefFeat = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRef)->owner());
1390             if (aRefFeat.get() && aRefFeat != owner()) {
1391               FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
1392               if (aDoc->objects()->isLater(aThisFeature, aRefFeat)) { // found better feature
1393                 aFoundNewContext = true;
1394                 aNewContext = aRefFeat->firstResult();
1395               }
1396             }
1397           }
1398         }
1399       }
1400     }
1401     if (aNewContext != aContext) {
1402       setValue(aNewContext, value());
1403     }
1404     return;
1405   }
1406   FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
1407   FeaturePtr aCurrentModifierFeat = aDoc->feature(aContext);
1408   // iterate the context shape modifications in order to find a feature that is upper in history
1409   // that this one and is really modifies the referenced result to refer to it
1410   ResultPtr aModifierResFound;
1411   TNaming_Iterator aPairIter(aContNS);
1412   if (!aPairIter.More())
1413     return;
1414   TopoDS_Shape aNewCShape = aPairIter.NewShape();
1415   bool anIterate = true;
1416   // trying to update also the sub-shape selected
1417   GeomShapePtr aSubShape = value();
1418   if (aSubShape.get() && aSubShape->isEqual(aContext->shape()))
1419     aSubShape.reset();
1420   TopoDS_Shape aValShape;
1421   if (aSubShape.get()) {
1422     aValShape = aSubShape->impl<TopoDS_Shape>();
1423   }
1424
1425   std::list<ResultPtr> aNewContexts;
1426   TopTools_ListOfShape aValShapes;
1427   if (searchNewContext(aDoc, aNewCShape, aContext, aValShape, aContLab, aNewContexts, aValShapes))
1428   {
1429     // update scope to reset to a new one
1430     myScope.Clear();
1431
1432     std::list<ResultPtr>::iterator aNewCont = aNewContexts.begin();
1433     TopTools_ListIteratorOfListOfShape aNewValues(aValShapes);
1434     if (aNewCont == aNewContexts.end()) { // all results were deleted
1435       ResultPtr anEmptyContext;
1436       std::shared_ptr<GeomAPI_Shape> anEmptyShape;
1437       setValue(anEmptyContext, anEmptyShape); // nullify the selection
1438       return;
1439     }
1440
1441     GeomShapePtr aValueShape;
1442     if (!aNewValues.Value().IsNull()) {
1443       aValueShape = std::make_shared<GeomAPI_Shape>();
1444       aValueShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(aNewValues.Value()));
1445     }
1446     setValue(*aNewCont, aValueShape);
1447     // if there are more than one result, put them by "append" into "parent" list
1448     if (myParent) {
1449       for(aNewCont++, aNewValues.Next(); aNewCont != aNewContexts.end();
1450           aNewCont++, aNewValues.Next()) {
1451         GeomShapePtr aValueShape;
1452         if (!aNewValues.Value().IsNull()) {
1453           aValueShape = std::make_shared<GeomAPI_Shape>();
1454           aValueShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(aNewValues.Value()));
1455         }
1456
1457         // Check that list has the same type of shape selection before adding.
1458         GeomAPI_Shape::ShapeType aListShapeType = GeomAPI_Shape::SHAPE;
1459         if (myParent->selectionType() == "VERTEX") aListShapeType = GeomAPI_Shape::VERTEX;
1460         else if (myParent->selectionType() == "EDGE") aListShapeType = GeomAPI_Shape::EDGE;
1461         else if (myParent->selectionType() == "FACE") aListShapeType = GeomAPI_Shape::FACE;
1462
1463         GeomAPI_Shape::ShapeType aShapeShapeType = GeomAPI_Shape::SHAPE;
1464         if (aValueShape.get()) {
1465           aShapeShapeType = aValueShape->shapeType();
1466         } else {
1467           (*aNewCont)->shape()->shapeType();
1468         }
1469
1470         if (aListShapeType != GeomAPI_Shape::SHAPE && aListShapeType != aShapeShapeType) {
1471           continue;
1472         }
1473
1474         myParent->append(*aNewCont, aValueShape);
1475       }
1476     }
1477   }
1478 }
1479
1480 void Model_AttributeSelection::setParent(Model_AttributeSelectionList* theParent)
1481 {
1482   myParent = theParent;
1483 }