Salome HOME
Provide view transformation signal in Salome viewer
[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 (anAttr->attr()) {
103     aPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr->attr());
104   }
105   if (aPointAttr.get() != NULL)
106     return aPointAttr->pnt();
107   return std::shared_ptr<GeomAPI_Pnt2d>();
108 }
109
110 /// Returns list of features connected in a councedence feature point
111 /// \param theStartCoin the coincidence feature
112 /// \param theList a list which collects lines features
113 /// \param theAttr the attribute name
114 void findCoincidences(FeaturePtr theStartCoin, QList<FeaturePtr>& theList, std::string theAttr)
115 {
116   AttributeRefAttrPtr aPnt = theStartCoin->refattr(theAttr);
117   FeaturePtr aObj = ModelAPI_Feature::feature(aPnt->object());
118   if (!theList.contains(aObj)) {
119     std::shared_ptr<GeomAPI_Pnt2d> aOrig = getPoint(theStartCoin, theAttr);
120     if (aOrig.get() == NULL)
121       return;
122     theList.append(aObj);
123     const std::set<AttributePtr>& aRefsList = aObj->data()->refsToMe();
124     std::set<AttributePtr>::const_iterator aIt;
125     for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
126       std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
127       FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
128       if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { 
129         std::shared_ptr<GeomAPI_Pnt2d> aPnt = getPoint(aConstrFeature, theAttr);
130         if (aPnt.get() && aOrig->isEqual(aPnt)) {
131           findCoincidences(aConstrFeature, theList, SketchPlugin_ConstraintCoincidence::ENTITY_A());
132           findCoincidences(aConstrFeature, theList, SketchPlugin_ConstraintCoincidence::ENTITY_B());
133         }
134       }
135     }
136   }
137 }
138
139
140 bool PartSet_MenuMgr::addViewerItems(QMenu* theMenu, const QMap<QString, QAction*>& theStdActions) const
141 {
142   ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
143   if (!PartSet_SketcherMgr::isSketchOperation(anOperation) &&
144       !PartSet_SketcherMgr::isNestedSketchOperation(anOperation))
145     return false;
146
147   myCoinsideLines.clear();
148   ModuleBase_ISelection* aSelection = myModule->workshop()->selection();
149
150   bool aIsDetach = false;
151   bool hasAttribute = false;
152   bool hasFeature = false;
153
154   QList<ModuleBase_ViewerPrs> aPrsList = aSelection->getSelected();
155   TopoDS_Shape aShape;
156   ResultPtr aResult;
157   FeaturePtr aFeature;
158   foreach(ModuleBase_ViewerPrs aPrs, aPrsList) {
159     aResult = std::dynamic_pointer_cast<ModelAPI_Result>(aPrs.object());
160     if (aResult.get() != NULL) {
161       aShape = aPrs.shape();
162       if (aShape.IsEqual(aResult->shape()->impl<TopoDS_Shape>()))
163         hasFeature = true;
164       else
165         hasAttribute = true;
166     } else {
167       aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aPrs.object());
168       hasFeature = (aFeature.get() != NULL);
169     }
170   }
171
172   if (aPrsList.size() == 1) {
173     TopoDS_Shape aShape = aPrsList.first().shape();
174     if ((!aShape.IsNull()) && aShape.ShapeType() == TopAbs_VERTEX) {
175       // Find 2d coordinates
176       FeaturePtr aSketchFea = myModule->sketchMgr()->activeSketch();
177       if (aSketchFea->getKind() == SketchPlugin_Sketch::ID()) {
178         gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(aShape));
179         std::shared_ptr<GeomAPI_Pnt> aPnt3d(new GeomAPI_Pnt(aPnt.X(), aPnt.Y(), aPnt.Z()));
180         std::shared_ptr<GeomAPI_Pnt2d> aSelPnt = PartSet_Tools::convertTo2D(aSketchFea, aPnt3d);
181
182         // Find coincident in these coordinates
183         ObjectPtr aObj = aPrsList.first().object();
184         FeaturePtr aFeature = ModelAPI_Feature::feature(aObj);
185         const std::set<AttributePtr>& aRefsList = aFeature->data()->refsToMe();
186         std::set<AttributePtr>::const_iterator aIt;
187         FeaturePtr aCoincident;
188         for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
189           std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
190           FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
191           if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { 
192             std::shared_ptr<GeomAPI_Pnt2d> a2dPnt = 
193               getPoint(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_A());
194             if (a2dPnt.get() && aSelPnt->isEqual(a2dPnt)) { 
195               aCoincident = aConstrFeature;
196               break;
197             } else {
198               a2dPnt = getPoint(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_B());
199               if (a2dPnt.get() && aSelPnt->isEqual(a2dPnt)) { 
200                 aCoincident = aConstrFeature;
201                 break;
202               }
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   if ((!aIsDetach) && hasFeature) {
230     theMenu->addAction(theStdActions["DELETE_CMD"]);
231   }
232   if (hasAttribute)
233     return true;
234   bool isAuxiliary;
235   if (canSetAuxiliary(isAuxiliary)) {
236     QAction* anAction = action("AUXILIARY_CMD");
237     theMenu->addAction(anAction);
238     anAction->setChecked(isAuxiliary);
239   }
240   return true;
241 }
242
243 void PartSet_MenuMgr::onLineHighlighted(QAction* theAction)
244 {
245   if (myPrevId != -1) {
246     // Restore color for previous object
247     setLineColor(myPrevId, myColor, false);
248   }
249   myPrevId = theAction->data().toInt();
250   myColor = setLineColor(myPrevId, Qt::white, true);
251 }
252
253 QColor PartSet_MenuMgr::setLineColor(int theId, const QColor theColor, bool theUpdate)
254 {
255   XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
256   XGUI_Workshop* aWorkshop = aConnector->workshop();
257   XGUI_Displayer* aDisplayer = aWorkshop->displayer();
258
259   FeaturePtr aLine = myCoinsideLines.at(myPrevId);
260   std::list<ResultPtr>::const_iterator aIt;
261   const std::list<ResultPtr>& aResults = aLine->results();
262   QColor aColor;
263   for (aIt = aResults.cbegin(); aIt != aResults.cend(); ++aIt) {
264     aColor = aDisplayer->setObjectColor((*aIt), theColor, false);
265   }
266   if (theUpdate)
267     aDisplayer->updateViewer();
268   return aColor;
269 }
270
271
272 void PartSet_MenuMgr::onLineDetach(QAction* theAction)
273 {
274   int aId = theAction->data().toInt();
275   FeaturePtr aLine = myCoinsideLines.at(aId);
276   std::shared_ptr<GeomAPI_Pnt2d> aOrig = getPoint(mySelectedFeature, SketchPlugin_ConstraintCoincidence::ENTITY_A());
277   if (aOrig.get() == NULL)
278     aOrig = getPoint(mySelectedFeature, SketchPlugin_ConstraintCoincidence::ENTITY_B());
279   
280   gp_Pnt aOr = aOrig->impl<gp_Pnt>();
281   const std::set<AttributePtr>& aRefsList = aLine->data()->refsToMe();
282
283   QObjectPtrList aToDelFeatures;
284   std::set<AttributePtr>::const_iterator aIt;
285   // Find all coincedences corresponded to the selected line in the selected point
286   for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
287     std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
288     FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
289     if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { 
290       std::shared_ptr<GeomAPI_Pnt2d> aPnt = getPoint(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_A());
291       if (aPnt.get() == NULL)
292         aPnt = getPoint(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_B());
293       if (aPnt.get() == NULL)
294         return;
295       gp_Pnt aP = aPnt->impl<gp_Pnt>();
296       if (aOrig->isEqual(aPnt)) {
297         aToDelFeatures.append(aConstrFeature);
298       } else {
299         aPnt = getPoint(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_B());
300         aP = aPnt->impl<gp_Pnt>();
301         if (aOrig->isEqual(aPnt)) {
302           aToDelFeatures.append(aConstrFeature);
303           break;
304         }
305       }
306     }
307   }
308   if (aToDelFeatures.size() > 0) {
309     XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
310     XGUI_Workshop* aWorkshop = aConnector->workshop();
311     ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
312     if (PartSet_SketcherMgr::isNestedSketchOperation(anOperation))
313       anOperation->abort();
314
315     SessionPtr aMgr = ModelAPI_Session::get();
316     std::set<FeaturePtr> anIgnoredFeatures;
317     anIgnoredFeatures.insert(myModule->sketchMgr()->activeSketch());
318
319     QString aName = tr("Detach %1").arg(aLine->data()->name().c_str());
320     aMgr->startOperation(aName.toStdString());
321     aWorkshop->deleteFeatures(aToDelFeatures, anIgnoredFeatures);
322     aMgr->finishOperation();
323   }
324   myCoinsideLines.clear();
325 }
326
327
328 void PartSet_MenuMgr::onDetachMenuHide()
329 {
330   if (myPrevId != -1) {
331     // Restore color for previous object
332     setLineColor(myPrevId, myColor, false);
333   }
334   // Clear previous definitions
335   myPrevId = -1;
336 }
337
338   
339 void PartSet_MenuMgr::setAuxiliary(const bool isChecked)
340 {
341   ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
342
343   bool isActiveSketch = PartSet_SketcherMgr::isSketchOperation(anOperation) ||
344                         PartSet_SketcherMgr::isNestedSketchOperation(anOperation);
345   if (!isActiveSketch)
346     return;
347
348   QObjectPtrList anObjects;
349   bool isUseTransaction = false;
350   // 1. change auxiliary type of a created feature
351   if (PartSet_SketcherMgr::isNestedCreateOperation(anOperation) &&
352     PartSet_SketcherMgr::isEntity(anOperation->id().toStdString()) ) {
353     anObjects.append(anOperation->feature());
354   }
355   else {
356     isUseTransaction = true;
357     // 2. change auxiliary type of selected sketch entities
358     ModuleBase_ISelection* aSelection = myModule->workshop()->selection();
359     anObjects = aSelection->selectedPresentations();
360   }
361
362   QAction* anAction = action("AUXILIARY_CMD");
363   SessionPtr aMgr = ModelAPI_Session::get();
364   if (isUseTransaction) {
365     if (PartSet_SketcherMgr::isNestedSketchOperation(anOperation))
366       anOperation->abort();
367     aMgr->startOperation(anAction->text().toStdString());
368   }
369   myModule->sketchMgr()->storeSelection();
370
371   if (anObjects.size() > 0) {
372     QObjectPtrList::const_iterator anIt = anObjects.begin(), aLast = anObjects.end();
373     for (; anIt != aLast; anIt++) {
374       FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
375       if (aFeature.get() != NULL) {
376         std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
377                             std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
378         if (aSketchFeature.get() != NULL) {
379           std::string anAttribute = SketchPlugin_SketchEntity::AUXILIARY_ID();
380
381           std::shared_ptr<ModelAPI_AttributeBoolean> anAuxiliaryAttr = 
382             std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(aSketchFeature->data()->attribute(anAttribute));
383           if (anAuxiliaryAttr)
384             anAuxiliaryAttr->setValue(isChecked);
385         }
386       }
387     }
388   }
389   if (isUseTransaction) {
390     aMgr->finishOperation();
391     XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
392     XGUI_Workshop* aWorkshop = aConnector->workshop();
393     aWorkshop->updateCommandStatus();
394   }
395
396   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
397   myModule->sketchMgr()->restoreSelection();
398 }
399
400 bool PartSet_MenuMgr::canSetAuxiliary(bool& theValue) const
401 {
402   bool anEnabled = false;
403   ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
404
405   bool isActiveSketch = PartSet_SketcherMgr::isSketchOperation(anOperation) ||
406                         PartSet_SketcherMgr::isNestedSketchOperation(anOperation);
407   if (!isActiveSketch)
408     return anEnabled;
409
410   QObjectPtrList anObjects;
411   // 1. change auxiliary type of a created feature
412   if (PartSet_SketcherMgr::isNestedCreateOperation(anOperation) &&
413     PartSet_SketcherMgr::isEntity(anOperation->id().toStdString()) ) {
414     anObjects.append(anOperation->feature());
415   }
416   else {
417     /// The operation should not be aborted here, because the method does not changed
418     /// the auxilliary state, but checks the possibility to perform this
419     ///if (PartSet_SketcherMgr::isNestedSketchOperation(anOperation))
420     ///  anOperation->abort();
421     // 2. change auxiliary type of selected sketch entities
422     ModuleBase_ISelection* aSelection = myModule->workshop()->selection();
423     anObjects = aSelection->selectedPresentations();
424   }
425
426   bool isNotAuxiliaryFound = false;
427   if (anObjects.size() > 0) {
428     QObjectPtrList::const_iterator anIt = anObjects.begin(), aLast = anObjects.end();
429     for (; anIt != aLast && !isNotAuxiliaryFound; anIt++) {
430       FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
431       if ((aFeature.get() != NULL) && PartSet_SketcherMgr::isEntity(aFeature->getKind())) {
432         anEnabled = true;
433         std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
434                             std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
435         if (aSketchFeature.get() != NULL) {
436           std::string anAttribute = SketchPlugin_SketchEntity::AUXILIARY_ID();
437
438           std::shared_ptr<ModelAPI_AttributeBoolean> anAuxiliaryAttr = 
439             std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(aSketchFeature->data()->attribute(anAttribute));
440           if (anAuxiliaryAttr)
441             isNotAuxiliaryFound = !anAuxiliaryAttr->value();
442         }
443       }
444     }
445   }
446   theValue = anObjects.size() && !isNotAuxiliaryFound;
447   return anEnabled;
448 }