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