Salome HOME
Issue #1787 : code's adjusting
[modules/shaper.git] / src / PartSet / PartSet_WidgetSketchCreator.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:        PartSet_WidgetSketchCreator.cpp
4 // Created:     08 June 2015
5 // Author:      Vitaly SMETANNIKOV
6
7 #include "PartSet_WidgetSketchCreator.h"
8 #include "PartSet_Module.h"
9 #include "PartSet_WidgetSketchLabel.h"
10 #include "PartSet_PreviewPlanes.h"
11
12 #include <Config_Keywords.h>
13
14 #include <XGUI_ModuleConnector.h>
15 #include <XGUI_Workshop.h>
16 #include <XGUI_Displayer.h>
17 #include <XGUI_SelectionMgr.h>
18 #include <XGUI_OperationMgr.h>
19 #include <XGUI_PropertyPanel.h>
20 #include <XGUI_Tools.h>
21 #include <XGUI_ViewerProxy.h>
22
23 #include <GeomAPI_Face.h>
24
25 #include <Events_InfoMessage.h>
26
27 #include <ModelAPI_Session.h>
28 #include <ModelAPI_ResultBody.h>
29 #include <ModelAPI_AttributeSelection.h>
30 #include <ModelAPI_AttributeSelectionList.h>
31 #include <ModelAPI_Validator.h>
32 #include <ModelAPI_Events.h>
33 #include <ModelAPI_ResultConstruction.h>
34
35 #include <SketchPlugin_SketchEntity.h>
36 #include <FeaturesPlugin_CompositeBoolean.h>
37
38 #include <ModuleBase_Tools.h>
39 #include <ModuleBase_Operation.h>
40 #include <ModuleBase_IPropertyPanel.h>
41 #include <ModuleBase_OperationFeature.h>
42 #include <ModuleBase_ViewerPrs.h>
43
44 #include <Config_WidgetAPI.h>
45
46 #include <Events_Loop.h>
47
48 #include <QLabel>
49 #include <QLineEdit>
50 #include <QDoubleValidator>
51 //#include <QFormLayout>
52 #include <QVBoxLayout>
53 #include <QMessageBox>
54 #include <QMainWindow>
55
56 #define DEBUG_UNDO_INVALID_SKETCH
57
58 PartSet_WidgetSketchCreator::PartSet_WidgetSketchCreator(QWidget* theParent,
59                                                          PartSet_Module* theModule,
60                                                          const Config_WidgetAPI* theData)
61 : ModuleBase_WidgetSelector(theParent, theModule->workshop(), theData),
62   myModule(theModule), myIsCustomAttribute(false)
63 {
64   myAttributeListID = theData->getProperty("attribute_list_id");
65
66   //QFormLayout* aLayout = new QFormLayout(this);
67   QVBoxLayout* aLayout = new QVBoxLayout(this);
68   ModuleBase_Tools::zeroMargins(aLayout);
69
70   ModuleBase_Tools::adjustMargins(aLayout);
71
72   QString aLabelText = QString::fromStdString(theData->widgetLabel());
73   QString aLabelIcon = QString::fromStdString(theData->widgetIcon());
74
75   // Size of the View control
76   mySizeOfViewWidget = new QWidget(this);
77   QHBoxLayout* aSizeLayout = new QHBoxLayout(mySizeOfViewWidget);
78   aSizeLayout->addWidget(new QLabel("Size of the view", mySizeOfViewWidget));
79   mySizeOfView = new QLineEdit(mySizeOfViewWidget);
80
81   QDoubleValidator* aValidator = new QDoubleValidator(0, DBL_MAX, 12, mySizeOfView);
82   aValidator->setLocale(ModuleBase_Tools::doubleLocale());
83   aValidator->setNotation(QDoubleValidator::StandardNotation);
84   mySizeOfView->setValidator(aValidator);
85   aSizeLayout->addWidget(mySizeOfView);
86
87   myLabel = new QLabel(aLabelText, this);
88   myLabel->setWordWrap(true);
89
90   aLayout->addWidget(mySizeOfViewWidget);
91   aLayout->addWidget(myLabel);
92   aLayout->addStretch(1);
93
94   std::string aTypes = theData->getProperty("shape_types");
95   myShapeTypes = QString(aTypes.c_str()).split(' ', QString::SkipEmptyParts);
96
97   myPreviewPlanes = new PartSet_PreviewPlanes();
98 }
99
100 PartSet_WidgetSketchCreator::~PartSet_WidgetSketchCreator()
101 {
102   // we need to deactivate here in order to hide preview planes if the selection mode is
103   // active
104   deactivate();
105 }
106
107 QList<QWidget*> PartSet_WidgetSketchCreator::getControls() const
108 {
109   QList<QWidget*> aControls;
110   aControls.append(myLabel);
111   return aControls;
112 }
113
114 bool PartSet_WidgetSketchCreator::restoreValueCustom()
115 {
116   return true;
117 }
118
119 bool PartSet_WidgetSketchCreator::storeValueCustom()
120 {
121   return true;
122 }
123
124 AttributePtr PartSet_WidgetSketchCreator::attribute() const
125 {
126   AttributePtr anAttribute;
127   if (myIsCustomAttribute)
128     anAttribute = myFeature->attribute(myAttributeListID);
129   else
130     anAttribute = ModuleBase_WidgetSelector::attribute();
131
132   return anAttribute;
133 }
134
135 //********************************************************************
136 void PartSet_WidgetSketchCreator::openExtrusionTransaction()
137 {
138   SessionPtr aMgr = ModelAPI_Session::get();
139   bool aIsOp = aMgr->isOperation();
140   if (!aIsOp) {
141     const static std::string aNestedOpID("Parameters modification");
142     aMgr->startOperation(aNestedOpID, true);
143   }
144 }
145
146 //********************************************************************
147 bool PartSet_WidgetSketchCreator::isValidSelection(const ModuleBase_ViewerPrsPtr& theValue)
148 {
149   bool aValid = false;
150   if (myIsCustomAttribute) {
151     // check only suiting of the value to custom attribute (myAttributeListID)
152     // do not cash of validation to avoid using states, stored for XML attribute
153     // there is an alternative is to call clearValidatedCash() in setSelection()
154     aValid = isValidSelectionForAttribute(theValue, attribute());
155   }
156   else { /// if the validated attribute is already custom
157     if (getValidState(theValue, aValid)) {
158       return aValid;
159     }
160     aValid = isValidSelectionCustom(theValue);
161     if (!aValid)
162       // check selection to create new sketh (XML current attribute)
163       aValid = isValidSelectionForAttribute(theValue, attribute());
164     if (!aValid) {
165       // check selection to fill list attribute (myAttributeListID)
166       bool isCustomAttribute = myIsCustomAttribute;
167       myIsCustomAttribute = true;
168       aValid = isValidSelectionForAttribute(theValue, attribute());
169       myIsCustomAttribute = isCustomAttribute;
170     }
171   }
172   storeValidState(theValue, aValid);
173   return aValid;
174 }
175
176 //********************************************************************
177 bool PartSet_WidgetSketchCreator::isValidSelectionCustom(const ModuleBase_ViewerPrsPtr& theValue)
178 {
179   return PartSet_WidgetSketchLabel::canFillSketch(theValue);
180 }
181
182 void PartSet_WidgetSketchCreator::activateSelectionControl()
183 {
184   // we need to call activate here as the widget has no focus accepted controls
185   // if these controls are added here, activate will happens automatically after focusIn()
186   XGUI_Workshop* aWorkshop = XGUI_Tools::workshop(myModule->workshop());
187   XGUI_PropertyPanel* aPanel = aWorkshop->propertyPanel();
188   aPanel->activateWidget(this, false);
189 }
190
191 void PartSet_WidgetSketchCreator::setVisibleSelectionControl(const bool theSelectionControl)
192 {
193   // hide current widget, activate the next widget
194   XGUI_Workshop* aWorkshop = XGUI_Tools::workshop(myModule->workshop());
195   XGUI_PropertyPanel* aPanel = aWorkshop->propertyPanel();
196   const QList<ModuleBase_ModelWidget*>& aWidgets = aPanel->modelWidgets();
197   foreach(ModuleBase_ModelWidget* aWidget, aWidgets) {
198     if (theSelectionControl) { // hide other controls
199       if (aWidget != this)
200         aWidget->setVisible(false);
201     }
202     else { // hide current control
203       if (aWidget == this)
204         aWidget->setVisible(false);
205       else {
206         aWidget->setVisible(true);
207         if (aWidget->attributeID() == myAttributeListID)
208           setEnabledModelWidget(aWidget, !hasSubObjects());
209       }
210     }
211   }
212
213   if (theSelectionControl) {
214     bool aBodyIsVisualized = myPreviewPlanes->hasVisualizedBodies(myWorkshop);
215     bool aSketchIsVisualized = myPreviewPlanes->hasVisualizedSketch(myWorkshop);
216     if (!aBodyIsVisualized && !aSketchIsVisualized) {
217       // We have to select a plane before any operation
218       myPreviewPlanes->showPreviewPlanes(myWorkshop);
219       mySizeOfViewWidget->setVisible(true);
220     }
221     else {
222       mySizeOfViewWidget->setVisible(false);
223     }
224
225   } else {
226     bool aHidePreview = myPreviewPlanes->isPreviewDisplayed();
227     myPreviewPlanes->erasePreviewPlanes(myWorkshop);
228     if (aHidePreview)
229       aWorkshop->viewer()->update();
230   }
231 }
232
233 QIntList PartSet_WidgetSketchCreator::shapeTypes() const
234 {
235   QIntList aShapeTypes;
236   foreach(QString aType, myShapeTypes) {
237     aShapeTypes.append(ModuleBase_Tools::shapeType(aType));
238   }
239   return aShapeTypes;
240 }
241
242 void PartSet_WidgetSketchCreator::setEditingMode(bool isEditing)
243 {
244   ModuleBase_ModelWidget::setEditingMode(isEditing);
245   if (isEditing) {
246     setVisibleSelectionControl(false);
247
248     ModuleBase_ModelWidget* anAttributeListWidget = 0;
249     XGUI_Workshop* aWorkshop = XGUI_Tools::workshop(myModule->workshop());
250     XGUI_PropertyPanel* aPanel = aWorkshop->propertyPanel();
251     const QList<ModuleBase_ModelWidget*>& aWidgets = aPanel->modelWidgets();
252     foreach(ModuleBase_ModelWidget* aWidget, aWidgets) {
253       if (aWidget->attributeID() == myAttributeListID) {
254         anAttributeListWidget = aWidget;
255         break;
256       }
257     }
258     if (anAttributeListWidget)
259       setEnabledModelWidget(anAttributeListWidget, !hasSubObjects());
260   }
261 }
262
263 bool PartSet_WidgetSketchCreator::isSelectionMode() const
264 {
265   AttributeSelectionListPtr anAttrList = myFeature->data()->selectionList(myAttributeListID);
266   bool aHasValueInList = anAttrList.get() && anAttrList->size() > 0;
267
268   return !aHasValueInList;
269 }
270
271 bool PartSet_WidgetSketchCreator::hasSubObjects() const
272 {
273   bool aHasSubObjects = false;
274
275   bool aCanSetFocus = true;
276   CompositeFeaturePtr aComposite = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(myFeature);
277   if (aComposite.get())
278     aHasSubObjects = aComposite->numberOfSubs() > 0;
279   return aHasSubObjects;
280 }
281
282 bool PartSet_WidgetSketchCreator::setSelection(QList<ModuleBase_ViewerPrsPtr>& theValues,
283                                                const bool theToValidate)
284 {
285   bool aDone = false;
286   if (!startSketchOperation(theValues)) {
287     myIsCustomAttribute = true;
288     QList<ModuleBase_ViewerPrsPtr>::const_iterator
289       anIt = theValues.begin(), aLast = theValues.end();
290     bool aProcessed = false;
291     for (; anIt != aLast; anIt++) {
292       ModuleBase_ViewerPrsPtr aValue = *anIt;
293       if (!theToValidate || isValidInFilters(aValue))
294         aProcessed = setSelectionCustom(aValue) || aProcessed;
295     }
296     myIsCustomAttribute = false;
297     aDone = aProcessed;
298     if (aProcessed) {
299       emit valuesChanged();
300       updateObject(myFeature);
301       setVisibleSelectionControl(false);
302       // manually deactivation because the widget was
303       // not activated as has no focus acceptin controls
304       deactivate();
305       emit focusOutWidget(this);
306     }
307   }
308   return aDone;
309 }
310
311 //********************************************************************
312 void PartSet_WidgetSketchCreator::onSelectionChanged()
313 {
314   QList<ModuleBase_ViewerPrsPtr> aSelected = getFilteredSelected();
315   bool isDone = setSelection(aSelected, true/*false*/);
316 }
317
318 //********************************************************************
319 void PartSet_WidgetSketchCreator::updateOnSelectionChanged(const bool theDone)
320 {
321 }
322
323 bool PartSet_WidgetSketchCreator::startSketchOperation(
324                               const QList<ModuleBase_ViewerPrsPtr>& theValues)
325 {
326   bool aSketchStarted = false;
327
328   if (theValues.size() != 1)
329     return aSketchStarted;
330
331   ModuleBase_ViewerPrsPtr aValue = theValues.front();
332   if (!aValue.get() || !PartSet_WidgetSketchLabel::canFillSketch(aValue))
333     return aSketchStarted;
334
335   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(aValue->object());
336   /// sketch should not started by object(face) selected as global. If Local face is selected,
337   /// sketch is started
338   if (aResult.get() && aValue->shape().get() && aResult->shape()->isEqual(aValue->shape())) {
339     ResultConstructionPtr aConstruction =
340       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aResult);
341     if (!aConstruction.get() || !aConstruction->isInfinite())
342       return aSketchStarted;
343   }
344   aSketchStarted = true;
345   // Set View size if a plane is selected
346   if (myPreviewPlanes->isPreviewDisplayed() &&
347       myPreviewPlanes->isPreviewShape(aValue->shape())) {
348     QString aSizeOfViewStr = mySizeOfView->text();
349     if (!aSizeOfViewStr.isEmpty()) {
350       bool isOk;
351       double aSizeOfView = aSizeOfViewStr.toDouble(&isOk);
352       if (isOk && aSizeOfView > 0) {
353         Handle(V3d_View) aView3d = myWorkshop->viewer()->activeView();
354         if (!aView3d.IsNull()) {
355           Bnd_Box aBndBox;
356           double aHalfSize = aSizeOfView/2.0;
357           aBndBox.Update(-aHalfSize, -aHalfSize, -aHalfSize, aHalfSize, aHalfSize, aHalfSize);
358           aView3d->FitAll(aBndBox, 0.01, false);
359         }
360       }
361     }
362   }
363   // manually deactivation because the widget was not activated as has no focus acceptin controls
364   deactivate();
365   bool aHidePreview = myPreviewPlanes->isPreviewDisplayed();
366   myPreviewPlanes->erasePreviewPlanes(myWorkshop);
367
368   // Launch Sketch operation
369   CompositeFeaturePtr aCompFeature =
370     std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(myFeature);
371
372   // start edit operation for the sketch
373   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
374                                                             (myModule->createOperation("Sketch"));
375   QList<ModuleBase_ViewerPrsPtr> aValues;
376   aValues.push_back(aValue);
377   aFOperation->setPreselection(aValues);
378
379   myWorkshop->processLaunchOperation(aFOperation);
380
381   return aSketchStarted;
382 }
383
384 bool PartSet_WidgetSketchCreator::focusTo()
385 {
386   // this method is called only in creation mode. In Edition mode this widget is hidden
387   if (isSelectionMode() && !hasSubObjects()) {
388     setVisibleSelectionControl(true);
389     activateSelectionControl();
390     openExtrusionTransaction();
391     return true;
392   }
393   else
394     connect(myModule, SIGNAL(resumed(ModuleBase_Operation*)),
395             SLOT(onResumed(ModuleBase_Operation*)));
396
397   return true;
398 }
399
400 void PartSet_WidgetSketchCreator::deactivate()
401 {
402   ModuleBase_WidgetSelector::deactivate();
403
404   bool aHidePreview = myPreviewPlanes->isPreviewDisplayed();
405   myPreviewPlanes->erasePreviewPlanes(myWorkshop);
406   if (aHidePreview)
407     XGUI_Tools::workshop(myWorkshop)->viewer()->update();
408
409 }
410
411 void PartSet_WidgetSketchCreator::onResumed(ModuleBase_Operation* theOp)
412 {
413   SessionPtr aMgr = ModelAPI_Session::get();
414   bool aIsOp = aMgr->isOperation();
415   if (aIsOp) {
416     // in current implementation, all transactions are closed when resume happens
417     // so, this is a wrong case, which is not checked.
418     // To provide it, make correction in later rows about abort/undo transactions
419     return;
420   }
421   // Set visible only selection control
422   setVisibleSelectionControl(true);
423
424   // Validate the created sketch. If it is valid, it is set into the composite feature selection
425   // list, otherwise it is removed
426   CompositeFeaturePtr aCompFeature =
427     std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(myFeature);
428   AttributeSelectionListPtr anAttrList = myFeature->data()->selectionList(myAttributeListID);
429   if (aCompFeature->numberOfSubs() > 0) {
430     // set the sub feature to attribute selection list and check whether sketch is valid
431     SessionPtr aMgr = ModelAPI_Session::get();
432     const static std::string aNestedOpID("Set Sketch result into Selection list");
433     aMgr->startOperation(aNestedOpID, false); // false to not attach to Extrusion operation
434     setSketchObjectToList(aCompFeature, anAttrList);
435     aMgr->finishOperation();
436
437     if (!validateSelectionList()) {
438 #ifdef DEBUG_UNDO_INVALID_SKETCH
439       aMgr->undo(); // Extrusion modification parameters: setSketchObjectToList()
440       aMgr->undo(); /// Sketch creation
441 #else
442       aMgr->startOperation("Delete invalid Sketch feature", false);
443
444       // delete invalid sketch
445       CompositeFeaturePtr aSketchFeature =
446               std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aCompFeature->subFeature(0));
447       QObjectPtrList anObjects;
448       anObjects.append(aSketchFeature);
449
450       XGUI_Workshop* aWorkshop = XGUI_Tools::workshop(myModule->workshop());
451       aWorkshop->deleteFeatures(anObjects);
452
453       aMgr->finishOperation();
454 #endif
455     }
456   }
457   openExtrusionTransaction();
458
459   if (aCompFeature->numberOfSubs() == 0) {
460     // call activateWidget() of the parent to connect to the viewer seleciton
461     activateSelectionControl();
462   }
463   else {
464     // check if the created sketch is valid. If it is invalid, it will be deleted with warning else
465     /// the attribute selection list will be filled by result of this sketch.
466     setVisibleSelectionControl(false);
467
468     // Update value in attribute selection list
469     XGUI_Workshop* aWorkshop = XGUI_Tools::workshop(myModule->workshop());
470     XGUI_PropertyPanel* aPropertyPanel = aWorkshop->propertyPanel();
471     const QList<ModuleBase_ModelWidget*>& aWidgets = aPropertyPanel->modelWidgets();
472     ModuleBase_ModelWidget* aListWidget = 0;
473     foreach(ModuleBase_ModelWidget* aWidget, aWidgets) {
474       if (aWidget->attributeID() == myAttributeListID) {
475         aListWidget = aWidget;
476         break;
477       }
478     }
479     if (aListWidget) {
480       aListWidget->restoreValue();
481       aPropertyPanel->activateNextWidget(aListWidget);
482     }
483
484     // Hide sketcher result
485     CompositeFeaturePtr aSketchFeature =
486       std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aCompFeature->subFeature(0));
487     std::list<ResultPtr> aResults = aSketchFeature->results();
488     std::list<ResultPtr>::const_iterator aIt;
489     for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
490       (*aIt)->setDisplayed(false);
491     }
492     aSketchFeature->setDisplayed(false);
493     static Events_Loop* aLoop = Events_Loop::loop();
494     aLoop->flush(aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY));
495
496     // Add Selected body were created the sketcher to list of selected objects
497     std::string anObjectsAttribute = FeaturesPlugin_CompositeBoolean::OBJECTS_ID();
498     AttributeSelectionListPtr aSelList = aCompFeature->data()->selectionList(anObjectsAttribute);
499     if (aSelList.get()) {
500       DataPtr aData = aSketchFeature->data();
501       AttributeSelectionPtr aSelAttr = std::dynamic_pointer_cast<ModelAPI_AttributeSelection>
502                                     (aData->attribute(SketchPlugin_SketchEntity::EXTERNAL_ID()));
503       ResultPtr aRes = aSelAttr.get() ? aSelAttr->context() : ResultPtr();
504       if (aRes.get()) {
505         SessionPtr aMgr = ModelAPI_Session::get();
506         ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
507         AttributePtr anAttribute = myFeature->attribute(anObjectsAttribute);
508         std::string aValidatorID;
509         Events_InfoMessage anError;
510         aSelList->append(aRes, GeomShapePtr());
511         if (aFactory->validate(anAttribute, aValidatorID, anError))
512           updateObject(aCompFeature);
513         else
514           aSelList->clear();
515       }
516     }
517   }
518   restoreValue();
519 }
520
521 bool PartSet_WidgetSketchCreator::validateSelectionList() const
522 {
523   AttributeSelectionListPtr anAttrList = myFeature->data()->selectionList(myAttributeListID);
524
525   SessionPtr aMgr = ModelAPI_Session::get();
526   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
527   std::string aValidatorID;
528   Events_InfoMessage anError;
529   bool isValidPComposite = aFactory->validate(anAttrList, aValidatorID, anError);
530   if (!isValidPComposite) {
531     XGUI_Workshop* aWorkshop = XGUI_Tools::workshop(myModule->workshop());
532     // TODO(spo): translate
533     QMessageBox::question(aWorkshop->desktop(), tr("Apply current feature"),
534                   tr("Sketch is invalid and will be deleted.\nError: %1")
535                   .arg(anError.messageString().c_str()),
536                   QMessageBox::Ok);
537   }
538   return isValidPComposite;
539 }
540
541 void PartSet_WidgetSketchCreator::setSketchObjectToList(
542                             const CompositeFeaturePtr& theCompositeFeature,
543                             const AttributePtr& theAttribute)
544 {
545   if (!theCompositeFeature.get() || theCompositeFeature->numberOfSubs() != 1)
546     return;
547
548   AttributeSelectionListPtr aBaseObjectsSelectionList =
549                      std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
550   if(!aBaseObjectsSelectionList.get() || aBaseObjectsSelectionList->isInitialized()) {
551     return;
552   }
553
554   FeaturePtr aSketchFeature = theCompositeFeature->subFeature(0);
555   if(!aSketchFeature.get() || aSketchFeature->results().empty()) {
556     return;
557   }
558
559   ResultPtr aSketchRes = aSketchFeature->results().front();
560   ResultConstructionPtr aConstruction =
561     std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aSketchRes);
562   if(!aConstruction.get()) {
563     return;
564   }
565
566   if(aBaseObjectsSelectionList->size() == 0) {
567     aBaseObjectsSelectionList->append(aSketchRes, GeomShapePtr());
568   }
569 }
570
571 void PartSet_WidgetSketchCreator::setEnabledModelWidget(ModuleBase_ModelWidget* theModelWidget,
572                                                         const bool theEnabled)
573 {
574   QList<QWidget*> aMyControls = theModelWidget->getControls();
575   foreach(QWidget*  eachControl, aMyControls) {
576     eachControl->setEnabled(theEnabled);
577   }
578 }