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