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