Salome HOME
Make coincidence non selectable
[modules/shaper.git] / src / PartSet / PartSet_MenuMgr.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:        PartSet_MenuMgr.cpp
4 // Created:     03 April 2015
5 // Author:      Vitaly SMETANNIKOV
6
7 #include "PartSet_MenuMgr.h"
8 #include "PartSet_Module.h"
9 #include "PartSet_SketcherMgr.h"
10 #include "PartSet_Tools.h"
11
12 #include <GeomAPI_Pnt2d.h>
13 #include <GeomDataAPI_Point2D.h>
14
15 #include <SketchPlugin_ConstraintCoincidence.h>
16 #include <SketchPlugin_Line.h>
17 #include <SketchPlugin_Circle.h>
18 #include <SketchPlugin_Point.h>
19 #include <SketchPlugin_Sketch.h>
20
21 #include <ModuleBase_ISelection.h>
22 #include <ModuleBase_Operation.h>
23
24 #include <XGUI_ModuleConnector.h>
25 #include <XGUI_Workshop.h>
26 #include <XGUI_Displayer.h>
27
28 #include <Events_Loop.h>
29 #include <ModelAPI_Events.h>
30 #include <ModelAPI_Session.h>
31
32 #include <QAction>
33 #include <QMenu>
34
35 #include <TopoDS.hxx>
36 #include <BRep_Tool.hxx>
37
38 PartSet_MenuMgr::PartSet_MenuMgr(PartSet_Module* theModule)
39   : QObject(theModule), myModule(theModule), myPrevId(-1)
40 {
41   createActions();
42 }
43
44
45 QAction* PartSet_MenuMgr::action(const QString& theId) const
46 {
47   if (myActions.contains(theId))
48     return myActions[theId];
49   return 0;
50 }
51
52 void PartSet_MenuMgr::addAction(const QString& theId, QAction* theAction)
53 {
54   if (myActions.contains(theId))
55     qCritical("A command with Id = '%s' already defined!", qPrintable(theId));
56   theAction->setData(theId);
57   connect(theAction, SIGNAL(triggered(bool)), this, SLOT(onAction(bool)));
58   myActions[theId] = theAction;
59 }
60
61 void PartSet_MenuMgr::createActions()
62 {
63   QAction* anAction;
64
65   anAction = new QAction(tr("Auxiliary"), this);
66   anAction->setCheckable(true);
67   addAction("AUXILIARY_CMD", anAction);
68 }
69
70
71 void PartSet_MenuMgr::onAction(bool isChecked)
72 {
73   QAction* aAction = static_cast<QAction*>(sender());
74   QString anId = aAction->data().toString();
75
76   if (anId == "AUXILIARY_CMD") {
77     setAuxiliary(isChecked);
78   }
79 }
80
81 /// Returns point of coincidence feature
82 /// \param theFeature the coincidence feature
83 /// \param theAttribute the attribute name
84 std::shared_ptr<GeomAPI_Pnt2d> getPoint(std::shared_ptr<ModelAPI_Feature>& theFeature,
85                                         const std::string& theAttribute)
86 {
87   std::shared_ptr<GeomDataAPI_Point2D> aPointAttr;
88
89   if (!theFeature->data())
90     return std::shared_ptr<GeomAPI_Pnt2d>();
91
92   FeaturePtr aFeature;
93   std::shared_ptr<ModelAPI_AttributeRefAttr> anAttr = std::dynamic_pointer_cast<
94       ModelAPI_AttributeRefAttr>(theFeature->data()->attribute(theAttribute));
95   if (anAttr)
96     aFeature = ModelAPI_Feature::feature(anAttr->object());
97
98   if (aFeature && aFeature->getKind() == SketchPlugin_Point::ID())
99     aPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
100         aFeature->data()->attribute(SketchPlugin_Point::COORD_ID()));
101
102   else if (aFeature &&  aFeature->getKind() == SketchPlugin_Circle::ID())
103     aPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
104         aFeature->data()->attribute(SketchPlugin_Circle::CENTER_ID()));
105
106   else if (anAttr->attr()) {
107     aPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr->attr());
108   }
109   if (aPointAttr.get() != NULL)
110     return aPointAttr->pnt();
111   return std::shared_ptr<GeomAPI_Pnt2d>();
112 }
113
114 /// Returns list of features connected in a councedence feature point
115 /// \param theStartCoin the coincidence feature
116 /// \param theList a list which collects lines features
117 /// \param theAttr the attribute name
118 void findCoincidences(FeaturePtr theStartCoin, QList<FeaturePtr>& theList, std::string theAttr)
119 {
120   AttributeRefAttrPtr aPnt = theStartCoin->refattr(theAttr);
121   FeaturePtr aObj = ModelAPI_Feature::feature(aPnt->object());
122   if (!theList.contains(aObj)) {
123     std::shared_ptr<GeomAPI_Pnt2d> aOrig = getPoint(theStartCoin, theAttr);
124     theList.append(aObj);
125     const std::set<AttributePtr>& aRefsList = aObj->data()->refsToMe();
126     std::set<AttributePtr>::const_iterator aIt;
127     for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
128       std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
129       FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
130       if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { 
131         std::shared_ptr<GeomAPI_Pnt2d> aPnt = getPoint(aConstrFeature, theAttr);
132         if (aOrig->isEqual(aPnt)) {
133           findCoincidences(aConstrFeature, theList, SketchPlugin_ConstraintCoincidence::ENTITY_A());
134           findCoincidences(aConstrFeature, theList, SketchPlugin_ConstraintCoincidence::ENTITY_B());
135         }
136       }
137     }
138   }
139 }
140
141
142 bool PartSet_MenuMgr::addViewerItems(QMenu* theMenu, const QMap<QString, QAction*>& theStdActions) const
143 {
144   ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
145   if (!PartSet_SketcherMgr::isSketchOperation(anOperation) &&
146       !PartSet_SketcherMgr::isNestedSketchOperation(anOperation))
147     return false;
148
149   myCoinsideLines.clear();
150   ModuleBase_ISelection* aSelection = myModule->workshop()->selection();
151
152   NCollection_List<TopoDS_Shape> aShapeList;
153   std::list<ObjectPtr> aObjectsList;
154   aSelection->selectedShapes(aShapeList, aObjectsList);
155   bool aIsDetach = false;
156
157   if (aShapeList.Extent() == 1) {
158     TopoDS_Shape aShape = aShapeList.First();
159     if (aShape.ShapeType() == TopAbs_VERTEX) {
160       // Find 2d coordinates
161       FeaturePtr aSketchFea = myModule->sketchMgr()->activeSketch();
162       std::shared_ptr<SketchPlugin_Sketch> aSketch = 
163         std::dynamic_pointer_cast<SketchPlugin_Sketch>(aSketchFea);
164       gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(aShape));
165       std::shared_ptr<GeomAPI_Pnt> aPnt3d(new GeomAPI_Pnt(aPnt.X(), aPnt.Y(), aPnt.Z()));
166       std::shared_ptr<GeomAPI_Pnt2d> aSelPnt = aSketch->to2D(aPnt3d);
167
168       // Find coincident in these coordinates
169       ObjectPtr aObj = aObjectsList.front();
170       FeaturePtr aFeature = ModelAPI_Feature::feature(aObj);
171       const std::set<AttributePtr>& aRefsList = aFeature->data()->refsToMe();
172       std::set<AttributePtr>::const_iterator aIt;
173       FeaturePtr aCoincident;
174       for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
175         std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
176         FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
177         if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { 
178           std::shared_ptr<GeomAPI_Pnt2d> a2dPnt = getPoint(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_A());
179           if (aSelPnt->isEqual(a2dPnt)) {
180             aCoincident = aConstrFeature;
181             break;
182           }
183         }
184       }
185       // If we have coincidence then add Detach menu
186       if (aCoincident.get() != NULL) {
187         mySelectedFeature = aCoincident;
188         findCoincidences(mySelectedFeature, myCoinsideLines, SketchPlugin_ConstraintCoincidence::ENTITY_A());
189         findCoincidences(mySelectedFeature, myCoinsideLines, SketchPlugin_ConstraintCoincidence::ENTITY_B());
190         if (myCoinsideLines.size() > 0) {
191           aIsDetach = true;
192           QMenu* aSubMenu = theMenu->addMenu(tr("Detach"));
193           QAction* aAction;
194           int i = 0;
195           foreach (FeaturePtr aCoins, myCoinsideLines) {
196             aAction = aSubMenu->addAction(aCoins->data()->name().c_str());
197             aAction->setData(QVariant(i));
198             i++;
199           }
200           connect(aSubMenu, SIGNAL(hovered(QAction*)), SLOT(onLineHighlighted(QAction*)));
201           connect(aSubMenu, SIGNAL(aboutToHide()), SLOT(onDetachMenuHide()));
202           connect(aSubMenu, SIGNAL(triggered(QAction*)), SLOT(onLineDetach(QAction*)));
203         } 
204       }
205     }
206   }
207   if ((!aIsDetach) && (aObjectsList.size() > 0)) {
208     bool hasFeature = false;
209     FeaturePtr aFeature;
210     std::list<ObjectPtr>::const_iterator aIt;
211     ObjectPtr aObject;
212     for (aIt = aObjectsList.cbegin(); aIt != aObjectsList.cend(); ++aIt) {
213       aObject = (*aIt);
214       aFeature = ModelAPI_Feature::feature(aObject);
215       if (aFeature.get() != NULL) {
216         hasFeature = true;
217       }
218     }
219     if (hasFeature)
220         theMenu->addAction(theStdActions["DELETE_CMD"]);
221   }
222   bool isAuxiliary;
223   if (canSetAuxiliary(isAuxiliary)) {
224     QAction* anAction = action("AUXILIARY_CMD");
225     theMenu->addAction(anAction);
226     anAction->setChecked(isAuxiliary);
227   }
228   return true;
229 }
230
231 void PartSet_MenuMgr::onLineHighlighted(QAction* theAction)
232 {
233   if (myPrevId != -1) {
234     // Restore color for previous object
235     setLineColor(myPrevId, myColor, false);
236   }
237   myPrevId = theAction->data().toInt();
238   myColor = setLineColor(myPrevId, Qt::white, true);
239 }
240
241 QColor PartSet_MenuMgr::setLineColor(int theId, const QColor theColor, bool theUpdate)
242 {
243   XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
244   XGUI_Workshop* aWorkshop = aConnector->workshop();
245   XGUI_Displayer* aDisplayer = aWorkshop->displayer();
246
247   FeaturePtr aLine = myCoinsideLines.at(myPrevId);
248   std::list<ResultPtr>::const_iterator aIt;
249   const std::list<ResultPtr>& aResults = aLine->results();
250   QColor aColor;
251   for (aIt = aResults.cbegin(); aIt != aResults.cend(); ++aIt) {
252     aColor = aDisplayer->setObjectColor((*aIt), theColor, false);
253   }
254   if (theUpdate)
255     aDisplayer->updateViewer();
256   return aColor;
257 }
258
259
260 void PartSet_MenuMgr::onLineDetach(QAction* theAction)
261 {
262   int aId = theAction->data().toInt();
263   FeaturePtr aLine = myCoinsideLines.at(aId);
264   std::shared_ptr<GeomAPI_Pnt2d> aOrig = getPoint(mySelectedFeature, SketchPlugin_ConstraintCoincidence::ENTITY_A());
265   gp_Pnt aOr = aOrig->impl<gp_Pnt>();
266   const std::set<AttributePtr>& aRefsList = aLine->data()->refsToMe();
267
268   QObjectPtrList aToDelFeatures;
269   std::set<AttributePtr>::const_iterator aIt;
270   // Find all coincedences corresponded to the selected line in the selected point
271   for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
272     std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
273     FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
274     if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { 
275       std::shared_ptr<GeomAPI_Pnt2d> aPnt = getPoint(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_A());
276       gp_Pnt aP = aPnt->impl<gp_Pnt>();
277       if (aOrig->isEqual(aPnt)) {
278         aToDelFeatures.append(aConstrFeature);
279       } else {
280         aPnt = getPoint(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_B());
281         aP = aPnt->impl<gp_Pnt>();
282         if (aOrig->isEqual(aPnt)) {
283           aToDelFeatures.append(aConstrFeature);
284           break;
285         }
286       }
287     }
288   }
289   if (aToDelFeatures.size() > 0) {
290     XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
291     XGUI_Workshop* aWorkshop = aConnector->workshop();
292     ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
293     if (PartSet_SketcherMgr::isNestedSketchOperation(anOperation))
294       anOperation->abort();
295
296     SessionPtr aMgr = ModelAPI_Session::get();
297     std::set<FeaturePtr> anIgnoredFeatures;
298     anIgnoredFeatures.insert(myModule->sketchMgr()->activeSketch());
299
300     QString aName = tr("Detach %1").arg(aLine->data()->name().c_str());
301     aMgr->startOperation(aName.toStdString());
302     aWorkshop->deleteFeatures(aToDelFeatures, anIgnoredFeatures);
303     aMgr->finishOperation();
304   }
305   myCoinsideLines.clear();
306 }
307
308
309 void PartSet_MenuMgr::onDetachMenuHide()
310 {
311   if (myPrevId != -1) {
312     // Restore color for previous object
313     setLineColor(myPrevId, myColor, false);
314   }
315   // Clear previous definitions
316   myPrevId = -1;
317 }
318
319   
320 void PartSet_MenuMgr::setAuxiliary(const bool isChecked)
321 {
322   ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
323
324   bool isActiveSketch = PartSet_SketcherMgr::isSketchOperation(anOperation) ||
325                         PartSet_SketcherMgr::isNestedSketchOperation(anOperation);
326   if (!isActiveSketch)
327     return;
328
329   QObjectPtrList anObjects;
330   bool isUseTransaction = false;
331   // 1. change auxiliary type of a created feature
332   if (PartSet_SketcherMgr::isNestedCreateOperation(anOperation) &&
333       PartSet_SketcherMgr::isEntityOperation(anOperation) ) {
334     anObjects.append(anOperation->feature());
335   }
336   else {
337     isUseTransaction = true;
338     // 2. change auxiliary type of selected sketch entities
339     ModuleBase_ISelection* aSelection = myModule->workshop()->selection();
340     anObjects = aSelection->selectedPresentations();
341   }
342
343   QAction* anAction = action("AUXILIARY_CMD");
344   SessionPtr aMgr = ModelAPI_Session::get();
345   if (isUseTransaction) {
346     if (PartSet_SketcherMgr::isNestedSketchOperation(anOperation))
347       anOperation->abort();
348     aMgr->startOperation(anAction->text().toStdString());
349   }
350   myModule->sketchMgr()->storeSelection();
351
352   if (anObjects.size() > 0) {
353     QObjectPtrList::const_iterator anIt = anObjects.begin(), aLast = anObjects.end();
354     for (; anIt != aLast; anIt++) {
355       FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
356       if (aFeature.get() != NULL) {
357         std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
358                             std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
359         if (aSketchFeature.get() != NULL) {
360           std::string anAttribute = SketchPlugin_SketchEntity::AUXILIARY_ID();
361
362           std::shared_ptr<ModelAPI_AttributeBoolean> anAuxiliaryAttr = 
363             std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(aSketchFeature->data()->attribute(anAttribute));
364           if (anAuxiliaryAttr)
365             anAuxiliaryAttr->setValue(isChecked);
366         }
367       }
368     }
369   }
370   if (isUseTransaction) {
371     aMgr->finishOperation();
372   }
373   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
374   myModule->sketchMgr()->restoreSelection();
375 }
376
377 bool PartSet_MenuMgr::canSetAuxiliary(bool& theValue) const
378 {
379   bool anEnabled = false;
380   ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
381
382   bool isActiveSketch = PartSet_SketcherMgr::isSketchOperation(anOperation) ||
383                         PartSet_SketcherMgr::isNestedSketchOperation(anOperation);
384   if (!isActiveSketch)
385     return anEnabled;
386
387   QObjectPtrList anObjects;
388   // 1. change auxiliary type of a created feature
389   if (PartSet_SketcherMgr::isNestedCreateOperation(anOperation) &&
390       PartSet_SketcherMgr::isEntityOperation(anOperation) ) {
391     anObjects.append(anOperation->feature());
392   }
393   else {
394     /// The operation should not be aborted here, because the method does not changed
395     /// the auxilliary state, but checks the possibility to perform this
396     ///if (PartSet_SketcherMgr::isNestedSketchOperation(anOperation))
397     ///  anOperation->abort();
398     // 2. change auxiliary type of selected sketch entities
399     ModuleBase_ISelection* aSelection = myModule->workshop()->selection();
400     anObjects = aSelection->selectedPresentations();
401   }
402   anEnabled = anObjects.size() > 0;
403
404   bool isNotAuxiliaryFound = false;
405   if (anObjects.size() > 0) {
406     QObjectPtrList::const_iterator anIt = anObjects.begin(), aLast = anObjects.end();
407     for (; anIt != aLast && !isNotAuxiliaryFound; anIt++) {
408       FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
409       if (aFeature.get() != NULL) {
410         std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
411                             std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
412         if (aSketchFeature.get() != NULL) {
413           std::string anAttribute = SketchPlugin_SketchEntity::AUXILIARY_ID();
414
415           std::shared_ptr<ModelAPI_AttributeBoolean> anAuxiliaryAttr = 
416             std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(aSketchFeature->data()->attribute(anAttribute));
417           if (anAuxiliaryAttr)
418             isNotAuxiliaryFound = !anAuxiliaryAttr->value();
419         }
420       }
421     }
422   }
423   theValue = anObjects.size() && !isNotAuxiliaryFound;
424   return anEnabled;
425 }