]> SALOME platform Git repositories - modules/shaper.git/blob - src/PartSet/PartSet_MenuMgr.cpp
Salome HOME
Issue #442: Avoid extra menus in pop-up of sketcher objects
[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   // Check that selected shape is not attribute
158   // if at least a one shape is attribute then we can not add auxiliary item
159   bool hasAttribute = false;
160   if (aShapeList.Extent() > 0) {
161     NCollection_List<TopoDS_Shape>::Iterator aIt(aShapeList);
162     std::list<ObjectPtr>::const_iterator aItObj;
163     TopoDS_Shape aShape;
164     ObjectPtr aObj;
165     ResultPtr aResult;
166     for (aItObj = aObjectsList.cbegin(); aIt.More(); aIt.Next(), aItObj++) {
167       aShape = aIt.Value(); 
168       aObj = (*aItObj);
169       aResult = std::dynamic_pointer_cast<ModelAPI_Result>(aObj);
170       if (aResult.get() != NULL) {
171         if (!aShape.IsEqual(aResult->shape()->impl<TopoDS_Shape>())) {
172           hasAttribute = true;
173           break;
174         }
175       }
176     }
177   }
178
179   if (aShapeList.Extent() == 1) {
180     TopoDS_Shape aShape = aShapeList.First();
181     if (aShape.ShapeType() == TopAbs_VERTEX) {
182       // Find 2d coordinates
183       FeaturePtr aSketchFea = myModule->sketchMgr()->activeSketch();
184       if (aSketchFea->getKind() == SketchPlugin_Sketch::ID()) {
185         gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(aShape));
186         std::shared_ptr<GeomAPI_Pnt> aPnt3d(new GeomAPI_Pnt(aPnt.X(), aPnt.Y(), aPnt.Z()));
187         std::shared_ptr<GeomAPI_Pnt2d> aSelPnt = PartSet_Tools::convertTo2D(aSketchFea, aPnt3d);
188
189         // Find coincident in these coordinates
190         ObjectPtr aObj = aObjectsList.front();
191         FeaturePtr aFeature = ModelAPI_Feature::feature(aObj);
192         const std::set<AttributePtr>& aRefsList = aFeature->data()->refsToMe();
193         std::set<AttributePtr>::const_iterator aIt;
194         FeaturePtr aCoincident;
195         for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
196           std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
197           FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
198           if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { 
199             std::shared_ptr<GeomAPI_Pnt2d> a2dPnt = getPoint(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_A());
200             if (aSelPnt->isEqual(a2dPnt)) { 
201               aCoincident = aConstrFeature;
202               break;
203             } 
204           }
205         }
206         // If we have coincidence then add Detach menu
207         if (aCoincident.get() != NULL) {
208           mySelectedFeature = aCoincident;
209           findCoincidences(mySelectedFeature, myCoinsideLines, SketchPlugin_ConstraintCoincidence::ENTITY_A());
210           findCoincidences(mySelectedFeature, myCoinsideLines, SketchPlugin_ConstraintCoincidence::ENTITY_B());
211           if (myCoinsideLines.size() > 0) {
212             aIsDetach = true;
213             QMenu* aSubMenu = theMenu->addMenu(tr("Detach"));
214             QAction* aAction;
215             int i = 0;
216             foreach (FeaturePtr aCoins, myCoinsideLines) {
217               aAction = aSubMenu->addAction(aCoins->data()->name().c_str());
218               aAction->setData(QVariant(i));
219               i++;
220             }
221             connect(aSubMenu, SIGNAL(hovered(QAction*)), SLOT(onLineHighlighted(QAction*)));
222             connect(aSubMenu, SIGNAL(aboutToHide()), SLOT(onDetachMenuHide()));
223             connect(aSubMenu, SIGNAL(triggered(QAction*)), SLOT(onLineDetach(QAction*)));
224           } 
225         }
226       }
227     }
228   }
229   QObjectPtrList aObjects = aSelection->selectedPresentations();
230   if ((!aIsDetach) && (aObjects.size() > 0)) {
231     bool hasFeature = false;
232     FeaturePtr aFeature;
233     foreach (ObjectPtr aObject, aObjects) {
234       aFeature = ModelAPI_Feature::feature(aObject);
235       if (aFeature.get() != NULL) {
236         hasFeature = true;
237       }
238     }
239     if (hasFeature && (!hasAttribute))
240         theMenu->addAction(theStdActions["DELETE_CMD"]);
241   }
242   if (hasAttribute)
243     return true;
244   bool isAuxiliary;
245   if (canSetAuxiliary(isAuxiliary)) {
246     QAction* anAction = action("AUXILIARY_CMD");
247     theMenu->addAction(anAction);
248     anAction->setChecked(isAuxiliary);
249   }
250   return true;
251 }
252
253 void PartSet_MenuMgr::onLineHighlighted(QAction* theAction)
254 {
255   if (myPrevId != -1) {
256     // Restore color for previous object
257     setLineColor(myPrevId, myColor, false);
258   }
259   myPrevId = theAction->data().toInt();
260   myColor = setLineColor(myPrevId, Qt::white, true);
261 }
262
263 QColor PartSet_MenuMgr::setLineColor(int theId, const QColor theColor, bool theUpdate)
264 {
265   XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
266   XGUI_Workshop* aWorkshop = aConnector->workshop();
267   XGUI_Displayer* aDisplayer = aWorkshop->displayer();
268
269   FeaturePtr aLine = myCoinsideLines.at(myPrevId);
270   std::list<ResultPtr>::const_iterator aIt;
271   const std::list<ResultPtr>& aResults = aLine->results();
272   QColor aColor;
273   for (aIt = aResults.cbegin(); aIt != aResults.cend(); ++aIt) {
274     aColor = aDisplayer->setObjectColor((*aIt), theColor, false);
275   }
276   if (theUpdate)
277     aDisplayer->updateViewer();
278   return aColor;
279 }
280
281
282 void PartSet_MenuMgr::onLineDetach(QAction* theAction)
283 {
284   int aId = theAction->data().toInt();
285   FeaturePtr aLine = myCoinsideLines.at(aId);
286   std::shared_ptr<GeomAPI_Pnt2d> aOrig = getPoint(mySelectedFeature, SketchPlugin_ConstraintCoincidence::ENTITY_A());
287   gp_Pnt aOr = aOrig->impl<gp_Pnt>();
288   const std::set<AttributePtr>& aRefsList = aLine->data()->refsToMe();
289
290   QObjectPtrList aToDelFeatures;
291   std::set<AttributePtr>::const_iterator aIt;
292   // Find all coincedences corresponded to the selected line in the selected point
293   for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
294     std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
295     FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
296     if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { 
297       std::shared_ptr<GeomAPI_Pnt2d> aPnt = getPoint(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_A());
298       gp_Pnt aP = aPnt->impl<gp_Pnt>();
299       if (aOrig->isEqual(aPnt)) {
300         aToDelFeatures.append(aConstrFeature);
301       } else {
302         aPnt = getPoint(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_B());
303         aP = aPnt->impl<gp_Pnt>();
304         if (aOrig->isEqual(aPnt)) {
305           aToDelFeatures.append(aConstrFeature);
306           break;
307         }
308       }
309     }
310   }
311   if (aToDelFeatures.size() > 0) {
312     XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
313     XGUI_Workshop* aWorkshop = aConnector->workshop();
314     ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
315     if (PartSet_SketcherMgr::isNestedSketchOperation(anOperation))
316       anOperation->abort();
317
318     SessionPtr aMgr = ModelAPI_Session::get();
319     std::set<FeaturePtr> anIgnoredFeatures;
320     anIgnoredFeatures.insert(myModule->sketchMgr()->activeSketch());
321
322     QString aName = tr("Detach %1").arg(aLine->data()->name().c_str());
323     aMgr->startOperation(aName.toStdString());
324     aWorkshop->deleteFeatures(aToDelFeatures, anIgnoredFeatures);
325     aMgr->finishOperation();
326   }
327   myCoinsideLines.clear();
328 }
329
330
331 void PartSet_MenuMgr::onDetachMenuHide()
332 {
333   if (myPrevId != -1) {
334     // Restore color for previous object
335     setLineColor(myPrevId, myColor, false);
336   }
337   // Clear previous definitions
338   myPrevId = -1;
339 }
340
341   
342 void PartSet_MenuMgr::setAuxiliary(const bool isChecked)
343 {
344   ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
345
346   bool isActiveSketch = PartSet_SketcherMgr::isSketchOperation(anOperation) ||
347                         PartSet_SketcherMgr::isNestedSketchOperation(anOperation);
348   if (!isActiveSketch)
349     return;
350
351   QObjectPtrList anObjects;
352   bool isUseTransaction = false;
353   // 1. change auxiliary type of a created feature
354   if (PartSet_SketcherMgr::isNestedCreateOperation(anOperation) &&
355     PartSet_SketcherMgr::isEntity(anOperation->id().toStdString()) ) {
356     anObjects.append(anOperation->feature());
357   }
358   else {
359     isUseTransaction = true;
360     // 2. change auxiliary type of selected sketch entities
361     ModuleBase_ISelection* aSelection = myModule->workshop()->selection();
362     anObjects = aSelection->selectedPresentations();
363   }
364
365   QAction* anAction = action("AUXILIARY_CMD");
366   SessionPtr aMgr = ModelAPI_Session::get();
367   if (isUseTransaction) {
368     if (PartSet_SketcherMgr::isNestedSketchOperation(anOperation))
369       anOperation->abort();
370     aMgr->startOperation(anAction->text().toStdString());
371   }
372   myModule->sketchMgr()->storeSelection();
373
374   if (anObjects.size() > 0) {
375     QObjectPtrList::const_iterator anIt = anObjects.begin(), aLast = anObjects.end();
376     for (; anIt != aLast; anIt++) {
377       FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
378       if (aFeature.get() != NULL) {
379         std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
380                             std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
381         if (aSketchFeature.get() != NULL) {
382           std::string anAttribute = SketchPlugin_SketchEntity::AUXILIARY_ID();
383
384           std::shared_ptr<ModelAPI_AttributeBoolean> anAuxiliaryAttr = 
385             std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(aSketchFeature->data()->attribute(anAttribute));
386           if (anAuxiliaryAttr)
387             anAuxiliaryAttr->setValue(isChecked);
388         }
389       }
390     }
391   }
392   if (isUseTransaction) {
393     aMgr->finishOperation();
394   }
395   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
396   myModule->sketchMgr()->restoreSelection();
397 }
398
399 bool PartSet_MenuMgr::canSetAuxiliary(bool& theValue) const
400 {
401   bool anEnabled = false;
402   ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
403
404   bool isActiveSketch = PartSet_SketcherMgr::isSketchOperation(anOperation) ||
405                         PartSet_SketcherMgr::isNestedSketchOperation(anOperation);
406   if (!isActiveSketch)
407     return anEnabled;
408
409   QObjectPtrList anObjects;
410   // 1. change auxiliary type of a created feature
411   if (PartSet_SketcherMgr::isNestedCreateOperation(anOperation) &&
412     PartSet_SketcherMgr::isEntity(anOperation->id().toStdString()) ) {
413     anObjects.append(anOperation->feature());
414   }
415   else {
416     /// The operation should not be aborted here, because the method does not changed
417     /// the auxilliary state, but checks the possibility to perform this
418     ///if (PartSet_SketcherMgr::isNestedSketchOperation(anOperation))
419     ///  anOperation->abort();
420     // 2. change auxiliary type of selected sketch entities
421     ModuleBase_ISelection* aSelection = myModule->workshop()->selection();
422     anObjects = aSelection->selectedPresentations();
423   }
424
425   bool isNotAuxiliaryFound = false;
426   if (anObjects.size() > 0) {
427     QObjectPtrList::const_iterator anIt = anObjects.begin(), aLast = anObjects.end();
428     for (; anIt != aLast && !isNotAuxiliaryFound; anIt++) {
429       FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
430       if ((aFeature.get() != NULL) && PartSet_SketcherMgr::isEntity(aFeature->getKind())) {
431         anEnabled = true;
432         std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
433                             std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
434         if (aSketchFeature.get() != NULL) {
435           std::string anAttribute = SketchPlugin_SketchEntity::AUXILIARY_ID();
436
437           std::shared_ptr<ModelAPI_AttributeBoolean> anAuxiliaryAttr = 
438             std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(aSketchFeature->data()->attribute(anAttribute));
439           if (anAuxiliaryAttr)
440             isNotAuxiliaryFound = !anAuxiliaryAttr->value();
441         }
442       }
443     }
444   }
445   theValue = anObjects.size() && !isNotAuxiliaryFound;
446   return anEnabled;
447 }