Salome HOME
Merge branch 'Dev_0.6.1' of newgeom:newgeom into Dev_0.6.1
[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
130     bool aHasShift = (theEvent->modifiers() & Qt::ShiftModifier);
131     QObjectPtrList aSelObjects;
132     if (aHasShift)
133       aSelObjects = getSumList(aHighlighted, aSelected);
134     else {
135       foreach (ModuleBase_ViewerPrs aPrs, aHighlighted) {
136         aSelObjects.append(aPrs.object());
137       }
138     }
139     if ((aSelObjects.size() == 0)) {
140       if (isSketchOpe && (!isSketcher))
141         // commit previous operation
142         if (!aOperation->commit())
143           aOperation->abort();
144       return;
145     }
146     if ((aHighlighted.size() == 1) && (aSelected.size() == 0)) {
147       // Move by selected shape (vertex). Can be used only for single selection
148       foreach(ModuleBase_ViewerPrs aPrs, aHighlighted) {
149         FeaturePtr aFeature = ModelAPI_Feature::feature(aHighlighted.first().object());
150         if (aFeature) {
151           myEditingFeatures.append(aFeature);
152           TopoDS_Shape aShape = aPrs.shape();
153           if (!aShape.IsNull()) {
154             if (aShape.ShapeType() == TopAbs_VERTEX) {
155               AttributePtr aAttr = PartSet_Tools::findAttributeBy2dPoint(myEditingFeatures.first(), 
156                                                                          aShape, myCurrentSketch);
157               if (aAttr)
158                 myEditingAttr.append(aAttr);
159             }
160           }
161         }
162       }
163     } else {
164       // Provide multi-selection. Can be used only for features
165       foreach (ObjectPtr aObj, aSelObjects) {
166         FeaturePtr aFeature = ModelAPI_Feature::feature(aObj);
167         if (aFeature && (!myEditingFeatures.contains(aFeature)))
168           myEditingFeatures.append(aFeature);
169       }
170
171     }
172     // If nothing highlighted - return
173     if (myEditingFeatures.size() == 0)
174       return;
175
176     if (isSketcher) {
177       myIsDragging = true;
178       get2dPoint(theWnd, theEvent, myCurX, myCurY);
179       myDragDone = false;
180       aWorkshop->viewer()->enableMultiselection(false);
181       launchEditing();
182
183     } else if (isSketchOpe && isEditing) {
184       // If selected another object
185       aOperation->abort();
186
187       myIsDragging = true;
188       get2dPoint(theWnd, theEvent, myCurX, myCurY);
189       myDragDone = false;
190       aWorkshop->viewer()->enableMultiselection(false);
191
192       // This is necessary in order to finalize previous operation
193       QApplication::processEvents();
194       launchEditing();
195     }
196   }
197 }
198
199 void PartSet_SketcherMgr::onMouseReleased(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
200 {
201   ModuleBase_IWorkshop* aWorkshop = myModule->workshop();
202   ModuleBase_Operation* aOp = aWorkshop->currentOperation();
203   if (!aOp)
204     return;
205   if (!sketchOperationIdList().contains(aOp->id()))
206     return;
207
208   // Only for sketcher operations
209   ModuleBase_IViewer* aViewer = aWorkshop->viewer();
210   if (myIsDragging) {
211     myIsDragging = false;
212     if (myDragDone) {
213       aViewer->enableMultiselection(true);
214       aOp->commit();
215       myEditingFeatures.clear();
216       myEditingAttr.clear();
217       return;
218     }
219   }
220   if (!aViewer->isMultiSelectionEnabled()) {
221     aViewer->enableMultiselection(true);
222   }
223   //aViewer->AISContext()->MoveTo(theEvent->x(), theEvent->y(), theWnd->v3dView());
224   //if (theEvent->modifiers() & Qt::ShiftModifier)
225   //  aViewer->AISContext()->ShiftSelect();
226   //else
227   //  aViewer->AISContext()->Select();
228 }
229
230 void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
231 {
232   if (myIsDragging) {
233     ModuleBase_Operation* aOperation = myModule->workshop()->currentOperation();
234     if (aOperation->id().toStdString() == SketchPlugin_Sketch::ID())
235       return; // No edit operation activated
236
237     static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_MOVED);
238     Handle(V3d_View) aView = theWnd->v3dView();
239     gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), aView);
240     double aX, aY;
241     PartSet_Tools::convertTo2D(aPoint, myCurrentSketch, aView, aX, aY);
242     double dX =  aX - myCurX;
243     double dY =  aY - myCurY;
244
245     if ((aOperation->id().toStdString() == SketchPlugin_Line::ID()) &&
246         (myEditingAttr.size() == 1) && 
247         myEditingAttr.first()) {
248       // probably we have prehighlighted point
249       AttributePtr aAttr = myEditingAttr.first();
250       std::string aAttrId = aAttr->id();
251       ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
252       QList<ModuleBase_ModelWidget*> aWidgets = aPanel->modelWidgets();
253       // Find corresponded widget to provide dragging
254       foreach (ModuleBase_ModelWidget* aWgt, aWidgets) {
255         if (aWgt->attributeID() == aAttrId) {
256           PartSet_WidgetPoint2D* aWgt2d = dynamic_cast<PartSet_WidgetPoint2D*>(aWgt);
257           if (aWgt2d) {
258             aWgt2d->setPoint(aWgt2d->x() + dX, aWgt2d->y() + dY);
259             break;
260           }
261         }
262       }
263     } else {
264       foreach(FeaturePtr aFeature, myEditingFeatures) {
265         std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
266           std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
267         if (aSketchFeature) { 
268           aSketchFeature->move(dX, dY);
269           ModelAPI_EventCreator::get()->sendUpdated(aSketchFeature, anEvent);
270         }
271       }
272       Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_MOVED));
273       Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
274     }
275     myDragDone = true;
276     myCurX = aX;
277     myCurY = aY;
278   }
279 }
280
281 void PartSet_SketcherMgr::onMouseDoubleClick(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
282 {
283   ModuleBase_Operation* aOperation = myModule->workshop()->currentOperation();
284   if (aOperation && aOperation->isEditOperation()) {
285     std::string aId = aOperation->id().toStdString();
286     if ((aId == SketchPlugin_ConstraintLength::ID()) ||
287       (aId == SketchPlugin_ConstraintDistance::ID()) ||
288       (aId == SketchPlugin_ConstraintRadius::ID())) 
289     {
290       // Activate dimension value editing on double click
291       ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
292       QList<ModuleBase_ModelWidget*> aWidgets = aPanel->modelWidgets();
293       // Find corresponded widget to activate value editing
294       foreach (ModuleBase_ModelWidget* aWgt, aWidgets) {
295         if (aWgt->attributeID() == "ConstraintValue") {
296           aWgt->focusTo();
297           return;
298         }
299       }
300     }
301   }
302 }
303
304 void PartSet_SketcherMgr::get2dPoint(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent, 
305                                 double& theX, double& theY)
306 {
307   Handle(V3d_View) aView = theWnd->v3dView();
308   gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), aView);
309   PartSet_Tools::convertTo2D(aPoint, myCurrentSketch, aView, theX, theY);
310 }
311
312 void PartSet_SketcherMgr::launchEditing()
313 {
314   if (myEditingFeatures.size() > 0) {
315     FeaturePtr aFeature = myEditingFeatures.first();
316     std::shared_ptr<SketchPlugin_Feature> aSPFeature = 
317               std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
318     if (aSPFeature) {
319       myModule->editFeature(aSPFeature);
320     }
321   }
322 }
323
324
325 QStringList PartSet_SketcherMgr::sketchOperationIdList()
326 {
327   static QStringList aIds;
328   if (aIds.size() == 0) {
329     aIds << SketchPlugin_Line::ID().c_str();
330     aIds << SketchPlugin_Point::ID().c_str();
331     aIds << SketchPlugin_Arc::ID().c_str();
332     aIds << SketchPlugin_Circle::ID().c_str();
333     aIds << SketchPlugin_ConstraintLength::ID().c_str();
334     aIds << SketchPlugin_ConstraintDistance::ID().c_str();
335     aIds << SketchPlugin_ConstraintRigid::ID().c_str();
336     aIds << SketchPlugin_ConstraintRadius::ID().c_str();
337     aIds << SketchPlugin_ConstraintPerpendicular::ID().c_str();
338     aIds << SketchPlugin_ConstraintParallel::ID().c_str();
339   }
340   return aIds;
341 }
342
343 void PartSet_SketcherMgr::startSketch(ModuleBase_Operation* theOperation)
344 {
345   // Display all sketcher sub-Objects
346   myCurrentSketch = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theOperation->feature());
347   XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
348   XGUI_Displayer* aDisplayer = aConnector->workshop()->displayer();
349
350   // Hide sketcher result
351   std::list<ResultPtr> aResults = myCurrentSketch->results();
352   std::list<ResultPtr>::const_iterator aIt;
353   for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
354     aDisplayer->erase((*aIt), false);
355   }
356   aDisplayer->erase(myCurrentSketch, false);
357
358   // Display sketcher objects
359   for (int i = 0; i < myCurrentSketch->numberOfSubs(); i++) {
360     FeaturePtr aFeature = myCurrentSketch->subFeature(i);
361     std::list<ResultPtr> aResults = aFeature->results();
362     std::list<ResultPtr>::const_iterator aIt;
363     for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
364       aDisplayer->display((*aIt), false);
365     }
366     aDisplayer->display(aFeature, false);
367   }
368
369   if (myPlaneFilter.IsNull()) 
370     myPlaneFilter = new ModuleBase_ShapeInPlaneFilter();
371
372   myModule->workshop()->viewer()->addSelectionFilter(myPlaneFilter);
373   if (theOperation->isEditOperation()) {
374     // If it is editing of sketch then it means that plane is already defined
375     std::shared_ptr<GeomAPI_Pln> aPln = PartSet_Tools::sketchPlane(myCurrentSketch);
376     myPlaneFilter->setPlane(aPln->impl<gp_Pln>());
377   }
378   aDisplayer->updateViewer();
379 }
380
381 void PartSet_SketcherMgr::stopSketch(ModuleBase_Operation* theOperation)
382 {
383   DataPtr aData = myCurrentSketch->data();
384   if ((!aData) || (!aData->isValid())) {
385     // The sketch was aborted
386     myCurrentSketch = CompositeFeaturePtr();
387     myModule->workshop()->viewer()->removeSelectionFilter(myPlaneFilter);
388     return; 
389   }
390   // Hide all sketcher sub-Objects
391   XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
392   XGUI_Displayer* aDisplayer = aConnector->workshop()->displayer();
393   for (int i = 0; i < myCurrentSketch->numberOfSubs(); i++) {
394     FeaturePtr aFeature = myCurrentSketch->subFeature(i);
395     std::list<ResultPtr> aResults = aFeature->results();
396     std::list<ResultPtr>::const_iterator aIt;
397     for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
398       aDisplayer->erase((*aIt), false);
399     }
400     aDisplayer->erase(aFeature, false);
401   }
402   // Display sketcher result
403   std::list<ResultPtr> aResults = myCurrentSketch->results();
404   std::list<ResultPtr>::const_iterator aIt;
405   for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
406     aDisplayer->display((*aIt), false);
407   }
408   aDisplayer->display(myCurrentSketch);
409     
410   myCurrentSketch = CompositeFeaturePtr();
411   myModule->workshop()->viewer()->removeSelectionFilter(myPlaneFilter);
412   aDisplayer->updateViewer();
413 }
414
415
416 void PartSet_SketcherMgr::onPlaneSelected(const std::shared_ptr<GeomAPI_Pln>& thePln)
417 {
418   myPlaneFilter->setPlane(thePln->impl<gp_Pln>());
419 }