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