Salome HOME
1. Tangent Arc - restart operation. Scenario: create Line, point, Start tangent arc...
[modules/shaper.git] / src / PartSet / PartSet_SketcherReetntrantMgr.cpp
index 15cf843d3072df5989d5dd1d08db2db2f0d81a1e..4cfcf167d8f49f9726c1ca2cb94d447d257c2013 100755 (executable)
@@ -6,6 +6,10 @@
 #include "PartSet_WidgetPoint2d.h"
 
 #include "ModelAPI_Session.h"
+#include "ModelAPI_AttributeString.h"
+#include "ModelAPI_AttributeRefAttr.h"
+
+#include "GeomDataAPI_Point2D.h"
 
 #include <ModuleBase_IPropertyPanel.h>
 #include <ModuleBase_OperationFeature.h>
 #include <ModuleBase_PageBase.h>
 #include <ModuleBase_WidgetFactory.h>
 #include <ModuleBase_OperationDescription.h>
+#include "ModuleBase_ToolBox.h"
+#include "ModuleBase_ISelection.h"
 
 #include <SketchPlugin_Feature.h>
 #include <SketchPlugin_Line.h>
+#include <SketchPlugin_Arc.h>
+#include <SketchPlugin_Circle.h>
+#include <SketchPlugin_Point.h>
 
 #include <XGUI_Workshop.h>
 #include <XGUI_ModuleConnector.h>
 #include <XGUI_OperationMgr.h>
 #include <XGUI_PropertyPanel.h>
+#include <XGUI_ErrorMgr.h>
+#include <XGUI_SelectionMgr.h>
 
 #include <QToolButton>
 
@@ -47,12 +58,14 @@ ModuleBase_ModelWidget* PartSet_SketcherReetntrantMgr::internalActiveWidget() co
   if (!isActiveMgr())
     return aWidget;
 
-  ModuleBase_Operation* aOperation = myWorkshop->currentOperation();
-  if (aOperation) {
-    ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
-    ModuleBase_ModelWidget* anActiveWidget = aPanel->activeWidget();
-    if (myIsInternalEditOperation && (!anActiveWidget || !anActiveWidget->isViewerSelector()))
-      aWidget = myInternalActiveWidget;
+  ModuleBase_Operation* anOperation = myWorkshop->currentOperation();
+  if (anOperation) {
+    ModuleBase_IPropertyPanel* aPanel = anOperation->propertyPanel();
+    if (aPanel) { // check for case when the operation is started but property panel is not filled
+      ModuleBase_ModelWidget* anActiveWidget = aPanel->activeWidget();
+      if (myIsInternalEditOperation && (!anActiveWidget || !anActiveWidget->isViewerSelector()))
+        aWidget = myInternalActiveWidget;
+    }
   }
   return aWidget;
 }
@@ -62,6 +75,26 @@ bool PartSet_SketcherReetntrantMgr::isInternalEditActive() const
   return myIsInternalEditOperation;
 }
 
