Salome HOME
refs #200 - edit Length constraint is outside of the transaction
[modules/shaper.git] / src / PartSet / PartSet_OperationFeatureEdit.cpp
1 // File:        PartSet_OperationFeatureEdit.h
2 // Created:     05 May 2014
3 // Author:      Natalia ERMOLAEVA
4
5 #include <PartSet_OperationFeatureEdit.h>
6 #include <PartSet_Tools.h>
7 #include <PartSet_OperationSketch.h>
8 #include <SketchPlugin_Constraint.h>
9
10 #include <ModuleBase_OperationDescription.h>
11 #include <ModuleBase_WidgetEditor.h>
12 #include <ModuleBase_ViewerPrs.h>
13 #include <ModuleBase_IPropertyPanel.h>
14 #include <ModuleBase_ISelection.h>
15 #include <ModuleBase_IViewer.h>
16
17 #include <ModelAPI_Events.h>
18
19 #include <SketchPlugin_Feature.h>
20 #include <GeomDataAPI_Point2D.h>
21
22 #include <ModelAPI_Data.h>
23 #include <ModelAPI_Document.h>
24 #include <ModelAPI_Events.h>
25
26 #include <Events_Loop.h>
27
28 #include <SketchPlugin_Line.h>
29
30 #include <V3d_View.hxx>
31 #include <TopoDS_Vertex.hxx>
32 #include <TopoDS.hxx>
33 #include <BRep_Tool.hxx>
34 #include <AIS_DimensionOwner.hxx>
35 #include <AIS_DimensionSelectionMode.hxx>
36
37 #ifdef _DEBUG
38 #include <QDebug>
39 #endif
40
41 #include <QMouseEvent>
42
43 using namespace std;
44
45 PartSet_OperationFeatureEdit::PartSet_OperationFeatureEdit(const QString& theId,
46                                                            QObject* theParent,
47                                                            CompositeFeaturePtr theFeature)
48     : PartSet_OperationFeatureBase(theId, theParent, theFeature),
49       myIsBlockedSelection(false), myIsBlockedByDoubleClick(false)
50 {
51   myIsEditing = true;
52 }
53
54 PartSet_OperationFeatureEdit::~PartSet_OperationFeatureEdit()
55 {
56 }
57
58 void PartSet_OperationFeatureEdit::initSelection(ModuleBase_ISelection* theSelection,
59                                                       ModuleBase_IViewer* theViewer)
60 {
61   // the method of the parent should is useless here because it processes the given
62   // selection in different way
63   //PartSet_OperationFeatureBase::initSelection(theSelection, theViewer);
64
65   // 1. unite selected and hightlighted objects in order to have an opportunity to drag
66   // by the highlighted object
67   QList<ModuleBase_ViewerPrs> aFeatures = theSelection->getSelected();
68   QList<ModuleBase_ViewerPrs> aHighlighted = theSelection->getHighlighted();
69   // add highlighted elements if they are not selected
70   foreach (ModuleBase_ViewerPrs aPrs, aHighlighted) {
71     if (!PartSet_Tools::isContainPresentation(aFeatures, aPrs))
72       aFeatures.append(aPrs);
73   }
74
75   // 1. find all features with skipping features with selected vertex shapes
76   myFeature2Attribute.clear();
77   // firstly, collect the features without local selection
78   foreach (ModuleBase_ViewerPrs aPrs, aFeatures) {
79     const TopoDS_Shape& aShape = aPrs.shape();
80     if (!aShape.IsNull() && aShape.ShapeType() == TopAbs_VERTEX) { // a point is selected
81       const TopoDS_Vertex& aVertex = TopoDS::Vertex(aShape);
82       if (!aVertex.IsNull()) {
83         continue;
84       }
85     }
86     else {
87       ObjectPtr aObject = aPrs.object();
88       if (!aObject)
89         continue;
90       FeaturePtr aFeature = ModelAPI_Feature::feature(aObject);
91       if (aFeature && myFeature2Attribute.find(aFeature) == myFeature2Attribute.end()) {
92         std::list<std::string> aList;
93         // using an empty list as a sign, that this feature should be moved itself
94         myFeature2Attribute[aFeature] = aList;
95       }
96     }
97   }
98   // 2. collect the features with a local selection on them.
99   // if the list already has this feature, the local selection is skipped
100   // that means that if the selection contains a feature and a feature with local selected point,
101   // the edit is performed for a full feature
102   Handle(V3d_View) aView = theViewer->activeView();
103   foreach (ModuleBase_ViewerPrs aPrs, aFeatures) {
104     const TopoDS_Shape& aShape = aPrs.shape();
105     if (!aShape.IsNull() && aShape.ShapeType() == TopAbs_VERTEX) { // a point is selected
106       const TopoDS_Vertex& aVertex = TopoDS::Vertex(aShape);
107       if (aVertex.IsNull())
108         continue;
109       ObjectPtr aObject = aPrs.object();
110       if (!aObject)
111         continue;
112       FeaturePtr aFeature = ModelAPI_Feature::feature(aObject);
113       if (!aFeature)
114         continue;
115
116       // append the attribute of the vertex if it is found on the current feature
117       gp_Pnt aPoint = BRep_Tool::Pnt(aVertex);
118       double aVX, aVY;
119       PartSet_Tools::convertTo2D(aPoint, sketch(), aView, aVX, aVY);
120       boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D = PartSet_Tools::getFeaturePoint(
121                                                                     aFeature, aVX, aVY);
122       std::string anAttribute = aFeature->data()->id(aPoint2D);
123       std::list<std::string> aList;
124       if (myFeature2Attribute.find(aFeature) != myFeature2Attribute.end())
125         aList = myFeature2Attribute[aFeature];
126
127       aList.push_back(anAttribute);
128       myFeature2Attribute[aFeature] = aList;
129     }
130   }
131 }
132
133 void PartSet_OperationFeatureEdit::mousePressed(QMouseEvent* theEvent, ModuleBase_IViewer* theViewer, ModuleBase_ISelection* theSelection)
134 {
135   ModuleBase_ModelWidget* aActiveWgt = myPropertyPanel->activeWidget();
136   if(aActiveWgt && aActiveWgt->isViewerSelector()) {
137     // Almost do nothing, all stuff in on PartSet_OperationFeatureBase::mouseReleased
138     PartSet_OperationFeatureBase::mousePressed(theEvent, theViewer, theSelection);
139     return;
140   }
141   else {
142     // commit always until the selection restore is realized (for feature and local selection)
143     // TODO: check whether the selection is changed and restart the operation only if it is modified
144     commit();
145     emitFeaturesDeactivation();
146     // find nearest feature and restart the operation for it
147     Handle(V3d_View) aView = theViewer->activeView();
148     QList<ModuleBase_ViewerPrs> aSelected = theSelection->getSelected();
149     QList<ModuleBase_ViewerPrs> aHighlighted = theSelection->getHighlighted();
150
151     ObjectPtr aFeature = PartSet_Tools::nearestFeature(theEvent->pos(), aView, sketch(),
152                                                        aSelected, aHighlighted);
153     if (aFeature) {
154       restartOperation(PartSet_OperationFeatureEdit::Type(), aFeature);
155     }
156   }
157   // the next code is commented because the new attempt to commit/restart operation implementation:
158   //QList<ModuleBase_ViewerPrs> aSelected = theSelection->getSelected();
159   //QList<ModuleBase_ViewerPrs> aHighlighted = theSelection->getHighlighted();
160   //bool aHasShift = (theEvent->modifiers() & Qt::ShiftModifier);
161   //if (aHasShift && !aHighlighted.empty()) {
162   //  foreach (ModuleBase_ViewerPrs aPrs, aHighlighted) {
163   //    aSelected.append(aPrs);
164   //  }
165   //}
166   //ObjectPtr aObject;
167   ///*if (!aSelected.empty()) {
168   //  aObject = aSelected.first().object();
169   //} else {   
170   //  if (!aHighlighted.empty())
171   //    aObject = aHighlighted.first().object();
172   //}*/
173   //// the priority to a highlighted object in order to edit it, even if the selected object is
174   //// the feature of this operation. Otherwise, the highlighting is ignored and the selected
175   //// object is moved
176   //if (!aHighlighted.empty()) {
177   //  aObject = aHighlighted.front().object();
178   //}
179   //if (!aObject && !aSelected.empty())  // changed for a constrain
180   //  aObject = aSelected.front().object();
181
182   //FeaturePtr aFeature = ModelAPI_Feature::feature(aObject);
183   //if (!aFeature || aFeature != feature() || (aSelected.size() > 1)) {
184   //  if (commit()) {
185   //    theViewer->enableSelection(true);
186   //    emit featureConstructed(feature(), FM_Deactivation);
187
188   //    // If we have selection and prehilighting with shift pressed 
189   //    // Then we have to select all these objects and restart as multi edit operfation
190   //    //bool aHasShift = (theEvent->modifiers() & Qt::ShiftModifier);
191   //    //if (aHasShift && !theHighlighted.empty()) {
192   //    //  QList<ObjectPtr> aSelected;
193   //    //  std::list<ModuleBase_ViewerPrs>::const_iterator aIt;
194   //    //  for (aIt = theSelected.cbegin(); aIt != theSelected.cend(); ++aIt)
195   //    //    aSelected.append((*aIt).object());
196
197   //    //  for (aIt = theHighlighted.cbegin(); aIt != theHighlighted.cend(); ++aIt) {
198   //    //    if (!aSelected.contains((*aIt).object()))
199   //    //      aSelected.append((*aIt).object());
200   //    //  }
201   //    //  emit setSelection(aSelected);
202   //    //} else 
203   //    if (aFeature) {
204   //      std::string anOperationType = PartSet_OperationFeatureEdit::Type();
205   //      restartOperation(anOperationType, aFeature);
206   //    }
207   //    //}
208   //  }
209   //}
210 }
211
212 void PartSet_OperationFeatureEdit::mouseMoved(QMouseEvent* theEvent, ModuleBase_IViewer* theViewer)
213 {
214   if (!(theEvent->buttons() & Qt::LeftButton))
215     return;
216   Handle(V3d_View) aView = theViewer->activeView();
217   gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), aView);
218
219   theViewer->enableSelection(false);
220
221   //blockSelection(true);
222   if (myCurPoint.myIsInitialized) {
223     double aCurX, aCurY;
224     PartSet_Tools::convertTo2D(myCurPoint.myPoint, sketch(), aView, aCurX, aCurY);
225
226     double aX, anY;
227     PartSet_Tools::convertTo2D(aPoint, sketch(), aView, aX, anY);
228
229     double aDeltaX = aX - aCurX;
230     double aDeltaY = anY - aCurY;
231
232     // the next code is commented because it is obsolete by the multi edit operation realization here
233     //if (myIsMultiOperation) {
234     //  std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter = myFeature2Attribute.begin();
235     //  while (aFeatIter != myFeature2Attribute.end()) {
236     //    FeaturePtr aFeature = aFeatIter->first;
237     //    std::list<std::string> anAttributes = aFeatIter->second;
238     //    // perform edit for the feature
239     //    if (anAttributes.empty()) {
240     //      boost::shared_ptr<SketchPlugin_Feature> aSketchFeature =
241     //                                     boost::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
242     //      if (aSketchFeature) {
243     //        aSketchFeature->move(aDeltaX, aDeltaY);
244     //      }
245     //    }
246     //    // perform edit for the feature's attribute
247     //    else {
248     //      std::list<std::string>::const_iterator anAttrIter = anAttributes.begin(),
249     //                                             anAttrEnd = anAttributes.end();
250     //      for (; anAttrIter != anAttrEnd; anAttrIter++) {
251     //        boost::shared_ptr<GeomDataAPI_Point2D> aPointAttr = boost::dynamic_pointer_cast<
252     //                         GeomDataAPI_Point2D>(aFeature->data()->attribute(*anAttrIter));
253     //        if (aPointAttr) {
254     //          aPointAttr->move(aDeltaX, aDeltaY);
255     //        }
256     //      }      
257     //    }
258     //    aFeatIter++;
259     //  }
260     //}
261     //else { // multieditoperation
262
263     //boost::shared_ptr<SketchPlugin_Feature> aSketchFeature = boost::dynamic_pointer_cast<
264     //    SketchPlugin_Feature>(feature());
265
266     bool isMoved = false;
267     // the functionality to move the feature attribute if it exists in the internal map
268     std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter = myFeature2Attribute.begin();
269     while (aFeatIter != myFeature2Attribute.end()) {
270       FeaturePtr aFeature = aFeatIter->first;
271       // MPV: added condition because it could be external edge of some object, not sketch
272       if (aFeature && !sketch()->isSub(aFeature)) {
273         aFeatIter++;
274         continue;
275       }
276
277       std::list<std::string> anAttributes = aFeatIter->second;
278       // perform edit for the feature
279       if (anAttributes.empty()) {
280         boost::shared_ptr<SketchPlugin_Feature> aSketchFeature =
281                                        boost::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
282         if (aSketchFeature) {
283           aSketchFeature->move(aDeltaX, aDeltaY);
284           isMoved = true;
285         }
286       }
287       // perform edit for the feature's attribute
288       else {
289         std::list<std::string>::const_iterator anAttrIter = anAttributes.begin(),
290                                                anAttrEnd = anAttributes.end();
291         for (; anAttrIter != anAttrEnd; anAttrIter++) {
292           boost::shared_ptr<GeomDataAPI_Point2D> aPointAttr = boost::dynamic_pointer_cast<
293                            GeomDataAPI_Point2D>(aFeature->data()->attribute(*anAttrIter));
294           if (aPointAttr) {
295             aPointAttr->move(aDeltaX, aDeltaY);
296             isMoved = true;
297           }
298         }      
299       }
300       aFeatIter++;
301     }
302     // the next code is commented because it is obsolete by the multi edit operation realization here
303     // the feature is moved only if there is no a local selection on this feature
304     //if (!isMoved) {
305     //  // MPV: added condition because it could be external edge of some object, not sketch
306     //  if (aSketchFeature && sketch()->isSub(aSketchFeature)) {
307     //   aSketchFeature->move(aDeltaX, aDeltaY);
308     //    static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY);
309     //   ModelAPI_EventCreator::get()->sendUpdated(feature(), anEvent);
310     //  }
311     // }
312     //} // multieditoperation
313   }
314   sendFeatures();
315
316   myCurPoint.setPoint(aPoint);
317 }
318
319 void PartSet_OperationFeatureEdit::mouseReleased(
320     QMouseEvent* theEvent, ModuleBase_IViewer* theViewer,
321     ModuleBase_ISelection* theSelection)
322 {
323   // the block is processed in order to do not commit the transaction until the started
324   // double click functionality is performed. It is reproduced on Linux only
325   if (myIsBlockedByDoubleClick)
326     return;
327
328   theViewer->enableSelection(true);
329   // the next code is commented because it is obsolete by the multi edit operation realization here
330   //if (myIsMultiOperation) {
331   //  if (commit()) {
332   //    std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter = myFeature2Attribute.begin();
333   //    while (aFeatIter != myFeature2Attribute.end()) {
334   //      FeaturePtr aFeature = aFeatIter->first;
335   //      if (aFeature) {
336   //        emit featureConstructed(aFeature, FM_Deactivation);
337   //      }
338   //      aFeatIter++;
339   //    }
340   //  }
341   //}
342   //else { // multieditoperation
343   ModuleBase_ModelWidget* aActiveWgt = 0;
344   if (myPropertyPanel)
345     aActiveWgt = myPropertyPanel->activeWidget();
346   if(aActiveWgt && aActiveWgt->isViewerSelector()) {
347     // Almost do nothing, all stuff in on PartSet_OperationFeatureBase::mouseReleased
348     PartSet_OperationFeatureBase::mouseReleased(theEvent, theViewer, theSelection);
349   //}// else {
350   ////blockSelection(false);
351   ////}
352   //} // multieditoperation
353   }
354   else {
355     theViewer->enableSelection(true);
356
357     // commit operation if there is no selected an highlighted objects anymore
358     Handle(V3d_View) aView = theViewer->activeView();
359     QList<ModuleBase_ViewerPrs> aSelected = theSelection->getSelected();
360     QList<ModuleBase_ViewerPrs> aHighlighted = theSelection->getHighlighted();
361
362     if (aSelected.empty() && aHighlighted.empty()) {
363       commit();
364       emitFeaturesDeactivation();
365     }
366   }
367 }
368
369 void PartSet_OperationFeatureEdit::mouseDoubleClick(
370     QMouseEvent* theEvent, Handle_V3d_View theView,
371     ModuleBase_ISelection* theSelection)
372 {
373   myIsBlockedByDoubleClick = true;
374   // TODO the functionality is important only for constraint feature. Should be moved in another place
375   QList<ModuleBase_ViewerPrs> aSelected = theSelection->getSelected();
376   if (!aSelected.empty()) {
377     ModuleBase_ViewerPrs aFeaturePrs = aSelected.first();
378     if (!aFeaturePrs.owner().IsNull()) {
379       Handle(AIS_DimensionOwner) anOwner = Handle(AIS_DimensionOwner)::DownCast(
380           aFeaturePrs.owner());
381       if (!anOwner.IsNull() && anOwner->SelectionMode() == AIS_DSM_Text) {
382         bool isValid;
383         double aValue = PartSet_Tools::featureValue(feature(), SketchPlugin_Constraint::VALUE(),
384                                                     isValid);
385         if (isValid) {
386           ModuleBase_WidgetEditor::editFeatureValue(feature(), SketchPlugin_Constraint::VALUE());
387           flushUpdated();
388         }
389       }
390     }
391   }
392   myIsBlockedByDoubleClick  = false;
393 }
394
395 void PartSet_OperationFeatureEdit::startOperation()
396 {
397   PartSet_OperationSketchBase::startOperation();
398   //emit multiSelectionEnabled(false);
399
400   myCurPoint.clear();
401 }
402
403 void PartSet_OperationFeatureEdit::stopOperation()
404 {
405   //emit multiSelectionEnabled(true);
406
407   //blockSelection(false, false);
408
409   myFeature2Attribute.clear();
410 }
411
412 //void PartSet_OperationFeatureEdit::blockSelection(bool isBlocked, const bool isRestoreSelection)
413 //{
414 //  if (myIsBlockedSelection == isBlocked)
415 //    return;
416 //
417 //  myIsBlockedSelection = isBlocked;
418 //  QList<ObjectPtr> aFeatureList;
419 //  aFeatureList.append(feature());
420 //
421 //  //if (isBlocked) {
422 //  //  emit setSelection(QList<ObjectPtr>());
423 //  //  emit stopSelection(aFeatureList, true);
424 //  //} else {
425 //  //  emit stopSelection(aFeatureList, false);
426 //  //  if (isRestoreSelection)
427 //  //    emit setSelection(aFeatureList);
428 //  //}
429 //}
430
431 FeaturePtr PartSet_OperationFeatureEdit::createFeature(const bool theFlushMessage,
432   CompositeFeaturePtr theCompositeFeature)
433 {
434   // do nothing in order to do not create a new feature
435   return FeaturePtr();
436 }
437
438 void PartSet_OperationFeatureEdit::sendFeatures()
439 {
440   static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_MOVED);
441
442   std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter = myFeature2Attribute.begin();
443   while (aFeatIter != myFeature2Attribute.end()) {
444     FeaturePtr aFeature = aFeatIter->first;
445     if (aFeature) {
446       ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent);
447     }
448     aFeatIter++;
449   }
450
451   Events_Loop::loop()->flush(anEvent);
452   flushUpdated();
453 }
454
455 void PartSet_OperationFeatureEdit::emitFeaturesDeactivation()
456 {
457   std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter = myFeature2Attribute.begin();
458   while (aFeatIter != myFeature2Attribute.end()) {
459     FeaturePtr aFeature = aFeatIter->first;
460     if (aFeature) {
461       emit featureConstructed(aFeature, FM_Deactivation);
462     }
463     aFeatIter++;
464   }
465 }
466