Salome HOME
Reset value state is provided in ModelWidget to remove 'myIsResetCurrentValue' in...
[modules/shaper.git] / src / PartSet / PartSet_WidgetPoint2d.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:        PartSet_WidgetPoint2D.cpp
4 // Created:     25 Apr 2014
5 // Author:      Natalia ERMOLAEVA
6
7 #include "PartSet_WidgetPoint2d.h"
8 #include <PartSet_Tools.h>
9 #include <PartSet_Module.h>
10 #include <PartSet_LockApplyMgr.h>
11
12 #include <ModuleBase_ParamSpinBox.h>
13 #include <ModuleBase_Tools.h>
14 #include <ModuleBase_IViewer.h>
15 #include <ModuleBase_IViewWindow.h>
16 #include <ModuleBase_ISelection.h>
17
18 #include <Config_Keywords.h>
19 #include <Config_WidgetAPI.h>
20
21 #include <Events_Loop.h>
22 #include <ModelAPI_Events.h>
23
24 #include <ModelAPI_Feature.h>
25 #include <ModelAPI_Data.h>
26 #include <ModelAPI_Object.h>
27 #include <GeomDataAPI_Point2D.h>
28 #include <GeomAPI_Pnt2d.h>
29
30 #include <SketchPlugin_Feature.h>
31 #include <SketchPlugin_ConstraintCoincidence.h>
32 #include <SketchPlugin_Line.h>
33 #include <SketchPlugin_Arc.h>
34 #include <SketchPlugin_Circle.h>
35 #include <SketchPlugin_Point.h>
36
37 #include <QGroupBox>
38 #include <QGridLayout>
39 #include <QLabel>
40 #include <QEvent>
41 #include <QMouseEvent>
42 #include <QApplication>
43
44 #include <TopoDS.hxx>
45 #include <TopoDS_Vertex.hxx>
46 #include <BRep_Tool.hxx>
47
48 #include <cfloat>
49 #include <climits>
50
51 const double MaxCoordinate = 1e12;
52
53 static QStringList MyFeaturesForCoincedence;
54
55 #define APPLY_BY_ENTER_OR_TAB
56
57 PartSet_WidgetPoint2D::PartSet_WidgetPoint2D(QWidget* theParent, 
58                                              ModuleBase_IWorkshop* theWorkshop,
59                                              const Config_WidgetAPI* theData,
60                                              const std::string& theParentId)
61  : ModuleBase_ModelWidget(theParent, theData, theParentId), myWorkshop(theWorkshop)
62 {
63   if (MyFeaturesForCoincedence.isEmpty()) {
64     MyFeaturesForCoincedence << SketchPlugin_Line::ID().c_str()
65       << SketchPlugin_Arc::ID().c_str()
66       << SketchPlugin_Point::ID().c_str()
67       << SketchPlugin_Circle::ID().c_str();
68   }
69   myLockApplyMgr = new PartSet_LockApplyMgr(theParent, myWorkshop);
70
71   // the control should accept the focus, so the boolen flag is corrected to be true
72   myIsObligatory = true;
73   //myOptionParam = theData->getProperty(PREVIOUS_FEATURE_PARAM);
74   QString aPageName = QString::fromStdString(theData->getProperty(CONTAINER_PAGE_NAME));
75   myGroupBox = new QGroupBox(aPageName, theParent);
76   myGroupBox->setFlat(false);
77
78   QGridLayout* aGroupLay = new QGridLayout(myGroupBox);
79   ModuleBase_Tools::adjustMargins(aGroupLay);
80   aGroupLay->setSpacing(2);
81   aGroupLay->setColumnStretch(1, 1);
82   {
83     QLabel* aLabel = new QLabel(myGroupBox);
84     aLabel->setText(tr("X "));
85     aGroupLay->addWidget(aLabel, 0, 0);
86
87     myXSpin = new ModuleBase_ParamSpinBox(myGroupBox);
88     myXSpin->setMinimum(-DBL_MAX);
89     myXSpin->setMaximum(DBL_MAX);
90     myXSpin->setToolTip(tr("X"));
91     aGroupLay->addWidget(myXSpin, 0, 1);
92
93 #ifdef APPLY_BY_ENTER_OR_TAB
94     // Apply widget value change by enter/tab event.
95     connect(myXSpin, SIGNAL(editingFinished()), this, SLOT(onValuesChanged()));
96     connect(myXSpin, SIGNAL(valueChanged(const QString&)), this, SIGNAL(valuesModified()));
97 #else
98     connect(myXSpin, SIGNAL(valueChanged(const QString&)), this, SLOT(onValuesChanged()));
99 #endif
100   }
101   {
102     QLabel* aLabel = new QLabel(myGroupBox);
103     aLabel->setText(tr("Y "));
104     aGroupLay->addWidget(aLabel, 1, 0);
105
106     myYSpin = new ModuleBase_ParamSpinBox(myGroupBox);
107     myYSpin->setMinimum(-DBL_MAX);
108     myYSpin->setMaximum(DBL_MAX);
109     myYSpin->setToolTip(tr("Y"));
110     aGroupLay->addWidget(myYSpin, 1, 1);
111
112 #ifdef APPLY_BY_ENTER_OR_TAB
113     // Apply widget value change by enter/tab event.
114     connect(myYSpin, SIGNAL(editingFinished()), this, SLOT(onValuesChanged()));
115     connect(myYSpin, SIGNAL(valueChanged(const QString&)), this, SIGNAL(valuesModified()));
116 #else
117     connect(myYSpin, SIGNAL(valueChanged(const QString&)), this, SLOT(onValuesChanged()));
118 #endif
119   }
120   QVBoxLayout* aLayout = new QVBoxLayout(this);
121   ModuleBase_Tools::zeroMargins(aLayout);
122   aLayout->addWidget(myGroupBox);
123   setLayout(aLayout);
124 }
125
126 bool PartSet_WidgetPoint2D::resetCustom()
127 {
128   bool aDone = false;
129   if (!isUseReset() || isComputedDefault() || myXSpin->hasVariable() || myYSpin->hasVariable()) {
130     aDone = false;
131   }
132   else {
133     bool isOk;
134     double aDefValue = QString::fromStdString(getDefaultValue()).toDouble(&isOk);
135     // it is important to block the spin box control in order to do not through out the
136     // locking of the validating state.
137     ModuleBase_Tools::setSpinValue(myXSpin, isOk ? aDefValue : 0.0);
138     ModuleBase_Tools::setSpinValue(myYSpin, isOk ? aDefValue : 0.0);
139     storeValueCustom();
140     aDone = true;
141   }
142   return aDone;
143 }
144
145 PartSet_WidgetPoint2D::~PartSet_WidgetPoint2D()
146 {
147 }
148
149 bool PartSet_WidgetPoint2D::setSelection(QList<ModuleBase_ViewerPrs>& theValues,
150                                          const bool theToValidate)
151 {
152   if (theValues.empty())
153     return false;
154
155   ModuleBase_ViewerPrs aValue = theValues.takeFirst();
156
157   Handle(V3d_View) aView = myWorkshop->viewer()->activeView();
158   bool isDone = false;
159   TopoDS_Shape aShape = aValue.shape();
160   double aX, aY;
161   if (getPoint2d(aView, aShape, aX, aY)) {
162     isDone = setPoint(aX, aY);
163   }
164   return isDone;
165 }
166
167 bool PartSet_WidgetPoint2D::setPoint(double theX, double theY)
168 {
169   if (fabs(theX) >= MaxCoordinate)
170     return false;
171   if (fabs(theY) >= MaxCoordinate)
172     return false;
173
174   ModuleBase_Tools::setSpinValue(myXSpin, theX);
175   ModuleBase_Tools::setSpinValue(myYSpin, theY);
176
177   storeValue();
178   return true;
179 }
180
181 bool PartSet_WidgetPoint2D::storeValueCustom() const
182 {
183   std::shared_ptr<ModelAPI_Data> aData = myFeature->data();
184   if (!aData) // can be on abort of sketcher element
185     return false;
186   std::shared_ptr<GeomDataAPI_Point2D> aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
187       aData->attribute(attributeID()));
188
189   PartSet_WidgetPoint2D* that = (PartSet_WidgetPoint2D*) this;
190   bool isBlocked = that->blockSignals(true);
191   bool isImmutable = aPoint->setImmutable(true);
192
193   // if text is not empty then setValue will be ignored
194   // so we should set the text at first
195   aPoint->setText(myXSpin->hasVariable() ? myXSpin->text().toStdString() : "",
196                   myYSpin->hasVariable() ? myYSpin->text().toStdString() : "");
197   aPoint->setValue(!myXSpin->hasVariable() ? myXSpin->value() : aPoint->x(),
198                    !myYSpin->hasVariable() ? myYSpin->value() : aPoint->y());
199
200   // after movement the solver will call the update event: optimization
201   moveObject(myFeature);
202   aPoint->setImmutable(isImmutable);
203   that->blockSignals(isBlocked);
204
205   return true;
206 }
207
208 bool PartSet_WidgetPoint2D::restoreValueCustom()
209 {
210   std::shared_ptr<ModelAPI_Data> aData = myFeature->data();
211   std::shared_ptr<GeomDataAPI_Point2D> aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
212       aData->attribute(attributeID()));
213   QString aTextX = QString::fromStdString(aPoint->textX());
214   QString aTextY = QString::fromStdString(aPoint->textY());
215
216   bool isDouble = false;
217   double aVal = 0;
218   if (aTextX.isEmpty()) {
219     ModuleBase_Tools::setSpinValue(myXSpin, aPoint->x());
220   } else {
221     aVal = aTextX.toDouble(&isDouble);
222     if (isDouble)
223       ModuleBase_Tools::setSpinValue(myXSpin, aVal);
224     else
225       ModuleBase_Tools::setSpinText(myXSpin, aTextX);
226   }
227   if (aTextY.isEmpty()) {
228     ModuleBase_Tools::setSpinValue(myYSpin, aPoint->y());
229   } else {
230     aVal = aTextY.toDouble(&isDouble);
231     if (isDouble)
232       ModuleBase_Tools::setSpinValue(myYSpin, aVal);
233     else
234       ModuleBase_Tools::setSpinText(myYSpin, aTextY);
235   }
236   //if (aTextX.empty() || aTextY.empty()) {
237   //  ModuleBase_Tools::setSpinValue(myXSpin, aPoint->x());
238   //  ModuleBase_Tools::setSpinValue(myYSpin, aPoint->y());
239   //} else {
240   //  ModuleBase_Tools::setSpinText(myXSpin, QString::fromStdString(aTextX));
241   //  ModuleBase_Tools::setSpinText(myYSpin, QString::fromStdString(aTextY));
242   //}
243   return true;
244 }
245
246 QList<QWidget*> PartSet_WidgetPoint2D::getControls() const
247 {
248   QList<QWidget*> aControls;
249   aControls.append(myXSpin);
250   aControls.append(myYSpin);
251   return aControls;
252 }
253
254
255 void PartSet_WidgetPoint2D::activateCustom()
256 {
257   ModuleBase_IViewer* aViewer = myWorkshop->viewer();
258   connect(aViewer, SIGNAL(mouseMove(ModuleBase_IViewWindow*, QMouseEvent*)), 
259           this, SLOT(onMouseMove(ModuleBase_IViewWindow*, QMouseEvent*)));
260   connect(aViewer, SIGNAL(mouseRelease(ModuleBase_IViewWindow*, QMouseEvent*)), 
261           this, SLOT(onMouseRelease(ModuleBase_IViewWindow*, QMouseEvent*)));
262
263   QIntList aModes;
264   aModes << TopAbs_VERTEX;
265   aModes << TopAbs_EDGE;
266   myWorkshop->activateSubShapesSelection(aModes);
267
268   myLockApplyMgr->activate();
269 }
270
271 void PartSet_WidgetPoint2D::deactivate()
272 {
273   ModuleBase_IViewer* aViewer = myWorkshop->viewer();
274   disconnect(aViewer, SIGNAL(mouseMove(ModuleBase_IViewWindow*, QMouseEvent*)),
275              this, SLOT(onMouseMove(ModuleBase_IViewWindow*, QMouseEvent*)));
276   disconnect(aViewer, SIGNAL(mouseRelease(ModuleBase_IViewWindow*, QMouseEvent*)), 
277              this, SLOT(onMouseRelease(ModuleBase_IViewWindow*, QMouseEvent*)));
278
279   myWorkshop->deactivateSubShapesSelection();
280
281   myLockApplyMgr->deactivate();
282 }
283
284 bool PartSet_WidgetPoint2D::getPoint2d(const Handle(V3d_View)& theView, 
285                                        const TopoDS_Shape& theShape, 
286                                        double& theX, double& theY) const
287 {
288   if (!theShape.IsNull()) {
289     if (theShape.ShapeType() == TopAbs_VERTEX) {
290       const TopoDS_Vertex& aVertex = TopoDS::Vertex(theShape);
291       if (!aVertex.IsNull()) {
292         // A case when point is taken from existing vertex
293         gp_Pnt aPoint = BRep_Tool::Pnt(aVertex);
294         PartSet_Tools::convertTo2D(aPoint, mySketch, theView, theX, theY);
295         return true;
296       }
297     }
298   }
299   return false;
300 }
301
302 void PartSet_WidgetPoint2D::setConstraintWith(const ObjectPtr& theObject)
303 {
304   // Create point-edge coincedence
305   FeaturePtr aFeature = mySketch->addFeature(SketchPlugin_ConstraintCoincidence::ID());
306   std::shared_ptr<ModelAPI_Data> aData = aFeature->data();
307
308   std::shared_ptr<ModelAPI_AttributeRefAttr> aRef1 = std::dynamic_pointer_cast<
309       ModelAPI_AttributeRefAttr>(aData->attribute(SketchPlugin_Constraint::ENTITY_A()));
310   AttributePtr aThisAttr = feature()->data()->attribute(attributeID());
311   std::shared_ptr<GeomDataAPI_Point2D> aThisPoint = 
312     std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aThisAttr);
313   aRef1->setAttr(aThisPoint);
314
315   std::shared_ptr<ModelAPI_AttributeRefAttr> aRef2 = std::dynamic_pointer_cast<
316       ModelAPI_AttributeRefAttr>(aData->attribute(SketchPlugin_Constraint::ENTITY_B()));
317   aRef2->setObject(theObject);
318
319   aFeature->execute();
320 }
321
322 void PartSet_WidgetPoint2D::onMouseRelease(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
323 {
324   // the contex menu release by the right button should not be processed by this widget
325   if (theEvent->button() != Qt::LeftButton)
326     return;
327
328   ModuleBase_ISelection* aSelection = myWorkshop->selection();
329   Handle(V3d_View) aView = theWnd->v3dView();
330   // TODO: This fragment doesn't work because bug in OCC Viewer. It can be used after fixing.
331   NCollection_List<TopoDS_Shape> aShapes;
332   std::list<ObjectPtr> aObjects;
333   aSelection->selectedShapes(aShapes, aObjects);
334   // if we have selection
335   if (aShapes.Extent() > 0) {
336     TopoDS_Shape aShape = aShapes.First();
337     ObjectPtr aObject = aObjects.front();
338     FeaturePtr aSelectedFeature = ModelAPI_Feature::feature(aObject);
339     bool anExternal = false;
340     if (aSelectedFeature.get() != NULL) {
341       std::shared_ptr<SketchPlugin_Feature> aSPFeature = 
342               std::dynamic_pointer_cast<SketchPlugin_Feature>(aSelectedFeature);
343       if ((!aSPFeature) && (!aShape.IsNull())) {
344         anExternal = true;
345         ResultPtr aFixedObject = PartSet_Tools::findFixedObjectByExternal(aShape, aObject, mySketch);
346         if (!aFixedObject.get())
347           aObject = PartSet_Tools::createFixedObjectByExternal(aShape, aObject, mySketch);
348
349         double aX, aY;
350         if (getPoint2d(aView, aShape, aX, aY) && isFeatureContainsPoint(myFeature, aX, aY)) {
351           // do not create a constraint to the point, which already used by the feature
352           // if the feature contains the point, focus is not switched
353           setPoint(aX, aY);
354         }
355         else {
356           if (getPoint2d(aView, aShape, aX, aY))
357             setPoint(aX, aY);
358           setConstraintWith(aObject);
359           emit vertexSelected();
360           emit focusOutWidget(this);
361         }
362       }
363     }
364     if (!anExternal) {
365       double aX, aY;
366       bool isProcessed = false;
367       if (getPoint2d(aView, aShape, aX, aY) && isFeatureContainsPoint(myFeature, aX, aY)) {
368         // when the point is selected, the coordinates of the point should be set into the attribute
369         // if the feature contains the point, focus is not switched
370         setPoint(aX, aY);
371       }
372       else {
373         // do not set a coincidence constraint in the attribute if the feature contains a point
374         // with the same coordinates. It is important for line creation in order to do not set
375         // the same constraints for the same points, oterwise the result line has zero length.
376         if (getPoint2d(aView, aShape, aX, aY)) {
377           setPoint(aX, aY);
378           PartSet_Tools::setConstraints(mySketch, feature(), attributeID(), aX, aY);
379         }
380         else if (aShape.ShapeType() == TopAbs_EDGE) {
381           if (MyFeaturesForCoincedence.contains(myFeature->getKind().c_str()))
382             setConstraintWith(aObject);
383         }
384         // it is important to perform updateObject() in order to the current value is 
385         // processed by Sketch Solver. Test case: line is created from a previous point
386         // to some distance, but in the area of the highlighting of the point. Constraint
387         // coincidence is created, after the solver is performed, the distance between the
388         // points of the line becomes less than the tolerance. Validator of the line returns
389         // false, the line will be aborted, but sketch stays valid.
390         updateObject(feature());
391         emit vertexSelected();
392         emit focusOutWidget(this);
393       }
394     }
395   }
396   // End of Bug dependent fragment
397   else {
398     // A case when point is taken from mouse event
399     gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), theWnd->v3dView());
400     double aX, anY;
401     PartSet_Tools::convertTo2D(aPoint, mySketch, aView, aX, anY);
402
403     // if the feature contains the point, focus is not switched
404     if (!setPoint(aX, anY) || isFeatureContainsPoint(myFeature, aX, anY))
405       return;
406
407     /// Start alternative code
408     //std::shared_ptr<GeomDataAPI_Point2D> aFeaturePoint = std::dynamic_pointer_cast<
409     //    GeomDataAPI_Point2D>(feature()->data()->attribute(attributeID()));
410     //QList<FeaturePtr> aIgnore;
411     //aIgnore.append(feature());
412
413     //double aTolerance = aView->Convert(7);
414     //std::shared_ptr<GeomDataAPI_Point2D> aAttrPnt = 
415     //  PartSet_Tools::findAttributePoint(mySketch, aX, anY, aTolerance, aIgnore);
416     //if (aAttrPnt.get() != NULL) {
417     //  aFeaturePoint->setValue(aAttrPnt->pnt());
418     //  PartSet_Tools::createConstraint(mySketch, aAttrPnt, aFeaturePoint);
419     //  emit vertexSelected();
420     //}
421     /// End alternative code
422     emit focusOutWidget(this);
423   }
424 }
425
426
427 void PartSet_WidgetPoint2D::onMouseMove(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
428 {
429   if (isEditingMode())
430     return;
431
432   gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), theWnd->v3dView());
433
434   double aX, anY;
435   PartSet_Tools::convertTo2D(aPoint, mySketch, theWnd->v3dView(), aX, anY);
436   setPoint(aX, anY);
437 }
438
439 double PartSet_WidgetPoint2D::x() const
440 {
441   return myXSpin->value();
442 }
443
444 double PartSet_WidgetPoint2D::y() const
445 {
446   return myYSpin->value();
447 }
448
449
450 bool PartSet_WidgetPoint2D::isFeatureContainsPoint(const FeaturePtr& theFeature,
451                                                    double theX, double theY)
452 {
453   bool aPointIsFound = false;
454   AttributePtr aWidgetAttribute = myFeature->attribute(attributeID());
455
456   std::shared_ptr<GeomAPI_Pnt2d> aPnt2d = 
457                                     std::shared_ptr<GeomAPI_Pnt2d>(new GeomAPI_Pnt2d(theX, theY));
458   std::list<AttributePtr> anAttributes =
459                                 myFeature->data()->attributes(GeomDataAPI_Point2D::typeId());
460   std::list<AttributePtr>::iterator anIter = anAttributes.begin();
461   for(; anIter != anAttributes.end() && !aPointIsFound; anIter++) {
462     AttributePoint2DPtr aPoint2DAttribute =
463       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(*anIter);
464     if (aPoint2DAttribute == aWidgetAttribute)
465       continue;
466     if (aPoint2DAttribute.get() && aPoint2DAttribute->isInitialized()) {
467       aPointIsFound = aPoint2DAttribute->pnt()->isEqual(aPnt2d);
468     }
469   }
470   return aPointIsFound;
471 }
472
473 void PartSet_WidgetPoint2D::onValuesChanged()
474 {
475   myLockApplyMgr->valuesChanged();
476   emit valuesChanged();
477 }
478
479 bool PartSet_WidgetPoint2D::isEventProcessed(QKeyEvent* theEvent)
480 {
481   return myXSpin->isEventProcessed(theEvent) || myXSpin->isEventProcessed(theEvent);
482 }