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