1 // File: PartSet_OperationFeatureEdit.h
2 // Created: 05 May 2014
3 // Author: Natalia ERMOLAEVA
5 #include <PartSet_OperationFeatureEdit.h>
6 #include <PartSet_Tools.h>
7 #include <PartSet_OperationSketch.h>
8 #include <SketchPlugin_Constraint.h>
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>
17 #include <ModelAPI_Events.h>
19 #include <SketchPlugin_Feature.h>
20 #include <GeomDataAPI_Point2D.h>
22 #include <ModelAPI_Data.h>
23 #include <ModelAPI_Document.h>
24 #include <ModelAPI_Events.h>
26 #include <Events_Loop.h>
28 #include <SketchPlugin_Line.h>
30 #include <V3d_View.hxx>
31 #include <TopoDS_Vertex.hxx>
33 #include <BRep_Tool.hxx>
34 #include <AIS_DimensionOwner.hxx>
35 #include <AIS_DimensionSelectionMode.hxx>
41 #include <QMouseEvent>
45 PartSet_OperationFeatureEdit::PartSet_OperationFeatureEdit(const QString& theId,
47 CompositeFeaturePtr theFeature)
48 : PartSet_OperationFeatureBase(theId, theParent, theFeature),
49 myIsBlockedSelection(false), myIsBlockedByDoubleClick(false)
54 PartSet_OperationFeatureEdit::~PartSet_OperationFeatureEdit()
58 void PartSet_OperationFeatureEdit::initSelection(ModuleBase_ISelection* theSelection,
59 ModuleBase_IViewer* theViewer)
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);
65 QList<ModuleBase_ViewerPrs> aSelected = theSelection->getSelected();
66 QList<ModuleBase_ViewerPrs> aHighlighted = theSelection->getHighlighted();
68 // there is a bug in OCC, where the highlighted objects are repeated and should be
69 // filtered on the unique state here
70 QList<ModuleBase_ViewerPrs> anUniqueHighlighted;
71 foreach (ModuleBase_ViewerPrs aPrs, aHighlighted) {
72 if (!PartSet_Tools::isContainPresentation(anUniqueHighlighted, aPrs))
73 anUniqueHighlighted.append(aPrs);
75 fillFeature2Attribute(anUniqueHighlighted, theViewer, myHighlightedFeature2Attribute);
77 foreach (ModuleBase_ViewerPrs aPrs, anUniqueHighlighted) {
78 if (!PartSet_Tools::isContainPresentation(aSelected, aPrs))
79 aSelected.append(aPrs);
81 fillFeature2Attribute(aSelected, theViewer, myAllFeature2Attribute);
84 void PartSet_OperationFeatureEdit::fillFeature2Attribute(
85 const QList<ModuleBase_ViewerPrs>& thePresentations,
86 ModuleBase_IViewer* theViewer,
87 std::map<FeaturePtr, std::list<std::string> >& theFeature2Attribute)
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()) {
101 ObjectPtr aObject = aPrs.object();
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;
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())
123 ObjectPtr aObject = aPrs.object();
126 FeaturePtr aFeature = ModelAPI_Feature::feature(aObject);
130 // append the attribute of the vertex if it is found on the current feature
131 gp_Pnt aPoint = BRep_Tool::Pnt(aVertex);
133 PartSet_Tools::convertTo2D(aPoint, sketch(), aView, aVX, aVY);
134 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D = PartSet_Tools::getFeaturePoint(
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];
141 aList.push_back(anAttribute);
142 theFeature2Attribute[aFeature] = aList;
147 void PartSet_OperationFeatureEdit::mousePressed(QMouseEvent* theEvent, ModuleBase_IViewer* theViewer, ModuleBase_ISelection* theSelection)
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.
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
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();
168 ObjectPtr aFeature = PartSet_Tools::nearestFeature(theEvent->pos(), aView, sketch(),
169 aSelected, aHighlighted);
171 restartOperation(PartSet_OperationFeatureEdit::Type(), aFeature);
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);
184 ///*if (!aSelected.empty()) {
185 // aObject = aSelected.first().object();
187 // if (!aHighlighted.empty())
188 // aObject = aHighlighted.first().object();
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
193 //if (!aHighlighted.empty()) {
194 // aObject = aHighlighted.front().object();
196 //if (!aObject && !aSelected.empty()) // changed for a constrain
197 // aObject = aSelected.front().object();
199 //FeaturePtr aFeature = ModelAPI_Feature::feature(aObject);
200 //if (!aFeature || aFeature != feature() || (aSelected.size() > 1)) {
202 // theViewer->enableSelection(true);
203 // emit featureConstructed(feature(), FM_Deactivation);
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());
214 // // for (aIt = theHighlighted.cbegin(); aIt != theHighlighted.cend(); ++aIt) {
215 // // if (!aSelected.contains((*aIt).object()))
216 // // aSelected.append((*aIt).object());
218 // // emit setSelection(aSelected);
221 // std::string anOperationType = PartSet_OperationFeatureEdit::Type();
222 // restartOperation(anOperationType, aFeature);
229 void PartSet_OperationFeatureEdit::mouseMoved(QMouseEvent* theEvent, ModuleBase_IViewer* theViewer)
231 if (!(theEvent->buttons() & Qt::LeftButton))
233 Handle(V3d_View) aView = theViewer->activeView();
234 gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), aView);
236 theViewer->enableSelection(false);
238 //blockSelection(true);
239 if (myCurPoint.myIsInitialized) {
241 PartSet_Tools::convertTo2D(myCurPoint.myPoint, sketch(), aView, aCurX, aCurY);
244 PartSet_Tools::convertTo2D(aPoint, sketch(), aView, aX, anY);
246 double aDeltaX = aX - aCurX;
247 double aDeltaY = anY - aCurY;
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);
263 // // perform edit for the feature's attribute
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));
271 // aPointAttr->move(aDeltaX, aDeltaY);
278 //else { // multieditoperation
280 //boost::shared_ptr<SketchPlugin_Feature> aSketchFeature = boost::dynamic_pointer_cast<
281 // SketchPlugin_Feature>(feature());
283 bool isMoved = false;
284 bool aHasShift = (theEvent->modifiers() & Qt::ShiftModifier);
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 || myHighlightedFeature2Attribute.empty()) {
289 aFeatIter = myAllFeature2Attribute.begin();
290 aFeatLast = myAllFeature2Attribute.end();
293 aFeatIter = myHighlightedFeature2Attribute.begin();
294 aFeatLast = myHighlightedFeature2Attribute.end();
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)) {
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);
315 // perform edit for the feature's attribute
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));
323 aPointAttr->move(aDeltaX, aDeltaY);
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
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);
340 //} // multieditoperation
341 sendFeatures(aHasShift);
344 myCurPoint.setPoint(aPoint);
347 void PartSet_OperationFeatureEdit::mouseReleased(
348 QMouseEvent* theEvent, ModuleBase_IViewer* theViewer,
349 ModuleBase_ISelection* theSelection)
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)
356 theViewer->enableSelection(true);
357 // the next code is commented because it is obsolete by the multi edit operation realization here
358 //if (myIsMultiOperation) {
360 // std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter = myFeature2Attribute.begin();
361 // while (aFeatIter != myFeature2Attribute.end()) {
362 // FeaturePtr aFeature = aFeatIter->first;
364 // emit featureConstructed(aFeature, FM_Deactivation);
370 //else { // multieditoperation
371 ModuleBase_ModelWidget* aActiveWgt = 0;
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);
378 ////blockSelection(false);
380 //} // multieditoperation
383 theViewer->enableSelection(true);
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();
390 if (aSelected.empty() && aHighlighted.empty()) {
392 emitFeaturesDeactivation();
394 else if (aSelected.size() == 1) {
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 ObjectPtr anObject = aSelected.first().object();
400 FeaturePtr aFeature = ModelAPI_Feature::feature(anObject);
401 if (aFeature && PartSet_Tools::isConstraintFeature(aFeature->getKind()) &&
402 aFeature != feature()) {
403 restartOperation(PartSet_OperationFeatureEdit::Type(), aFeature);
409 void PartSet_OperationFeatureEdit::mouseDoubleClick(
410 QMouseEvent* theEvent, Handle_V3d_View theView,
411 ModuleBase_ISelection* theSelection)
413 // TODO the functionality is important only for constraint feature. Should be moved in another place
414 QList<ModuleBase_ViewerPrs> aSelected = theSelection->getSelected();
415 // in case when the double click happens on another constraint feature when selection control is active
416 // we should not perform the double click functionality
417 // if there is no the viewer selector widget active, the operation is restarted with a correct feature
418 ModuleBase_ModelWidget* aActiveWgt = myPropertyPanel->activeWidget();
419 if(aActiveWgt && aActiveWgt->isViewerSelector()) {
420 if (!aSelected.empty()) {
421 if (aSelected.size() == 1) {
422 /// TODO: OCC bug: 25034 - the highlighted list should be filled not only for AIS_Shape
423 /// but for other IO, for example constraint dimensions.
424 /// It is empty and we have to use the process mouse release to start edition operation
425 /// for these objects
426 ObjectPtr anObject = aSelected.first().object();
427 FeaturePtr aFeature = ModelAPI_Feature::feature(anObject);
428 if (aFeature && PartSet_Tools::isConstraintFeature(aFeature->getKind()) &&
429 aFeature != feature()) {
436 myIsBlockedByDoubleClick = true;
437 if (!aSelected.empty()) {
438 ModuleBase_ViewerPrs aFeaturePrs = aSelected.first();
439 if (!aFeaturePrs.owner().IsNull()) {
440 Handle(AIS_DimensionOwner) anOwner = Handle(AIS_DimensionOwner)::DownCast(
441 aFeaturePrs.owner());
442 if (!anOwner.IsNull() && anOwner->SelectionMode() == AIS_DSM_Text) {
444 double aValue = PartSet_Tools::featureValue(feature(), SketchPlugin_Constraint::VALUE(),
447 ModuleBase_WidgetEditor::editFeatureValue(feature(), SketchPlugin_Constraint::VALUE());
453 myIsBlockedByDoubleClick = false;
456 void PartSet_OperationFeatureEdit::startOperation()
458 PartSet_OperationSketchBase::startOperation();
459 //emit multiSelectionEnabled(false);
464 void PartSet_OperationFeatureEdit::stopOperation()
466 //emit multiSelectionEnabled(true);
468 //blockSelection(false, false);
470 myHighlightedFeature2Attribute.clear();
471 myAllFeature2Attribute.clear();
474 //void PartSet_OperationFeatureEdit::blockSelection(bool isBlocked, const bool isRestoreSelection)
476 // if (myIsBlockedSelection == isBlocked)
479 // myIsBlockedSelection = isBlocked;
480 // QList<ObjectPtr> aFeatureList;
481 // aFeatureList.append(feature());
483 // //if (isBlocked) {
484 // // emit setSelection(QList<ObjectPtr>());
485 // // emit stopSelection(aFeatureList, true);
487 // // emit stopSelection(aFeatureList, false);
488 // // if (isRestoreSelection)
489 // // emit setSelection(aFeatureList);
493 FeaturePtr PartSet_OperationFeatureEdit::createFeature(const bool theFlushMessage,
494 CompositeFeaturePtr theCompositeFeature)
496 // do nothing in order to do not create a new feature
500 void PartSet_OperationFeatureEdit::sendFeatures(const bool theIsAllFeatures)
502 static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_MOVED);
504 std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter, aFeatLast;
505 if (theIsAllFeatures || myHighlightedFeature2Attribute.empty()) {
506 aFeatIter = myAllFeature2Attribute.begin();
507 aFeatLast = myAllFeature2Attribute.end();
510 aFeatIter = myHighlightedFeature2Attribute.begin();
511 aFeatLast = myHighlightedFeature2Attribute.end();
513 while (aFeatIter != aFeatLast) {
514 FeaturePtr aFeature = aFeatIter->first;
516 ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent);
521 Events_Loop::loop()->flush(anEvent);
525 void PartSet_OperationFeatureEdit::emitFeaturesDeactivation()
527 std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter = myAllFeature2Attribute.begin();
528 while (aFeatIter != myAllFeature2Attribute.end()) {
529 FeaturePtr aFeature = aFeatIter->first;
531 emit featureConstructed(aFeature, FM_Deactivation);