Salome HOME
A sketcher operations manager is created
[modules/shaper.git] / src / PartSet / PartSet_SketcherMgr.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:        PartSet_SketcherMgr.cpp
4 // Created:     19 Dec 2014
5 // Author:      Vitaly SMETANNIKOV
6
7 #include "PartSet_SketcherMgr.h"
8 #include "PartSet_Module.h"
9 #include "PartSet_WidgetPoint2D.h"
10 #include "PartSet_Tools.h"
11
12 #include <XGUI_ModuleConnector.h>
13 #include <XGUI_Displayer.h>
14 #include <XGUI_Workshop.h>
15
16 #include <ModuleBase_IViewer.h>
17 #include <ModuleBase_IWorkshop.h>
18 #include <ModuleBase_IViewWindow.h>
19 #include <ModuleBase_Operation.h>
20 #include <ModuleBase_ISelection.h>
21 #include <ModuleBase_IPropertyPanel.h>
22 #include <ModuleBase_Operation.h>
23
24 #include <Events_Loop.h>
25
26 #include <SketchPlugin_Line.h>
27 #include <SketchPlugin_Sketch.h>
28 #include <SketchPlugin_Point.h>
29 #include <SketchPlugin_Arc.h>
30 #include <SketchPlugin_Circle.h>
31 #include <SketchPlugin_ConstraintLength.h>
32 #include <SketchPlugin_ConstraintDistance.h>
33 #include <SketchPlugin_ConstraintParallel.h>
34 #include <SketchPlugin_ConstraintPerpendicular.h>
35 #include <SketchPlugin_ConstraintRadius.h>
36 #include <SketchPlugin_ConstraintRigid.h>
37
38 #include <ModelAPI_Events.h>
39
40 #include <QMouseEvent>
41 #include <QApplication>
42
43
44
45
46 /// Returns list of unique objects by sum of objects from List1 and List2
47 QList<ObjectPtr> getSumList(const QList<ModuleBase_ViewerPrs>& theList1,
48                                        const QList<ModuleBase_ViewerPrs>& theList2)
49 {
50   QList<ObjectPtr> aRes;
51   foreach (ModuleBase_ViewerPrs aPrs, theList1) {
52     if (!aRes.contains(aPrs.object()))
53       aRes.append(aPrs.object());
54   }
55   foreach (ModuleBase_ViewerPrs aPrs, theList2) {
56     if (!aRes.contains(aPrs.object()))
57       aRes.append(aPrs.object());
58   }
59   return aRes;
60 }
61
62
63
64
65 PartSet_SketcherMgr::PartSet_SketcherMgr(PartSet_Module* theModule)
66   : QObject(theModule), myModule(theModule), myIsDragging(false), myDragDone(false)
67 {
68   ModuleBase_IWorkshop* aWorkshop = myModule->workshop();
69   ModuleBase_IViewer* aViewer = aWorkshop->viewer();
70
71   connect(aViewer, SIGNAL(mousePress(ModuleBase_IViewWindow*, QMouseEvent*)),
72           this, SLOT(onMousePressed(ModuleBase_IViewWindow*, QMouseEvent*)));
73
74   connect(aViewer, SIGNAL(mouseRelease(ModuleBase_IViewWindow*, QMouseEvent*)),
75           this, SLOT(onMouseReleased(ModuleBase_IViewWindow*, QMouseEvent*)));
76
77   connect(aViewer, SIGNAL(mouseMove(ModuleBase_IViewWindow*, QMouseEvent*)),
78           this, SLOT(onMouseMoved(ModuleBase_IViewWindow*, QMouseEvent*)));
79
80   connect(aViewer, SIGNAL(mouseDoubleClick(ModuleBase_IViewWindow*, QMouseEvent*)),
81           this, SLOT(onMouseDoubleClick(ModuleBase_IViewWindow*, QMouseEvent*)));
82 }
83
84 PartSet_SketcherMgr::~PartSet_SketcherMgr()
85 {
86   if (!myPlaneFilter.IsNull())
87     myPlaneFilter.Nullify();
88 }
89
90 void PartSet_SketcherMgr::onMousePressed(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
91 {
92   // 
93   if (!(theEvent->buttons() & Qt::LeftButton))
94     return;
95
96   ModuleBase_IWorkshop* aWorkshop = myModule->workshop();
97   ModuleBase_Operation* aOperation = aWorkshop->currentOperation();
98   // Use only for sketch operations
99   if (aOperation && myCurrentSketch) {
100     if (!PartSet_Tools::sketchPlane(myCurrentSketch))
101       return;
102
103     bool isSketcher = (aOperation->id().toStdString() == SketchPlugin_Sketch::ID());
104     bool isSketchOpe = sketchOperationIdList().contains(aOperation->id());
105
106     // Avoid non-sketch operations
107     if ((!isSketchOpe) && (!isSketcher))
108       return;
109
110     bool isEditing = aOperation->isEditOperation();
111
112     // Ignore creation sketch operation
113     if ((!isSketcher) && (!isEditing))
114       return;
115
116     if (theEvent->modifiers()) {
117       // If user performs multiselection
118       if (isSketchOpe /* && (!isSketcher)*/)
119         if (!aOperation->commit())
120           aOperation->abort();
121       return;
122     }
123     // Remember highlighted objects for editing
124     ModuleBase_ISelection* aSelect = aWorkshop->selection();
125     QList<ModuleBase_ViewerPrs> aHighlighted = aSelect->getHighlighted();
126     QList<ModuleBase_ViewerPrs> aSelected = aSelect->getSelected();
127     myEditingFeatures.clear();
128     myEditingAttr.clear();
129     if ((aHighlighted.size() == 0) && (aSelected.size() == 0)) {
130       if (isSketchOpe && (!isSketcher))
131         // commit previous operation
132         if (!aOperation->commit())
133           aOperation->abort();
134       return;
135     }
136
137     QObjectPtrList aSelObjects = getSumList(aHighlighted, aSelected);
138     //foreach (ModuleBase_ViewerPrs aPrs, aHighlighted) {
139     //  aSelObjects.append(aPrs.object());
140     //}
141
142     if ((aHighlighted.size() == 1) && (aSelected.size() == 0)) {
143       // Move by selected shape (vertex). Can be used only for single selection
144       foreach(ModuleBase_ViewerPrs aPrs, aHighlighted) {
145         FeaturePtr aFeature = ModelAPI_Feature::feature(aHighlighted.first().object());
146         if (aFeature) {
147           myEditingFeatures.append(aFeature);
148           TopoDS_Shape aShape = aPrs.shape();
149           if (!aShape.IsNull()) {
150             if (aShape.ShapeType() == TopAbs_VERTEX) {
151               AttributePtr aAttr = PartSet_Tools::findAttributeBy2dPoint(myEditingFeatures.first(), 
152                                                                          aShape, myCurrentSketch);
153               if (aAttr)
154                 myEditingAttr.append(aAttr);
155             }
156           }
157         }
158       }
159     } else {
160       // Provide multi-selection. Can be used only for features
161       foreach (ObjectPtr aObj, aSelObjects) {
162         FeaturePtr aFeature = ModelAPI_Feature::feature(aObj);
163         if (aFeature && (!myEditingFeatures.contains(aFeature)))
164           myEditingFeatures.append(aFeature);
165       }
166
167     }
168     // If nothing highlighted - return
169     if (myEditingFeatures.size() == 0)
170       return;
171
172     if (isSketcher) {
173       myIsDragging = true;
174       get2dPoint(theWnd, theEvent, myCurX, myCurY);
175       myDragDone = false;
176       aWorkshop->viewer()->enableMultiselection(false);
177       launchEditing();
178
179     } else if (isSketchOpe && isEditing) {
180       // If selected another object
181       aOperation->abort();
182
183       myIsDragging = true;
184       get2dPoint(theWnd, theEvent, myCurX, myCurY);
185       myDragDone = false;
186       aWorkshop->viewer()->enableMultiselection(false);
187
188       // This is necessary in order to finalize previous operation
189       QApplication::processEvents();
190       launchEditing();
191     }
192   }
193 }
194
195 void PartSet_SketcherMgr::onMouseReleased(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
196 {
197   ModuleBase_IWorkshop* aWorkshop = myModule->workshop();
198   ModuleBase_Operation* aOp = aWorkshop->currentOperation();
199   if (!aOp)
200     return;
201   if (!sketchOperationIdList().contains(aOp->id()))
202     return;
203
204   // Only for sketcher operations
205   ModuleBase_IViewer* aViewer = aWorkshop->viewer();
206   if (myIsDragging) {
207     myIsDragging = false;
208     if (myDragDone) {
209       aViewer->enableMultiselection(true);
210       aOp->commit();
211       myEditingFeatures.clear();
212       myEditingAttr.clear();
213       return;
214     }
215   }
216   if (!aViewer->isMultiSelectionEnabled()) {
217     aViewer->enableMultiselection(true);
218   }
219   aViewer->AISContext()->MoveTo(theEvent->x(), theEvent->y(), theWnd->v3dView());
220   if (theEvent->modifiers() & Qt::ShiftModifier)
221     aViewer->AISContext()->ShiftSelect();
222   else
223     aViewer->AISContext()->Select();
224 }
225
226 void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
227 {
228   if (myIsDragging) {
229     ModuleBase_Operation* aOperation = myModule->workshop()->currentOperation();
230     if (aOperation->id().toStdString() == SketchPlugin_Sketch::ID())
231       return; // No edit operation activated
232
233     static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_MOVED);
234     Handle(V3d_View) aView = theWnd->v3dView();
235     gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), aView);
236     double aX, aY;
237     PartSet_Tools::convertTo2D(aPoint, myCurrentSketch, aView, aX, aY);
238     double dX =  aX - myCurX;
239     double dY =  aY - myCurY;
240
241     if ((aOperation->id().toStdString() == SketchPlugin_Line::ID()) &&
242         (myEditingAttr.size() == 1) && 
243         myEditingAttr.first()) {
244       // probably we have prehighlighted point
245       AttributePtr aAttr = myEditingAttr.first();
246       std::string aAttrId = aAttr->id();
247       ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
248       QList<ModuleBase_ModelWidget*> aWidgets = aPanel->modelWidgets();
249       // Find corresponded widget to provide dragging
250       foreach (ModuleBase_ModelWidget* aWgt, aWidgets) {
251         if (aWgt->attributeID() == aAttrId) {
252           PartSet_WidgetPoint2D* aWgt2d = dynamic_cast<PartSet_WidgetPoint2D*>(aWgt);
253           if (aWgt2d) {
254             aWgt2d->setPoint(aWgt2d->x() + dX, aWgt2d->y() + dY);
255             break;
256           }
257         }
258       }
259     } else {
260       foreach(FeaturePtr aFeature, myEditingFeatures) {
261         std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
262           std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
263         if (aSketchFeature) { 
264           aSketchFeature->move(dX, dY);
265           ModelAPI_EventCreator::get()->sendUpdated(aSketchFeature, anEvent);
266         }
267       }
268       Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_MOVED));
269       Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
270     }
271     myDragDone = true;
272     myCurX = aX;
273     myCurY = aY;
274   }
275 }
276
277 void PartSet_SketcherMgr::onMouseDoubleClick(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
278 {
279   ModuleBase_Operation* aOperation = myModule->workshop()->currentOperation();
280   if (aOperation && aOperation->isEditOperation()) {
281     std::string aId = aOperation->id().toStdString();
282     if ((aId == SketchPlugin_ConstraintLength::ID()) ||
283       (aId == SketchPlugin_ConstraintDistance::ID()) ||
284       (aId == SketchPlugin_ConstraintRadius::ID())) 
285     {
286       // Activate dimension value editing on double click
287       ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
288       QList<ModuleBase_ModelWidget*> aWidgets = aPanel->modelWidgets();
289       // Find corresponded widget to activate value editing
290       foreach (ModuleBase_ModelWidget* aWgt, aWidgets) {
291         if (aWgt->attributeID() == "ConstraintValue") {
292           aWgt->focusTo();
293           return;
294         }
295       }
296     }
297   }
298 }
299
300 void PartSet_SketcherMgr::get2dPoint(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent, 
301                                 double& theX, double& theY)
302 {
303   Handle(V3d_View) aView = theWnd->v3dView();
304   gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), aView);
305   PartSet_Tools::convertTo2D(aPoint, myCurrentSketch, aView, theX, theY);
306 }
307
308 void PartSet_SketcherMgr::launchEditing()
309 {
310   if (myEditingFeatures.size() > 0) {
311     FeaturePtr aFeature = myEditingFeatures.first();
312     std::shared_ptr<SketchPlugin_Feature> aSPFeature = 
313               std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
314     if (aSPFeature) {
315       myModule->editFeature(aSPFeature);
316     }
317   }
318 }
319
320
321 QStringList PartSet_SketcherMgr::sketchOperationIdList()
322 {
323   static QStringList aIds;
324   if (aIds.size() == 0) {
325     aIds << SketchPlugin_Line::ID().c_str();
326     aIds << SketchPlugin_Point::ID().c_str();
327     aIds << SketchPlugin_Arc::ID().c_str();
328     aIds << SketchPlugin_Circle::ID().c_str();
329     aIds << SketchPlugin_ConstraintLength::ID().c_str();
330     aIds << SketchPlugin_ConstraintDistance::ID().c_str();
331     aIds << SketchPlugin_ConstraintRigid::ID().c_str();
332     aIds << SketchPlugin_ConstraintRadius::ID().c_str();
333     aIds << SketchPlugin_ConstraintPerpendicular::ID().c_str();
334     aIds << SketchPlugin_ConstraintParallel::ID().c_str();
335   }
336   return aIds;
337 }
338
339 void PartSet_SketcherMgr::startSketch(ModuleBase_Operation* theOperation)
340 {
341   // Display all sketcher sub-Objects
342   myCurrentSketch = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theOperation->feature());
343   XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
344   XGUI_Displayer* aDisplayer = aConnector->workshop()->displayer();
345
346   // Hide sketcher result
347   std::list<ResultPtr> aResults = myCurrentSketch->results();
348   std::list<ResultPtr>::const_iterator aIt;
349   for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
350     aDisplayer->erase((*aIt), false);
351   }
352   aDisplayer->erase(myCurrentSketch, false);
353
354   // Display sketcher objects
355   for (int i = 0; i < myCurrentSketch->numberOfSubs(); i++) {
356     FeaturePtr aFeature = myCurrentSketch->subFeature(i);
357     std::list<ResultPtr> aResults = aFeature->results();
358     std::list<ResultPtr>::const_iterator aIt;
359     for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
360       aDisplayer->display((*aIt), false);
361     }
362     aDisplayer->display(aFeature, false);
363   }
364
365   if (myPlaneFilter.IsNull()) 
366     myPlaneFilter = new ModuleBase_ShapeInPlaneFilter();
367
368   myModule->workshop()->viewer()->addSelectionFilter(myPlaneFilter);
369   if (theOperation->isEditOperation()) {
370     // If it is editing of sketch then it means that plane is already defined
371     std::shared_ptr<GeomAPI_Pln> aPln = PartSet_Tools::sketchPlane(myCurrentSketch);
372     myPlaneFilter->setPlane(aPln->impl<gp_Pln>());
373   }
374   aDisplayer->updateViewer();
375 }
376
377 void PartSet_SketcherMgr::stopSketch(ModuleBase_Operation* theOperation)
378 {
379   DataPtr aData = myCurrentSketch->data();
380   if ((!aData) || (!aData->isValid())) {
381     // The sketch was aborted
382     myCurrentSketch = CompositeFeaturePtr();
383     return; 
384   }
385   // Hide all sketcher sub-Objects
386   XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
387   XGUI_Displayer* aDisplayer = aConnector->workshop()->displayer();
388   for (int i = 0; i < myCurrentSketch->numberOfSubs(); i++) {
389     FeaturePtr aFeature = myCurrentSketch->subFeature(i);
390     std::list<ResultPtr> aResults = aFeature->results();
391     std::list<ResultPtr>::const_iterator aIt;
392     for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
393       aDisplayer->erase((*aIt), false);
394     }
395     aDisplayer->erase(aFeature, false);
396   }
397   // Display sketcher result
398   std::list<ResultPtr> aResults = myCurrentSketch->results();
399   std::list<ResultPtr>::const_iterator aIt;
400   for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
401     aDisplayer->display((*aIt), false);
402   }
403   aDisplayer->display(myCurrentSketch);
404     
405   myCurrentSketch = CompositeFeaturePtr();
406   myModule->workshop()->viewer()->removeSelectionFilter(myPlaneFilter);
407   aDisplayer->updateViewer();
408 }
409
410
411 void PartSet_SketcherMgr::onPlaneSelected(const std::shared_ptr<GeomAPI_Pln>& thePln)
412 {
413   myPlaneFilter->setPlane(thePln->impl<gp_Pln>());
414 }