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 <AIS_DimensionOwner.hxx>
32 #include <AIS_DimensionSelectionMode.hxx>
38 #include <QMouseEvent>
42 PartSet_OperationFeatureEdit::PartSet_OperationFeatureEdit(const QString& theId,
44 CompositeFeaturePtr theFeature)
45 : PartSet_OperationFeatureBase(theId, theParent, theFeature),
46 myIsBlockedSelection(false), myIsBlockedByDoubleClick(false)
51 PartSet_OperationFeatureEdit::~PartSet_OperationFeatureEdit()
55 void PartSet_OperationFeatureEdit::initSelection(ModuleBase_ISelection* theSelection,
56 ModuleBase_IViewer* theViewer)
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);
62 QList<ModuleBase_ViewerPrs> aSelected = theSelection->getSelected();
63 QList<ModuleBase_ViewerPrs> aHighlighted = theSelection->getHighlighted();
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);
72 fillFeature2Attribute(anUniqueHighlighted, theViewer, myHighlightedFeature2Attribute);
74 foreach (ModuleBase_ViewerPrs aPrs, anUniqueHighlighted) {
75 if (!PartSet_Tools::isContainPresentation(aSelected, aPrs))
76 aSelected.append(aPrs);
78 fillFeature2Attribute(aSelected, theViewer, myAllFeature2Attribute);
81 void PartSet_OperationFeatureEdit::fillFeature2Attribute(
82 const QList<ModuleBase_ViewerPrs>& thePresentations,
83 ModuleBase_IViewer* theViewer,
84 std::map<FeaturePtr, std::list<std::string> >& theFeature2Attribute)
86 // 1. find all features with skipping features with selected vertex shapes
87 theFeature2Attribute.clear();
88 // firstly, collect the features without local selection
90 foreach (ModuleBase_ViewerPrs aPrs, thePresentations) {
91 if (getViewerPoint(aPrs, theViewer, aX, anY))
94 ObjectPtr aObject = aPrs.object();
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;
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();
114 FeaturePtr aFeature = ModelAPI_Feature::feature(aObject);
118 // append the attribute of the vertex if it is found on the current feature
119 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D = PartSet_Tools::getFeaturePoint(
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];
126 aList.push_back(anAttribute);
127 theFeature2Attribute[aFeature] = aList;
132 void PartSet_OperationFeatureEdit::mousePressed(QMouseEvent* theEvent, ModuleBase_IViewer* theViewer, ModuleBase_ISelection* theSelection)
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.
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
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();
153 ObjectPtr aFeature = PartSet_Tools::nearestFeature(theEvent->pos(), aView, sketch(),
154 aSelected, aHighlighted);
156 restartOperation(PartSet_OperationFeatureEdit::Type(), aFeature);
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);
169 ///*if (!aSelected.empty()) {
170 // aObject = aSelected.first().object();
172 // if (!aHighlighted.empty())
173 // aObject = aHighlighted.first().object();
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
178 //if (!aHighlighted.empty()) {
179 // aObject = aHighlighted.front().object();
181 //if (!aObject && !aSelected.empty()) // changed for a constrain
182 // aObject = aSelected.front().object();
184 //FeaturePtr aFeature = ModelAPI_Feature::feature(aObject);
185 //if (!aFeature || aFeature != feature() || (aSelected.size() > 1)) {
187 // theViewer->enableSelection(true);
188 // emit featureConstructed(feature(), FM_Deactivation);
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());
199 // // for (aIt = theHighlighted.cbegin(); aIt != theHighlighted.cend(); ++aIt) {
200 // // if (!aSelected.contains((*aIt).object()))
201 // // aSelected.append((*aIt).object());
203 // // emit setSelection(aSelected);
206 // std::string anOperationType = PartSet_OperationFeatureEdit::Type();
207 // restartOperation(anOperationType, aFeature);
214 void PartSet_OperationFeatureEdit::mouseMoved(QMouseEvent* theEvent, ModuleBase_IViewer* theViewer)
216 if (!(theEvent->buttons() & Qt::LeftButton))
218 Handle(V3d_View) aView = theViewer->activeView();
219 gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), aView);
221 theViewer->enableSelection(false);
223 //blockSelection(true);
224 if (myCurPoint.myIsInitialized) {
226 PartSet_Tools::convertTo2D(myCurPoint.myPoint, sketch(), aView, aCurX, aCurY);
229 PartSet_Tools::convertTo2D(aPoint, sketch(), aView, aX, anY);
231 double aDeltaX = aX - aCurX;
232 double aDeltaY = anY - aCurY;
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 // boost::shared_ptr<SketchPlugin_Feature> aSketchFeature =
243 // boost::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
244 // if (aSketchFeature) {
245 // aSketchFeature->move(aDeltaX, aDeltaY);
248 // // perform edit for the feature's attribute
250 // std::list<std::string>::const_iterator anAttrIter = anAttributes.begin(),
251 // anAttrEnd = anAttributes.end();
252 // for (; anAttrIter != anAttrEnd; anAttrIter++) {
253 // boost::shared_ptr<GeomDataAPI_Point2D> aPointAttr = boost::dynamic_pointer_cast<
254 // GeomDataAPI_Point2D>(aFeature->data()->attribute(*anAttrIter));
256 // aPointAttr->move(aDeltaX, aDeltaY);
263 //else { // multieditoperation
265 //boost::shared_ptr<SketchPlugin_Feature> aSketchFeature = boost::dynamic_pointer_cast<
266 // SketchPlugin_Feature>(feature());
268 bool isMoved = false;
269 bool aHasShift = (theEvent->modifiers() & Qt::ShiftModifier);
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();
278 aFeatIter = myHighlightedFeature2Attribute.begin();
279 aFeatLast = myHighlightedFeature2Attribute.end();
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)) {
290 std::list<std::string> anAttributes = aFeatIter->second;
291 // perform edit for the feature
292 if (anAttributes.empty()) {
293 boost::shared_ptr<SketchPlugin_Feature> aSketchFeature =
294 boost::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
295 if (aSketchFeature) {
296 aSketchFeature->move(aDeltaX, aDeltaY);
300 // perform edit for the feature's attribute
302 std::list<std::string>::const_iterator anAttrIter = anAttributes.begin(),
303 anAttrEnd = anAttributes.end();
304 for (; anAttrIter != anAttrEnd; anAttrIter++) {
305 boost::shared_ptr<GeomDataAPI_Point2D> aPointAttr = boost::dynamic_pointer_cast<
306 GeomDataAPI_Point2D>(aFeature->data()->attribute(*anAttrIter));
308 aPointAttr->move(aDeltaX, aDeltaY);
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
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);
325 //} // multieditoperation
326 sendFeatures(aHasShift);
329 myCurPoint.setPoint(aPoint);
332 void PartSet_OperationFeatureEdit::mouseReleased(
333 QMouseEvent* theEvent, ModuleBase_IViewer* theViewer,
334 ModuleBase_ISelection* theSelection)
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)
341 theViewer->enableSelection(true);
342 // the next code is commented because it is obsolete by the multi edit operation realization here
343 //if (myIsMultiOperation) {
345 // std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter = myFeature2Attribute.begin();
346 // while (aFeatIter != myFeature2Attribute.end()) {
347 // FeaturePtr aFeature = aFeatIter->first;
349 // emit featureConstructed(aFeature, FM_Deactivation);
355 //else { // multieditoperation
356 ModuleBase_ModelWidget* aActiveWgt = 0;
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);
363 ////blockSelection(false);
365 //} // multieditoperation
368 theViewer->enableSelection(true);
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();
375 if (aSelected.empty() && aHighlighted.empty()) {
377 emitFeaturesDeactivation();
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);
394 void PartSet_OperationFeatureEdit::mouseDoubleClick(
395 QMouseEvent* theEvent, Handle_V3d_View theView,
396 ModuleBase_ISelection* theSelection)
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()) {
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) {
429 double aValue = PartSet_Tools::featureValue(feature(), SketchPlugin_Constraint::VALUE(),
432 ModuleBase_WidgetEditor::editFeatureValue(feature(), SketchPlugin_Constraint::VALUE());
438 myIsBlockedByDoubleClick = false;
441 void PartSet_OperationFeatureEdit::startOperation()
443 PartSet_OperationSketchBase::startOperation();
444 //emit multiSelectionEnabled(false);
449 void PartSet_OperationFeatureEdit::stopOperation()
451 //emit multiSelectionEnabled(true);
453 //blockSelection(false, false);
455 myHighlightedFeature2Attribute.clear();
456 myAllFeature2Attribute.clear();
459 //void PartSet_OperationFeatureEdit::blockSelection(bool isBlocked, const bool isRestoreSelection)
461 // if (myIsBlockedSelection == isBlocked)
464 // myIsBlockedSelection = isBlocked;
465 // QList<ObjectPtr> aFeatureList;
466 // aFeatureList.append(feature());
468 // //if (isBlocked) {
469 // // emit setSelection(QList<ObjectPtr>());
470 // // emit stopSelection(aFeatureList, true);
472 // // emit stopSelection(aFeatureList, false);
473 // // if (isRestoreSelection)
474 // // emit setSelection(aFeatureList);
478 FeaturePtr PartSet_OperationFeatureEdit::createFeature(const bool theFlushMessage,
479 CompositeFeaturePtr theCompositeFeature)
481 // do nothing in order to do not create a new feature
485 void PartSet_OperationFeatureEdit::sendFeatures(const bool theIsAllFeatures)
487 static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_MOVED);
489 std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter, aFeatLast;
490 if (theIsAllFeatures || myHighlightedFeature2Attribute.empty()) {
491 aFeatIter = myAllFeature2Attribute.begin();
492 aFeatLast = myAllFeature2Attribute.end();
495 aFeatIter = myHighlightedFeature2Attribute.begin();
496 aFeatLast = myHighlightedFeature2Attribute.end();
498 while (aFeatIter != aFeatLast) {
499 FeaturePtr aFeature = aFeatIter->first;
501 ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent);
506 Events_Loop::loop()->flush(anEvent);
510 void PartSet_OperationFeatureEdit::emitFeaturesDeactivation()
512 std::map<FeaturePtr, std::list<std::string>>::iterator aFeatIter = myAllFeature2Attribute.begin();
513 while (aFeatIter != myAllFeature2Attribute.end()) {
514 FeaturePtr aFeature = aFeatIter->first;
516 emit featureConstructed(aFeature, FM_Deactivation);