Salome HOME
Providing Action class to have a common approach to start/finish/abort model transact...
[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 <PartSetPlugin_Part.h>
13
14 #include <GeomAPI_Pnt2d.h>
15 #include <GeomDataAPI_Point2D.h>
16
17 #include <SketchPlugin_ConstraintCoincidence.h>
18 #include <SketchPlugin_Line.h>
19 #include <SketchPlugin_Circle.h>
20 #include <SketchPlugin_Point.h>
21 #include <SketchPlugin_Sketch.h>
22
23 #include <ModuleBase_ISelection.h>
24 #include <ModuleBase_Operation.h>
25 #include <ModuleBase_OperationFeature.h>
26
27 #include <XGUI_ModuleConnector.h>
28 #include <XGUI_Workshop.h>
29 #include <XGUI_Displayer.h>
30 #include <XGUI_DataModel.h>
31 #include <XGUI_ObjectsBrowser.h>
32
33 #include <Events_Loop.h>
34 #include <ModelAPI_Events.h>
35 #include <ModelAPI_Session.h>
36 #include <ModelAPI_ResultPart.h>
37 #include <ModelAPI_ResultParameter.h>
38
39 #include <QAction>
40 #include <QMenu>
41
42 #include <TopoDS.hxx>
43 #include <BRep_Tool.hxx>
44
45 PartSet_MenuMgr::PartSet_MenuMgr(PartSet_Module* theModule)
46   : QObject(theModule), myModule(theModule), myPrevId(-1)
47 {
48   createActions();
49 }
50
51
52 QAction* PartSet_MenuMgr::action(const QString& theId) const
53 {
54   if (myActions.contains(theId))
55     return myActions[theId];
56   return 0;
57 }
58
59 void PartSet_MenuMgr::addAction(const QString& theId, QAction* theAction)
60 {
61   if (myActions.contains(theId))
62     qCritical("A command with Id = '%s' already defined!", qPrintable(theId));
63   theAction->setData(theId);
64   connect(theAction, SIGNAL(triggered(bool)), this, SLOT(onAction(bool)));
65   myActions[theId] = theAction;
66 }
67
68 void PartSet_MenuMgr::createActions()
69 {
70   QAction* aAction;
71
72   aAction = new QAction(tr("Auxiliary"), this);
73   aAction->setCheckable(true);
74   addAction("AUXILIARY_CMD", aAction);
75
76   aAction = new QAction(QIcon(":icons/activate.png"), tr("Activate"), this);
77   connect(aAction, SIGNAL(triggered(bool)), this, SLOT(onActivatePart(bool)));
78   myActions["ACTIVATE_PART_CMD"] = aAction;
79
80   aAction = new QAction(QIcon(":icons/deactivate.png"), tr("Deactivate"), this);
81   connect(aAction, SIGNAL(triggered(bool)), this, SLOT(onActivatePartSet(bool)));
82   myActions["DEACTIVATE_PART_CMD"] = aAction;
83
84   // Activate PartSet
85   aAction = new QAction(QIcon(":icons/activate.png"), tr("Activate"), this);
86   connect(aAction, SIGNAL(triggered(bool)), this, SLOT(onActivatePartSet(bool)));
87   myActions["ACTIVATE_PARTSET_CMD"] = aAction;
88
89   aAction = new QAction(QIcon(":icons/edit.png"), tr("Edit..."), this);
90   connect(aAction, SIGNAL(triggered(bool)), this, SLOT(onEdit(bool)));
91   myActions["EDIT_CMD"] = aAction;
92
93   aAction = new QAction(QIcon(), tr("Select parent feature"), this);
94   aAction->setCheckable(false);
95   connect(aAction, SIGNAL(triggered(bool)), this, SLOT(onSelectParentFeature()));
96   myActions["SELECT_PARENT_CMD"] = aAction;
97 }
98
99
100 void PartSet_MenuMgr::onAction(bool isChecked)
101 {
102   QAction* aAction = static_cast<QAction*>(sender());
103   QString anId = aAction->data().toString();
104
105   if (anId == "AUXILIARY_CMD") {
106     setAuxiliary(isChecked);
107   }
108 }
109
110 bool PartSet_MenuMgr::addViewerMenu(QMenu* theMenu, const QMap<QString, QAction*>& theStdActions) const
111 {
112   ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
113   if (!PartSet_SketcherMgr::isSketchOperation(anOperation) &&
114       !PartSet_SketcherMgr::isNestedSketchOperation(anOperation))
115     return false;
116
117   myCoinsideLines.clear();
118   ModuleBase_ISelection* aSelection = myModule->workshop()->selection();
119
120   bool aIsDetach = false;
121   bool hasAttribute = false;
122   bool hasFeature = false;
123
124   QList<ModuleBase_ViewerPrs> aPrsList = aSelection->getSelected(ModuleBase_ISelection::Viewer);
125   TopoDS_Shape aShape;
126   ResultPtr aResult;
127   FeaturePtr aFeature;
128   foreach(ModuleBase_ViewerPrs aPrs, aPrsList) {
129     aResult = std::dynamic_pointer_cast<ModelAPI_Result>(aPrs.object());
130     if (aResult.get() != NULL) {
131       aShape = aPrs.shape();
132       if (aShape.IsEqual(aResult->shape()->impl<TopoDS_Shape>()))
133         hasFeature = true;
134       else
135         hasAttribute = true;
136     } else {
137       aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aPrs.object());
138       hasFeature = (aFeature.get() != NULL);
139     }
140   }
141
142   if (aPrsList.size() == 1) {
143     TopoDS_Shape aShape = aPrsList.first().shape();
144     if ((!aShape.IsNull()) && aShape.ShapeType() == TopAbs_VERTEX) {
145       // Find 2d coordinates
146       FeaturePtr aSketchFea = myModule->sketchMgr()->activeSketch();
147       if (aSketchFea->getKind() == SketchPlugin_Sketch::ID()) {
148         gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(aShape));
149         std::shared_ptr<GeomAPI_Pnt> aPnt3d(new GeomAPI_Pnt(aPnt.X(), aPnt.Y(), aPnt.Z()));
150         std::shared_ptr<GeomAPI_Pnt2d> aSelPnt = PartSet_Tools::convertTo2D(aSketchFea, aPnt3d);
151
152         // Find coincident in these coordinates
153         ObjectPtr aObj = aPrsList.first().object();
154         FeaturePtr aFeature = ModelAPI_Feature::feature(aObj);
155         const std::set<AttributePtr>& aRefsList = aFeature->data()->refsToMe();
156         std::set<AttributePtr>::const_iterator aIt;
157         FeaturePtr aCoincident;
158         for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
159           std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
160           FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
161           if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { 
162             std::shared_ptr<GeomAPI_Pnt2d> a2dPnt = 
163               PartSet_Tools::getPoint(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_A());
164             if (a2dPnt.get() && aSelPnt->isEqual(a2dPnt)) { 
165               aCoincident = aConstrFeature;
166               break;
167             } else {
168               a2dPnt = PartSet_Tools::getPoint(aConstrFeature,
169                                                SketchPlugin_ConstraintCoincidence::ENTITY_B());
170               if (a2dPnt.get() && aSelPnt->isEqual(a2dPnt)) { 
171                 aCoincident = aConstrFeature;
172                 break;
173               }
174             }
175           }
176         }
177         // If we have coincidence then add Detach menu
178         if (aCoincident.get() != NULL) {
179           mySelectedFeature = aCoincident;
180           PartSet_Tools::findCoincidences(mySelectedFeature, myCoinsideLines,
181                                           SketchPlugin_ConstraintCoincidence::ENTITY_A());
182           PartSet_Tools::findCoincidences(mySelectedFeature, myCoinsideLines,
183                                           SketchPlugin_ConstraintCoincidence::ENTITY_B());
184           if (myCoinsideLines.size() > 0) {
185             aIsDetach = true;
186             QMenu* aSubMenu = theMenu->addMenu(tr("Detach"));
187             QAction* aAction;
188             int i = 0;
189             foreach (FeaturePtr aCoins, myCoinsideLines) {
190               aAction = aSubMenu->addAction(aCoins->data()->name().c_str());
191               aAction->setData(QVariant(i));
192               i++;
193             }
194             connect(aSubMenu, SIGNAL(hovered(QAction*)), SLOT(onLineHighlighted(QAction*)));
195             connect(aSubMenu, SIGNAL(aboutToHide()), SLOT(onDetachMenuHide()));
196             connect(aSubMenu, SIGNAL(triggered(QAction*)), SLOT(onLineDetach(QAction*)));
197           } 
198         }
199       }
200     }
201   }
202   if ((!aIsDetach) && hasFeature) {
203     theMenu->addAction(theStdActions["DELETE_CMD"]);
204   }
205   if (hasAttribute)
206     return true;
207   bool isAuxiliary;
208   if (canSetAuxiliary(isAuxiliary)) {
209     QAction* anAction = action("AUXILIARY_CMD");
210     theMenu->addAction(anAction);
211     anAction->setChecked(isAuxiliary);
212   }
213   return true;
214 }
215
216 void PartSet_MenuMgr::updateViewerMenu(const QMap<QString, QAction*>& theStdActions)
217 {
218   ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
219
220   bool isActiveSketch = PartSet_SketcherMgr::isSketchOperation(anOperation) ||
221                         PartSet_SketcherMgr::isNestedSketchOperation(anOperation);
222   if (isActiveSketch) {
223     theStdActions["WIREFRAME_CMD"]->setEnabled(false);
224     theStdActions["SHADING_CMD"]->setEnabled(false);
225     theStdActions["SHOW_ONLY_CMD"]->setEnabled(false);
226     theStdActions["SHOW_CMD"]->setEnabled(false);
227     theStdActions["HIDE_CMD"]->setEnabled(false);
228     theStdActions["HIDEALL_CMD"]->setEnabled(false);
229   }
230 }
231
232
233 void PartSet_MenuMgr::onLineHighlighted(QAction* theAction)
234 {
235   if (myPrevId != -1) {
236     // Restore color for previous object
237     setLineColor(myPrevId, myColor, false);
238   }
239   myPrevId = theAction->data().toInt();
240   myColor = setLineColor(myPrevId, Qt::white, true);
241 }
242
243 QColor PartSet_MenuMgr::setLineColor(int theId, const QColor theColor, bool theUpdate)
244 {
245   XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
246   XGUI_Workshop* aWorkshop = aConnector->workshop();
247   XGUI_Displayer* aDisplayer = aWorkshop->displayer();
248
249   FeaturePtr aLine = myCoinsideLines.at(myPrevId);
250   std::list<ResultPtr>::const_iterator aIt;
251   const std::list<ResultPtr>& aResults = aLine->results();
252   QColor aColor;
253   for (aIt = aResults.cbegin(); aIt != aResults.cend(); ++aIt) {
254     aColor = aDisplayer->setObjectColor((*aIt), theColor, false);
255   }
256   if (theUpdate)
257     aDisplayer->updateViewer();
258   return aColor;
259 }
260
261
262 void PartSet_MenuMgr::onLineDetach(QAction* theAction)
263 {
264   int aId = theAction->data().toInt();
265   FeaturePtr aLine = myCoinsideLines.at(aId);
266   std::shared_ptr<GeomAPI_Pnt2d> aOrig = PartSet_Tools::getPoint(mySelectedFeature,
267                                                         SketchPlugin_ConstraintCoincidence::ENTITY_A());
268   if (aOrig.get() == NULL)
269     aOrig = PartSet_Tools::getPoint(mySelectedFeature,
270                                     SketchPlugin_ConstraintCoincidence::ENTITY_B());
271   
272   gp_Pnt aOr = aOrig->impl<gp_Pnt>();
273   const std::set<AttributePtr>& aRefsList = aLine->data()->refsToMe();
274
275   QObjectPtrList aToDelFeatures;
276   std::set<AttributePtr>::const_iterator aIt;
277   // Find all coincedences corresponded to the selected line in the selected point
278   for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
279     std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
280     FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
281     if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { 
282       std::shared_ptr<GeomAPI_Pnt2d> aPnt = PartSet_Tools::getPoint(aConstrFeature,
283                                             SketchPlugin_ConstraintCoincidence::ENTITY_A());
284       if (aPnt.get() == NULL)
285         aPnt = PartSet_Tools::getPoint(aConstrFeature,
286                                        SketchPlugin_ConstraintCoincidence::ENTITY_B());
287       if (aPnt.get() == NULL)
288         return;
289       gp_Pnt aP = aPnt->impl<gp_Pnt>();
290       if (aOrig->isEqual(aPnt)) {
291         aToDelFeatures.append(aConstrFeature);
292       } else {
293         aPnt = PartSet_Tools::getPoint(aConstrFeature,
294                                        SketchPlugin_ConstraintCoincidence::ENTITY_B());
295         if (aPnt.get() == NULL)
296           return;
297         aP = aPnt->impl<gp_Pnt>();
298         if (aOrig->isEqual(aPnt)) {
299           aToDelFeatures.append(aConstrFeature);
300           break;
301         }
302       }
303     }
304   }
305   if (aToDelFeatures.size() > 0) {
306     XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
307     XGUI_Workshop* aWorkshop = aConnector->workshop();
308     ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
309     if (PartSet_SketcherMgr::isNestedSketchOperation(anOperation))
310       anOperation->abort();
311
312     SessionPtr aMgr = ModelAPI_Session::get();
313
314     QString aName = tr("Detach %1").arg(aLine->data()->name().c_str());
315     aMgr->startOperation(aName.toStdString());
316     aWorkshop->deleteFeatures(aToDelFeatures);
317     aMgr->finishOperation();
318   }
319   myCoinsideLines.clear();
320 }
321
322
323 void PartSet_MenuMgr::onDetachMenuHide()
324 {
325   if (myPrevId != -1) {
326     // Restore color for previous object
327     setLineColor(myPrevId, myColor, false);
328   }
329   // Clear previous definitions
330   myPrevId = -1;
331 }
332
333   
334 void PartSet_MenuMgr::setAuxiliary(const bool isChecked)
335 {
336   ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
337
338   bool isActiveSketch = PartSet_SketcherMgr::isSketchOperation(anOperation) ||
339                         PartSet_SketcherMgr::isNestedSketchOperation(anOperation);
340   if (!isActiveSketch)
341     return;
342
343   QObjectPtrList anObjects;
344   bool isUseTransaction = false;
345   // 1. change auxiliary type of a created feature
346   if (PartSet_SketcherMgr::isNestedCreateOperation(anOperation) &&
347       PartSet_SketcherMgr::isEntity(anOperation->id().toStdString()) ) {
348       ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
349                                                                (anOperation);
350       if (aFOperation)
351         anObjects.append(aFOperation->feature());
352   }
353   else {
354     isUseTransaction = true;
355     // 2. change auxiliary type of selected sketch entities
356     ModuleBase_ISelection* aSelection = myModule->workshop()->selection();
357     anObjects = aSelection->selectedPresentations();
358   }
359
360   QAction* anAction = action("AUXILIARY_CMD");
361   SessionPtr aMgr = ModelAPI_Session::get();
362   if (isUseTransaction) {
363     if (PartSet_SketcherMgr::isNestedSketchOperation(anOperation))
364       anOperation->abort();
365     aMgr->startOperation(anAction->text().toStdString());
366   }
367   myModule->sketchMgr()->storeSelection();
368
369   if (anObjects.size() > 0) {
370     QObjectPtrList::const_iterator anIt = anObjects.begin(), aLast = anObjects.end();
371     for (; anIt != aLast; anIt++) {
372       FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
373       if (aFeature.get() != NULL) {
374         std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
375                             std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
376         if (aSketchFeature.get() != NULL) {
377           std::string anAttribute = SketchPlugin_SketchEntity::AUXILIARY_ID();
378
379           std::shared_ptr<ModelAPI_AttributeBoolean> anAuxiliaryAttr = 
380             std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(aSketchFeature->data()->attribute(anAttribute));
381           if (anAuxiliaryAttr)
382             anAuxiliaryAttr->setValue(isChecked);
383         }
384       }
385     }
386   }
387   if (isUseTransaction) {
388     aMgr->finishOperation();
389     XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
390     XGUI_Workshop* aWorkshop = aConnector->workshop();
391     aWorkshop->updateCommandStatus();
392   }
393
394   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
395   myModule->sketchMgr()->restoreSelection();
396 }
397
398 bool PartSet_MenuMgr::canSetAuxiliary(bool& theValue) const
399 {
400   bool anEnabled = false;
401   ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
402
403   bool isActiveSketch = PartSet_SketcherMgr::isSketchOperation(anOperation) ||
404                         PartSet_SketcherMgr::isNestedSketchOperation(anOperation);
405   if (!isActiveSketch)
406     return anEnabled;
407
408   QObjectPtrList anObjects;
409   // 1. change auxiliary type of a created feature
410   if (PartSet_SketcherMgr::isNestedCreateOperation(anOperation) &&
411     PartSet_SketcherMgr::isEntity(anOperation->id().toStdString()) ) {
412     ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(anOperation);
413     if (aFOperation)
414       anObjects.append(aFOperation->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 }
449
450 void PartSet_MenuMgr::onActivatePart(bool)
451 {
452   if (myModule->workshop()->currentOperation())
453     return;
454   QObjectPtrList aObjects = myModule->workshop()->selection()->selectedObjects();
455   if (aObjects.size() > 0) {
456     ObjectPtr aObj = aObjects.first();
457     ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aObj);
458     if (!aPart.get()) {
459       FeaturePtr aPartFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObj);
460       if (aPartFeature.get() && (aPartFeature->getKind() == PartSetPlugin_Part::ID())) {
461         aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aPartFeature->firstResult());
462       }
463     }
464     if (aPart.get())
465       aPart->activate();
466   }
467 }
468
469 void PartSet_MenuMgr::onActivatePartSet(bool)
470 {
471   if (myModule->workshop()->currentOperation())
472     return;
473   SessionPtr aMgr = ModelAPI_Session::get();
474   bool isNewTransaction = !aMgr->isOperation();
475   // activation may cause changes in current features in document, so it must be in transaction
476   if (isNewTransaction) {
477     aMgr->startOperation("Activation");
478   }
479   aMgr->setActiveDocument(aMgr->moduleDocument());
480   if (isNewTransaction) {
481     aMgr->finishOperation();
482   }
483 }
484
485 void PartSet_MenuMgr::onEdit(bool)
486 {
487   QObjectPtrList aObjects = myModule->workshop()->selection()->selectedObjects();
488   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObjects.first());
489   if (aFeature == NULL) {
490     ResultParameterPtr aParam = 
491       std::dynamic_pointer_cast<ModelAPI_ResultParameter>(aObjects.first());
492     if (aParam.get() != NULL) {
493       aFeature = ModelAPI_Feature::feature(aParam);
494     }
495   }
496   if (aFeature.get() != NULL)
497     myModule->editFeature(aFeature);
498 }
499
500 void PartSet_MenuMgr::onSelectParentFeature()
501 {
502   QObjectPtrList aObjects = myModule->workshop()->selection()->selectedObjects();
503   if (aObjects.size() != 1)
504     return;
505
506   SessionPtr aMgr = ModelAPI_Session::get();
507   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>( aObjects.first() );
508   if( !aResult.get() )
509     return;
510
511   FeaturePtr aParentFeature = aResult->document()->feature( aResult );
512   QObjectPtrList aSelection;
513   aSelection.append( aParentFeature );
514   myModule->workshop()->selection()->setSelectedObjects( aSelection );
515 }