Salome HOME
Merge branch 'master' of newgeom:newgeom
[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> aSelected = 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   fillFeature2Attribute(aHighlighted, theViewer, myHighlightedFeature2Attribute);
76
77   foreach (ModuleBase_ViewerPrs aPrs, aHighlighted) {
78     if (!PartSet_Tools::isContainPresentation(aSelected, aPrs))
79       aSelected.append(aPrs);
80   }
81   fillFeature2Attribute(aSelected, theViewer, myAllFeature2Attribute);
82 }
83
84 void PartSet_OperationFeatureEdit::fillFeature2Attribute(
85                                     const QList<ModuleBase_ViewerPrs>& thePresentations,
86                                     ModuleBase_IViewer* theViewer,
87                                     std::map<FeaturePtr, std::list<std::string> >& theFeature2Attribute)
88 {
89   // 1. find all features with skipping features with selected vertex shapes
90   theFeature2Attribute.clear();
91   // firstly, collect the features without local selection
92   foreach (ModuleBase_ViewerPrs aPrs, thePresentations) {
93     const TopoDS_Shape& aShape = aPrs.shape();
94     if (!aShape.IsNull() && aShape.ShapeType() == TopAbs_VERTEX) { // a point is selected
95       const TopoDS_Vertex& aVertex = TopoDS::Vertex(aShape);
96       if (!aVertex.IsNull()) {
97         continue;
98       }
99     }
100     else {
101       ObjectPtr aObject = aPrs.object();
102       if (!aObject)
103         continue;
104       FeaturePtr aFeature = ModelAPI_Feature::feature(aObject);
105       if (aFeature && theFeature2Attribute.find(aFeature) == theFeature2Attribute.end()) {
106         std::list<std::string> aList;
107         // using an empty list as a sign, that this feature should be moved itself
108         theFeature2Attribute[aFeature] = aList;
109       }
110     }
111   }
112   // 2. collect the features with a local selection on them.
113   // if the list already has this feature, the local selection is skipped
114   // that means that if the selection contains a feature and a feature with local selected point,
115   // the edit is performed for a full feature
116   Handle(V3d_View) aView = theViewer->activeView();
117   foreach (ModuleBase_ViewerPrs aPrs, thePresentations) {
118     const TopoDS_Shape& aShape = aPrs.shape();
119     if (!aShape.IsNull() && aShape.ShapeType() == TopAbs_VERTEX) { // a point is selected
120       const TopoDS_Vertex& aVertex = TopoDS::Vertex(aShape);
121       if (aVertex.IsNull())
122         continue;
123       ObjectPtr aObject = aPrs.object();
124       if (!aObject)
125         continue;
126       FeaturePtr aFeature = ModelAPI_Feature::feature(aObject);
127       if (!aFeature)
128         continue;
129
130       // append the attribute of the vertex if it is found on the current feature
131       gp_Pnt aPoint = BRep_Tool::Pnt(aVertex);
132       double aVX, aVY;
133       PartSet_Tools::convertTo2D(aPoint, sketch(), aView, aVX, aVY);
134       boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D = PartSet_Tools::getFeaturePoint(
135                                                                     aFeature, aVX, aVY);
136       std::string anAttribute = aFeature->data()->id(aPoint2D);
137       std::list<std::string> aList;
138       if (theFeature2Attribute.find(aFeature) != theFeature2Attribute.end())
139         aList = theFeature2Attribute[aFeature];
140
141       aList.push_back(anAttribute);
142       theFeature2Attribute[aFeature] = aList;
143     }
144   }
145 }
146
147 void PartSet_OperationFeatureEdit::mousePressed(QMouseEvent* theEvent, ModuleBase_IViewer* theViewer, ModuleBase_ISelection* theSelection)
148 {
149   ModuleBase_ModelWidget* aActiveWgt = myPropertyPanel->activeWidget();
150   if(aActiveWgt && aActiveWgt->isViewerSelector()) {
151     // Almost do nothing, all stuff in on PartSet_OperationFeatureBase::mouseReleased
152     PartSet_OperationFeatureBase::mousePressed(theEvent, theViewer, theSelection);
153     // the current point should be cleared because it is saved from the previous move and 
154     // should be reinitialized after the start moving. It is important for example for the lenght
155     // constraint where the first widget is a viewer selector.
156     myCurPoint.clear();
157   }
158   else {
159     // commit always until the selection restore is realized (for feature and local selection)
160     // TODO: check whether the selection is changed and restart the operation only if it is modified
161     commit();
162     emitFeaturesDeactivation();
163     // find nearest feature and restart the operation for it
164     Handle(V3d_View) aView = theViewer->activeView();
165     QList<ModuleBase_ViewerPrs> aSelected = theSelection->getSelected();
166     QList<ModuleBase_ViewerPrs> aHighlighted = theSelection->getHighlighted();
167
168     ObjectPtr aFeature = PartSet_Tools::nearestFeature(theEvent->pos(), aView, sketch(),
169                                                        aSelected, aHighlighted);
170     if (aFeature) {
171       restartOperation(PartSet_OperationFeatureEdit::Type(), aFeature);
172     }
173   }
174   // the next code is commented because the new attempt to commit/restart operation implementation:
175   //QList<ModuleBase_ViewerPrs> aSelected = theSelection->getSelected();
176   //QList<ModuleBase_ViewerPrs> aHighlighted = theSelection->getHighlighted();
177   //bool aHasShift = (theEvent->modifiers() & Qt::ShiftModifier);
178   //if (aHasShift && !aHighlighted.empty()) {
179   //  foreach (ModuleBase_ViewerPrs aPrs, aHighlighted) {
180   //    aSelected.append(aPrs);
181   //  }
182   //}
183   //ObjectPtr aObject;
184   ///*if (!aSelected.empty()) {
185   //  aObject = aSelected.first().object();
186   //} else {   
187   //  if (!aHighlighted.empty())
188   //    aObject = aHighlighted.first().object();
189   //}*/
190   //// the priority to a highlighted object in order to edit it, even if the selected object is
191   //// the feature of this operation. Otherwise, the highlighting is ignored and the selected
192   //// object is moved
193   //if (!aHighlighted.empty()) {
194   //  aObject = aHighlighted.front().object();
195   //}
196   //if (!aObject && !aSelected.empty())  // changed for a constrain
197   //  aObject = aSelected.front().object();
198
199   //FeaturePtr aFeature = ModelAPI_Feature::feature(aObject);
200   //if (!aFeature || aFeature != feature() || (aSelected.size() > 1)) {
201   //  if (commit()) {
202   //    theViewer->enableSelection(true);
203   //    emit featureConstructed(feature(), FM_Deactivation);
204
205   //    // If we have selection and prehilighting with shift pressed 
206   //    // Then we have to select all these objects and restart as multi edit operfation
207   //    //bool aHasShift = (theEvent->modifiers() & Qt::ShiftModifier);
208   //    //if (aHasShift && !theHighlighted.empty()) {
209   //    //  QList<ObjectPtr> aSelected;
210   //    //  std::list<ModuleBase_ViewerPrs>::const_iterator aIt;
211   //    //  for (aIt = theSelected.cbegin(); aIt != theSelected.cend(); ++aIt)
212   //    //    aSelected.append((*aIt).object());
213
214   //    //  for (aIt = theHighlighted.cbegin(); aIt != theHighlighted.cend(); ++aIt) {
215   //    //    if (!aSelected.contains((*aIt).object()))
216   //    //      aSelected.append((*aIt).object());
217   //    //  }
218   //    //  emit setSelection(aSelected);
219   //    //} else 
220   //    if (aFeature) {
221   //      std::string anOperationType = PartSet_OperationFeatureEdit::Type();
222   //      restartOperation(anOperationType, aFeature);
223   //    }
224   //    //}
225   //  }
226   //}
227 }
228
229 void PartSet_OperationFeatureEdit::mouseMoved(QMouseEvent* theEvent, ModuleBase_IViewer* theViewer)
230 {
231   if (!(theEvent->buttons() & Qt::LeftButton))
232     return;
233   Handle(V3d_View) aView = theViewer->activeView();
234   gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), aView);
235
236   theViewer->enableSelection(false);
237
238   //blockSelection(true);
239   if (myCurPoint.myIsInitialized) {
240     double aCurX, aCurY;
241     PartSet_Tools::convertTo2D(myCurPoint.myPoint, sketch(), aView, aCurX, aCurY);
242
243     double aX, anY;
244     PartSet_Tools::convertTo2D(aPoint, sketch(), aView, aX, anY);
245
246     double aDeltaX = aX - aCurX;
247     double aDeltaY = anY - aCurY;
248
249     // the next code is commented because it is obsolete by the multi edit operation realization here
250     //if (myIsMultiOperation) {
251     //  std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter = myFeature2Attribute.begin();
252     //  while (aFeatIter != myFeature2Attribute.end()) {
253     //    FeaturePtr aFeature = aFeatIter->first;
254     //    std::list<std::string> anAttributes = aFeatIter->second;
255     //    // perform edit for the feature
256     //    if (anAttributes.empty()) {
257     //      boost::shared_ptr<SketchPlugin_Feature> aSketchFeature =
258     //                                     boost::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
259     //      if (aSketchFeature) {
260     //        aSketchFeature->move(aDeltaX, aDeltaY);
261     //      }
262     //    }
263     //    // perform edit for the feature's attribute
264     //    else {
265     //      std::list<std::string>::const_iterator anAttrIter = anAttributes.begin(),
266     //                                             anAttrEnd = anAttributes.end();
267     //      for (; anAttrIter != anAttrEnd; anAttrIter++) {
268     //        boost::shared_ptr<GeomDataAPI_Point2D> aPointAttr = boost::dynamic_pointer_cast<
269     //                         GeomDataAPI_Point2D>(aFeature->data()->attribute(*anAttrIter));
270     //        if (aPointAttr) {
271     //          aPointAttr->move(aDeltaX, aDeltaY);
272     //        }
273     //      }      
274     //    }
275     //    aFeatIter++;
276     //  }
277     //}
278     //else { // multieditoperation
279
280     //boost::shared_ptr<SketchPlugin_Feature> aSketchFeature = boost::dynamic_pointer_cast<
281     //    SketchPlugin_Feature>(feature());
282
283     bool isMoved = false;
284     bool aHasShift = (theEvent->modifiers() & Qt::ShiftModifier);
285
286     // the functionality to move the feature attribute if it exists in the internal map
287     std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter, aFeatLast;
288     if (aHasShift) {
289       aFeatIter = myAllFeature2Attribute.begin();
290       aFeatLast = myAllFeature2Attribute.end();
291     }
292     else {
293       aFeatIter = myHighlightedFeature2Attribute.begin();
294       aFeatLast = myHighlightedFeature2Attribute.end();
295     }
296
297     while (aFeatIter != aFeatLast) {
298       FeaturePtr aFeature = aFeatIter->first;
299       // MPV: added condition because it could be external edge of some object, not sketch
300       if (aFeature && !sketch()->isSub(aFeature)) {
301         aFeatIter++;
302         continue;
303       }
304
305       std::list<std::string> anAttributes = aFeatIter->second;
306       // perform edit for the feature
307       if (anAttributes.empty()) {
308         boost::shared_ptr<SketchPlugin_Feature> aSketchFeature =
309                                        boost::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
310         if (aSketchFeature) {
311           aSketchFeature->move(aDeltaX, aDeltaY);
312           isMoved = true;
313         }
314       }
315       // perform edit for the feature's attribute
316       else {
317         std::list<std::string>::const_iterator anAttrIter = anAttributes.begin(),
318                                                anAttrEnd = anAttributes.end();
319         for (; anAttrIter != anAttrEnd; anAttrIter++) {
320           boost::shared_ptr<GeomDataAPI_Point2D> aPointAttr = boost::dynamic_pointer_cast<
321                            GeomDataAPI_Point2D>(aFeature->data()->attribute(*anAttrIter));
322           if (aPointAttr) {
323             aPointAttr->move(aDeltaX, aDeltaY);
324             isMoved = true;
325           }
326         }      
327       }
328       aFeatIter++;
329     }
330     // the next code is commented because it is obsolete by the multi edit operation realization here
331     // the feature is moved only if there is no a local selection on this feature
332     //if (!isMoved) {
333     //  // MPV: added condition because it could be external edge of some object, not sketch
334     //  if (aSketchFeature && sketch()->isSub(aSketchFeature)) {
335     //   aSketchFeature->move(aDeltaX, aDeltaY);
336     //    static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY);
337     //   ModelAPI_EventCreator::get()->sendUpdated(feature(), anEvent);
338     //  }
339     // }
340     //} // multieditoperation
341   }
342   sendFeatures();
343
344   myCurPoint.setPoint(aPoint);
345 }
346
347 void PartSet_OperationFeatureEdit::mouseReleased(
348     QMouseEvent* theEvent, ModuleBase_IViewer* theViewer,
349     ModuleBase_ISelection* theSelection)
350 {
351   // the block is processed in order to do not commit the transaction until the started
352   // double click functionality is performed. It is reproduced on Linux only
353   if (myIsBlockedByDoubleClick)
354     return;
355
356   theViewer->enableSelection(true);
357   // the next code is commented because it is obsolete by the multi edit operation realization here
358   //if (myIsMultiOperation) {
359   //  if (commit()) {
360   //    std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter = myFeature2Attribute.begin();
361   //    while (aFeatIter != myFeature2Attribute.end()) {
362   //      FeaturePtr aFeature = aFeatIter->first;
363   //      if (aFeature) {
364   //        emit featureConstructed(aFeature, FM_Deactivation);
365   //      }
366   //      aFeatIter++;
367   //    }
368   //  }
369   //}
370   //else { // multieditoperation
371   ModuleBase_ModelWidget* aActiveWgt = 0;
372   if (myPropertyPanel)
373     aActiveWgt = myPropertyPanel->activeWidget();
374   if(aActiveWgt && aActiveWgt->isViewerSelector()) {
375     // Almost do nothing, all stuff in on PartSet_OperationFeatureBase::mouseReleased
376     PartSet_OperationFeatureBase::mouseReleased(theEvent, theViewer, theSelection);
377   //}// else {
378   ////blockSelection(false);
379   ////}
380   //} // multieditoperation
381   }
382   else {
383     theViewer->enableSelection(true);
384
385     // commit operation if there is no selected an highlighted objects anymore
386     Handle(V3d_View) aView = theViewer->activeView();
387     QList<ModuleBase_ViewerPrs> aSelected = theSelection->getSelected();
388     QList<ModuleBase_ViewerPrs> aHighlighted = theSelection->getHighlighted();
389
390     if (aSelected.empty() && aHighlighted.empty()) {
391       commit();
392       emitFeaturesDeactivation();
393     }
394     else {
395       /// TODO: OCC bug: 25034 - the highlighted list should be filled not only for AIS_Shape
396       /// but for other IO, for example constraint dimensions.
397       /// It is empty and we have to use the process mouse release to start edition operation
398       /// for these objects
399       /*QList<ModuleBase_ViewerPrs> aSelected = theSelection->getSelected();
400       if (aSelected.size() == 1) {
401         ObjectPtr anObject = aSelected.first().object();
402         FeaturePtr aSelFeature = ModelAPI_Feature::feature(anObject);
403         FeaturePtr aCurFeature = feature();
404         QList<ModuleBase_ViewerPrs> aHighlighted = theSelection->getHighlighted();
405         //QList<ModuleBase_ViewerPrs> anEmpty;
406         //aSelected.push_back(aHighlighted);
407         ObjectPtr aNFeature = PartSet_Tools::nearestFeature(theEvent->pos(), aView, sketch(),
408                                                             aSelected, aHighlighted);
409         int aSizeS = aSelected.size();
410         if (anObject && aNFeature != feature()) {
411           restartOperation(PartSet_OperationFeatureEdit::Type(), aNFeature);
412         }
413         //bool aValue  = 9;
414       }*/
415     }
416   }
417 }
418
419 void PartSet_OperationFeatureEdit::mouseDoubleClick(
420     QMouseEvent* theEvent, Handle_V3d_View theView,
421     ModuleBase_ISelection* theSelection)
422 {
423   myIsBlockedByDoubleClick = true;
424   // TODO the functionality is important only for constraint feature. Should be moved in another place
425   QList<ModuleBase_ViewerPrs> aSelected = theSelection->getSelected();
426   if (!aSelected.empty()) {
427     ModuleBase_ViewerPrs aFeaturePrs = aSelected.first();
428     if (!aFeaturePrs.owner().IsNull()) {
429       Handle(AIS_DimensionOwner) anOwner = Handle(AIS_DimensionOwner)::DownCast(
430           aFeaturePrs.owner());
431       if (!anOwner.IsNull() && anOwner->SelectionMode() == AIS_DSM_Text) {
432         bool isValid;
433         double aValue = PartSet_Tools::featureValue(feature(), SketchPlugin_Constraint::VALUE(),
434                                                     isValid);
435         if (isValid) {
436           ModuleBase_WidgetEditor::editFeatureValue(feature(), SketchPlugin_Constraint::VALUE());
437           flushUpdated();
438         }
439       }
440     }
441   }
442   myIsBlockedByDoubleClick  = false;
443 }
444
445 void PartSet_OperationFeatureEdit::startOperation()
446 {
447   PartSet_OperationSketchBase::startOperation();
448   //emit multiSelectionEnabled(false);
449
450   myCurPoint.clear();
451 }
452
453 void PartSet_OperationFeatureEdit::stopOperation()
454 {
455   //emit multiSelectionEnabled(true);
456
457   //blockSelection(false, false);
458
459   //myFeature2Attribute.clear();
460   myHighlightedFeature2Attribute.clear();
461   myAllFeature2Attribute.clear();
462 }
463
464 //void PartSet_OperationFeatureEdit::blockSelection(bool isBlocked, const bool isRestoreSelection)
465 //{
466 //  if (myIsBlockedSelection == isBlocked)
467 //    return;
468 //
469 //  myIsBlockedSelection = isBlocked;
470 //  QList<ObjectPtr> aFeatureList;
471 //  aFeatureList.append(feature());
472 //
473 //  //if (isBlocked) {
474 //  //  emit setSelection(QList<ObjectPtr>());
475 //  //  emit stopSelection(aFeatureList, true);
476 //  //} else {
477 //  //  emit stopSelection(aFeatureList, false);
478 //  //  if (isRestoreSelection)
479 //  //    emit setSelection(aFeatureList);
480 //  //}
481 //}
482
483 FeaturePtr PartSet_OperationFeatureEdit::createFeature(const bool theFlushMessage,
484   CompositeFeaturePtr theCompositeFeature)
485 {
486   // do nothing in order to do not create a new feature
487   return FeaturePtr();
488 }
489
490 void PartSet_OperationFeatureEdit::sendFeatures()
491 {
492   static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_MOVED);
493
494   std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter = myAllFeature2Attribute.begin();
495   while (aFeatIter != myAllFeature2Attribute.end()) {
496     FeaturePtr aFeature = aFeatIter->first;
497     if (aFeature) {
498       ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent);
499     }
500     aFeatIter++;
501   }
502
503   Events_Loop::loop()->flush(anEvent);
504   flushUpdated();
505 }
506
507 void PartSet_OperationFeatureEdit::emitFeaturesDeactivation()
508 {
509   std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter = myAllFeature2Attribute.begin();
510   while (aFeatIter != myAllFeature2Attribute.end()) {
511     FeaturePtr aFeature = aFeatIter->first;
512     if (aFeature) {
513       emit featureConstructed(aFeature, FM_Deactivation);
514     }
515     aFeatIter++;
516   }
517 }
518