+void PartSet_SketcherReetntrantMgr::updateInternalEditActiveState()
+{
+  if  (myIsInternalEditOperation) {
+    ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
+                                                         (myWorkshop->currentOperation());
+    if (aFOperation) {
+      FeaturePtr aFeature = aFOperation->feature();
+      QString anError = myWorkshop->module()->getFeatureError(aFeature);
+      // stop started internal edit operation as soon as the operation becomes invalid
+      // it is especially important for the sketch tangent arc feature
+      if (!anError.isEmpty()) {
+        aFOperation->setEditOperation(false);
+        //workshop()->operationMgr()->updateApplyOfOperations();
+        myIsInternalEditOperation = false;
+        updateAcceptAllAction();
+      }
+    }
+  }
+}
+
 bool PartSet_SketcherReetntrantMgr::operationCommitted(ModuleBase_Operation* theOperation)
 {
   bool aProcessed = false;
@@ -79,6 +112,12 @@ void PartSet_SketcherReetntrantMgr::operationStarted(ModuleBase_Operation* theOp
   if (!isActiveMgr())
     return;
 
+  if (myPreviousFeature.get() && myRestartingMode == RM_LastFeatureUsed) {
+    ModuleBase_OperationFeature* aCurrentOperation = dynamic_cast<ModuleBase_OperationFeature*>(
+                                                                myWorkshop->currentOperation());
+    CompositeFeaturePtr aSketch = module()->sketchMgr()->activeSketch();
+    copyReetntrantAttributes(myPreviousFeature, aCurrentOperation->feature(), aSketch);
+  }
   resetFlags();
 }
 
@@ -90,31 +129,57 @@ void PartSet_SketcherReetntrantMgr::operationAborted(ModuleBase_Operation* theOp
   resetFlags();
 }
 
-bool PartSet_SketcherReetntrantMgr::processMouseMoved(ModuleBase_IViewWindow* /* theWnd*/,
-                                                      QMouseEvent* /* theEvent*/)
+bool PartSet_SketcherReetntrantMgr::processMouseMoved(ModuleBase_IViewWindow* theWnd,
+                                                      QMouseEvent* theEvent)
 {
   bool aProcessed = false;
   if (!isActiveMgr())
     return aProcessed;
 
   if  (myIsInternalEditOperation) {
-    PartSet_WidgetPoint2D* aPoint2DWdg = dynamic_cast<PartSet_WidgetPoint2D*>(module()->activeWidget());
-    if (aPoint2DWdg && aPoint2DWdg->canBeActivatedByMove()) {
-      ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
+    ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
                                                          (myWorkshop->currentOperation());
-      FeaturePtr aLastFeature = myRestartingMode == RM_LastFeatureUsed ? aFOperation->feature() : FeaturePtr();
-      restartOperation();
-      aProcessed = true;
-
-      if (aLastFeature) {
-        ModuleBase_IPropertyPanel* aPanel = myWorkshop->currentOperation()->propertyPanel();
-        PartSet_WidgetPoint2D* aPoint2DWdg = dynamic_cast<PartSet_WidgetPoint2D*>(aPanel->activeWidget());
-        if (aPoint2DWdg && aPoint2DWdg->canBeActivatedByMove()) {
-          QList<ModuleBase_ViewerPrs> aSelection;
-          aSelection.append(ModuleBase_ViewerPrs(aLastFeature, TopoDS_Shape(), NULL));
-          if (aPoint2DWdg->setSelection(aSelection, true))
-            aPanel->activateNextWidget(aPoint2DWdg);
+    FeaturePtr aLastFeature = myRestartingMode == RM_LastFeatureUsed ? aFOperation->feature()
+                                                                     : FeaturePtr();
+    if (aLastFeature) {
+      ModuleBase_ModelWidget* anActiveWidget = module()->activeWidget();
+      ModuleBase_IPropertyPanel* aPanel = myWorkshop->currentOperation()->propertyPanel();
+
+      FeaturePtr aCurrentFeature = aFOperation->feature();
+      bool isLineFeature = false, isArcFeature = false;
+      std::string anAttributeOnStart;
+      if (aCurrentFeature->getKind() == SketchPlugin_Line::ID()) {
+        anAttributeOnStart = SketchPlugin_Line::START_ID();
+        isLineFeature = anActiveWidget->attributeID() == anAttributeOnStart;
+      }
+      else if (isTangentArc(aFOperation, module()->sketchMgr()->activeSketch())) {
+        anAttributeOnStart = SketchPlugin_Arc::TANGENT_POINT_ID();
+        isArcFeature = anActiveWidget->attributeID() == anAttributeOnStart;
+      }
+      bool aCanBeActivatedByMove = isLineFeature || isArcFeature;
+      if (aCanBeActivatedByMove) {
+        /// before restarting of operation we need to clear selection, as it may take part in
+        /// new feature creation, e.g. tangent arc. But it is not necessary as it was processed
+        /// by mouse release when the operation was restarted.
+        workshop()->selector()->clearSelection();\r
+
+        myPreviousFeature = aFOperation->feature();
+        restartOperation();
+        myPreviousFeature = FeaturePtr();
+
+        anActiveWidget = module()->activeWidget();
+        aCurrentFeature = anActiveWidget->feature();
+        aProcessed = true;
+        if (anActiveWidget->attributeID() == anAttributeOnStart) {
+          // it was not deactivated by preselection processing
+          aPanel->activateNextWidget(anActiveWidget);
         }
+      } else {
+        // processing mouse move in active widget of restarted operation
+        ModuleBase_ModelWidget* anActiveWidget = module()->activeWidget();
+        PartSet_MouseProcessor* aProcessor = dynamic_cast<PartSet_MouseProcessor*>(anActiveWidget);
+        if (aProcessor)
+          aProcessor->mouseMoved(theWnd, theEvent);
       }
     }
   }
@@ -140,7 +205,26 @@ bool PartSet_SketcherReetntrantMgr::processMouseReleased(ModuleBase_IViewWindow*
 
     ModuleBase_ModelWidget* anActiveWidget = aPanel->activeWidget();
     if (!anActiveWidget || !anActiveWidget->isViewerSelector()) {
+
+      // block of viewer update
+      // we need to block update content of the viewer because of Sketch Point feature
+      // in activate() the value of the point is initialized and it can be displayed
+      // but the default value is [0, 0]. So, we block update viewer contentent until
+      // onMouseRelease happens, which correct the point position
+      ModuleBase_Tools::blockUpdateViewer(true);
+
+      ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
+                                                           (myWorkshop->currentOperation());
+      myPreviousFeature = aFOperation->feature();
+
+      /// selection should be obtained from workshop before ask if the operation can be started as
+      /// the canStartOperation method performs commit/abort of previous operation. Sometimes commit/abort
+      /// may cause selection clear(Sketch operation) as a result it will be lost and is not used for preselection.
+      ModuleBase_ISelection* aSelection = myWorkshop->selection();
+      QList<ModuleBase_ViewerPrsPtr> aPreSelected = aSelection->getSelected(ModuleBase_ISelection::AllControls);
+
       restartOperation();
+      myPreviousFeature = FeaturePtr();
       aProcessed = true;
 
       // fill the first widget by the mouse event point
@@ -149,8 +233,14 @@ bool PartSet_SketcherReetntrantMgr::processMouseReleased(ModuleBase_IViewWindow*
       PartSet_WidgetPoint2D* aPoint2DWdg = dynamic_cast<PartSet_WidgetPoint2D*>(module()->activeWidget());
       ModuleBase_ModelWidget* aFirstWidget = aPanel->findFirstAcceptingValueWidget();
       if (aPoint2DWdg && aPoint2DWdg == aFirstWidget) {
-        aPoint2DWdg->onMouseRelease(theWnd, theEvent);
+        if (!aPreSelected.empty())
+          aPoint2DWdg->setPreSelection(aPreSelected.front());
+        aPoint2DWdg->mouseReleased(theWnd, theEvent);
+        if (!aPreSelected.empty())
+          aPoint2DWdg->setPreSelection(ModuleBase_ViewerPrsPtr());
       }
+      // unblock viewer update
+      ModuleBase_Tools::blockUpdateViewer(false);
     }
   }
 
@@ -193,7 +283,7 @@ void PartSet_SketcherReetntrantMgr::onNoMoreWidgets(const std::string& thePrevio
   if (!myWorkshop->module()->getFeatureError(aFOperation->feature()).isEmpty())
     return;
 
-  if (aFOperation && PartSet_SketcherMgr::isNestedSketchOperation(aFOperation)) {
+  if (aFOperation && module()->sketchMgr()->isNestedSketchOperation(aFOperation)) {
     bool isStarted = false;
     if (!module()->sketchMgr()->sketchSolverError()) {
       if (myRestartingMode != RM_Forbided) {
@@ -238,10 +328,12 @@ void PartSet_SketcherReetntrantMgr::onVertexSelected()
   if (!isActiveMgr())
     return;
 
-  ModuleBase_Operation* aOperation = myWorkshop->currentOperation();
-  if (aOperation->id().toStdString() == SketchPlugin_Line::ID()) {
+  ModuleBase_Operation* anOperation = myWorkshop->currentOperation();
+  std::string anOperationId = anOperation->id().toStdString();
+  if (anOperationId == SketchPlugin_Line::ID() ||
+      isTangentArc(anOperation, module()->sketchMgr()->activeSketch())) {
     /// If last line finished on vertex the lines creation sequence has to be break
-    ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
+    ModuleBase_IPropertyPanel* aPanel = anOperation->propertyPanel();
     ModuleBase_ModelWidget* anActiveWidget = aPanel->activeWidget();
     const QList<ModuleBase_ModelWidget*>& aWidgets = aPanel->modelWidgets();
     QList<ModuleBase_ModelWidget*>::const_iterator anIt = aWidgets.begin(), aLast = aWidgets.end();
@@ -271,13 +363,18 @@ bool PartSet_SketcherReetntrantMgr::canBeCommittedByPreselection()
   return !isActiveMgr() || myRestartingMode == RM_None;
 }
 
+bool PartSet_SketcherReetntrantMgr::isInternalEditStarted() const
+{
+  return myIsInternalEditOperation;
+}
+
 bool PartSet_SketcherReetntrantMgr::isActiveMgr() const
 {
   ModuleBase_Operation* aCurrentOperation = myWorkshop->currentOperation();
 
   bool anActive = PartSet_SketcherMgr::isSketchOperation(aCurrentOperation);
   if (!anActive) {
-    anActive = PartSet_SketcherMgr::isNestedSketchOperation(aCurrentOperation);
+    anActive = module()->sketchMgr()->isNestedSketchOperation(aCurrentOperation);
     if (anActive) { // the manager is not active when the current operation is a usual Edit
       ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
                                                        (myWorkshop->currentOperation());
@@ -300,13 +397,13 @@ bool PartSet_SketcherReetntrantMgr::startInternalEdit(const std::string& thePrev
   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
                                                      (myWorkshop->currentOperation());
 
-  if (aFOperation && PartSet_SketcherMgr::isNestedSketchOperation(aFOperation)) {
-    aFOperation->setEditOperation(false);
-    workshop()->operationMgr()->updateApplyOfOperations();
-
+  if (aFOperation && module()->sketchMgr()->isNestedSketchOperation(aFOperation)) {
+    aFOperation->setEditOperation(true/*, false*/);
     createInternalFeature();
 
     myIsInternalEditOperation = true;
+    updateAcceptAllAction();
+
     isDone = true;
     connect(aFOperation, SIGNAL(beforeCommitted()), this, SLOT(onBeforeStopped()));
     connect(aFOperation, SIGNAL(beforeAborted()), this, SLOT(onBeforeStopped()));
@@ -322,8 +419,12 @@ bool PartSet_SketcherReetntrantMgr::startInternalEdit(const std::string& thePrev
         ModuleBase_ModelWidget* aPreviousAttributeWidget = 0;
         QList<ModuleBase_ModelWidget*> aWidgets = aPanel->modelWidgets();
         for (int i = 0, aNb = aWidgets.size(); i < aNb && !aPreviousAttributeWidget; i++) {
-          if (aWidgets[i]->attributeID() == thePreviousAttributeID)
+          if (aWidgets[i]->attributeID() == thePreviousAttributeID) {
+          /// workaround for the same attributes used in different stacked widgets(attribute types)
+          if (ModuleBase_ToolBox::isOffToolBoxParent(aWidgets[i]))
+            continue;
             aPreviousAttributeWidget = aWidgets[i];
+          }
         }
         // If the current widget is a selector, do nothing, it processes the mouse press
         if (aPreviousAttributeWidget) {
@@ -341,7 +442,7 @@ bool PartSet_SketcherReetntrantMgr::startInternalEdit(const std::string& thePrev
             }
             // if there is no the next widget to be automatically activated, the Ok button in property
             // panel should accept the focus(example is parallel constraint on sketch lines)
-            QToolButton* anOkBtn = aPanel->findChild<QToolButton*>(PROP_PANEL_OK);
+            QToolButton* anOkBtn = dynamic_cast<XGUI_PropertyPanel*>(aPanel)->findButton(PROP_PANEL_OK);
             if (anOkBtn)
               anOkBtn->setFocus(Qt::TabFocusReason);
           }
@@ -375,7 +476,6 @@ void PartSet_SketcherReetntrantMgr::restartOperation()
     if (aFOperation) {
       myNoMoreWidgetsAttribute = "";
       myIsFlagsBlocked = true;
-      aFOperation->commit();
       module()->launchOperation(aFOperation->id());
       myIsFlagsBlocked = false;
       resetFlags();
@@ -396,11 +496,14 @@ void PartSet_SketcherReetntrantMgr::createInternalFeature()
   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
                                                      (myWorkshop->currentOperation());
 
-  if (aFOperation && PartSet_SketcherMgr::isNestedSketchOperation(aFOperation)) {
+  if (aFOperation && module()->sketchMgr()->isNestedSketchOperation(aFOperation)) {
     FeaturePtr anOperationFeature = aFOperation->feature();
 
     CompositeFeaturePtr aSketch = module()->sketchMgr()->activeSketch();
     myInternalFeature = aSketch->addFeature(anOperationFeature->getKind());
+
+    bool isFeatureChanged = copyReetntrantAttributes(anOperationFeature, myInternalFeature,
+                                                     aSketch, false);
     XGUI_PropertyPanel* aPropertyPanel = dynamic_cast<XGUI_PropertyPanel*>
                                                   (aFOperation->propertyPanel());
 
@@ -420,7 +523,10 @@ void PartSet_SketcherReetntrantMgr::createInternalFeature()
                           !aWidget->getDefaultValue().empty() &&
                           !aWidget->isComputedDefault();
       aWidget->setFeature(myInternalFeature, isStoreValue);
+      if (!isStoreValue && isFeatureChanged)
+        aWidget->restoreValue();
     }
+
     ModuleBase_ModelWidget* aFirstWidget = ModuleBase_IPropertyPanel::findFirstAcceptingValueWidget
                                                                                         (aWidgets);
     if (aFirstWidget)
@@ -442,16 +548,109 @@ void PartSet_SketcherReetntrantMgr::deleteInternalFeature()
   QObjectPtrList anObjects;
   anObjects.append(myInternalFeature);
   workshop()->deleteFeatures(anObjects);
+  myInternalFeature = FeaturePtr();
 }
 
 void PartSet_SketcherReetntrantMgr::resetFlags()
 {
   if (!myIsFlagsBlocked) {
     myIsInternalEditOperation = false;
+    updateAcceptAllAction();
     myRestartingMode = RM_None;
   }
 }
 
+bool PartSet_SketcherReetntrantMgr::copyReetntrantAttributes(const FeaturePtr& theSourceFeature,
+                                                             const FeaturePtr& theNewFeature,
+                                                             const CompositeFeaturePtr& theSketch,
+                                                             const bool isTemporary)
+{
+  bool aChanged = false;
+  if (!theSourceFeature.get() || !theSourceFeature->data().get() ||
+      !theSourceFeature->data()->isValid())
+    return aChanged;
+
+  std::string aFeatureKind = theSourceFeature->getKind();
+  if (aFeatureKind == SketchPlugin_Line::ID()) {
+    // Initialize new line with first point equal to end of previous
+    std::shared_ptr<ModelAPI_Data> aSFData = theSourceFeature->data();
+    std::shared_ptr<GeomDataAPI_Point2D> aSPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+                                                 aSFData->attribute(SketchPlugin_Line::END_ID()));
+    std::shared_ptr<ModelAPI_Data> aNFData = theNewFeature->data();
+    std::shared_ptr<GeomDataAPI_Point2D> aNPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+                                                 aNFData->attribute(SketchPlugin_Line::START_ID()));
+    aNPoint->setValue(aSPoint->x(), aSPoint->y());
+    PartSet_Tools::createConstraint(theSketch, aSPoint, aNPoint);
+
+    aNPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+                                                 aSFData->attribute(SketchPlugin_Line::END_ID()));
+    aNPoint->setValue(aSPoint->x(), aSPoint->y());
+  }
+  else if (aFeatureKind == SketchPlugin_Circle::ID()) {
+    // set circle type
+    std::string aTypeAttributeId = SketchPlugin_Circle::CIRCLE_TYPE();
+    AttributeStringPtr aSourceFeatureTypeAttr = theSourceFeature->data()->string(aTypeAttributeId);
+    AttributeStringPtr aNewFeatureTypeAttr = theNewFeature->data()->string(aTypeAttributeId);
+    aNewFeatureTypeAttr->setValue(aSourceFeatureTypeAttr->value());
+    //ModuleBase_Tools::flushUpdated(theNewFeature);
+    aChanged = true;
+  }
+  else if (aFeatureKind == SketchPlugin_Arc::ID()) {
+    // set arc type
+    std::string aTypeAttributeId = SketchPlugin_Arc::ARC_TYPE();
+    AttributeStringPtr aSourceFeatureTypeAttr = theSourceFeature->data()->string(aTypeAttributeId);
+    AttributeStringPtr aNewFeatureTypeAttr = theNewFeature->data()->string(aTypeAttributeId);
+    aNewFeatureTypeAttr->setValue(aSourceFeatureTypeAttr->value());
+
+    // if the arc is tangent, set coincidence to end point of the previous arc
+    std::string anArcType = aSourceFeatureTypeAttr->value();
+    if (anArcType == SketchPlugin_Arc::ARC_TYPE_TANGENT()) {
+      // get the last point of the previuos arc feature(geom point 2d)
+      std::shared_ptr<ModelAPI_Data> aSData = theSourceFeature->data();
+      std::shared_ptr<GeomDataAPI_Point2D> aSPointAttr = 
+                                      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+                                      aSData->attribute(SketchPlugin_Arc::END_ID()));
+      // get point attribute on the current feature
+      AttributeRefAttrPtr aTangentPointAttr = theNewFeature->data()->refattr(
+                                                    SketchPlugin_Arc::TANGENT_POINT_ID());
+      aTangentPointAttr->setAttr(aSPointAttr);
+
+      std::shared_ptr<GeomDataAPI_Point2D> aNPointAttr = 
+                                    std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+                                    theNewFeature->data()->attribute(SketchPlugin_Arc::END_ID()));
+      aNPointAttr->setValue(aSPointAttr->x(), aSPointAttr->y());
+
+    }
+    //ModuleBase_Tools::flushUpdated(theNewFeature);
+    aChanged = true;
+  }
+  return aChanged;
+}
+
+bool PartSet_SketcherReetntrantMgr::isTangentArc(ModuleBase_Operation* theOperation,
+                                                 const CompositeFeaturePtr& /*theSketch*/) const
+{
+  bool aTangentArc = false;
+  ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
+                                                                        (theOperation);
+  if (aFOperation && module()->sketchMgr()->isNestedSketchOperation(aFOperation)) {
+    FeaturePtr aFeature = aFOperation->feature();
+    if (aFeature.get() && aFeature->getKind() == SketchPlugin_Arc::ID()) {
+      AttributeStringPtr aTypeAttr = aFeature->data()->string(SketchPlugin_Arc::ARC_TYPE());
+      std::string anArcType = aTypeAttr.get() ? aTypeAttr->value() : "";
+      aTangentArc = anArcType == SketchPlugin_Arc::ARC_TYPE_TANGENT();
+    }
+  }
+  return aTangentArc;
+}
+
+void PartSet_SketcherReetntrantMgr::updateAcceptAllAction()
+{
+  CompositeFeaturePtr aSketch = module()->sketchMgr()->activeSketch();
+  if (aSketch.get())
+    workshop()->errorMgr()->updateAcceptAllAction(aSketch);
+}
+
 XGUI_Workshop* PartSet_SketcherReetntrantMgr::workshop() const
 {
   XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myWorkshop);