Salome HOME
Merge remote-tracking branch 'remotes/origin/HigherLevelObjectsHistory'
[modules/shaper.git] / src / XGUI / XGUI_OperationMgr.cpp
index ac99247118721ec8df39363c731c581975ab0851..e00490f8e16789f31d2ae213fe75a0857a207d2d 100644 (file)
@@ -1,13 +1,31 @@
-// Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
-
-// File:        XGUI_OperationMgr.cpp
-// Created:     20 Apr 2014
-// Author:      Natalia ERMOLAEVA
+// Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
 
 #include "XGUI_OperationMgr.h"
+
+#include "XGUI_ActiveControlMgr.h"
+#include "XGUI_ActiveControlSelector.h"
+#include "XGUI_FacesPanelSelector.h"
 #include "XGUI_ModuleConnector.h"
 #include "XGUI_Workshop.h"
 #include "XGUI_ErrorMgr.h"
+#include "XGUI_FacesPanel.h"
 #include "XGUI_Tools.h"
 #include "XGUI_ObjectsBrowser.h"
 #include "XGUI_ContextMenuMgr.h"
@@ -45,7 +63,7 @@ public:
   /// \param theParent the parent to be deleted when the parent is deleted
   /// \param theOperationMgr the class to perform deletion
   XGUI_ShortCutListener(QObject* theParent, XGUI_OperationMgr* theOperationMgr)
-    : QObject(theParent), myOperationMgr(theOperationMgr)
+    : QObject(theParent), myOperationMgr(theOperationMgr), myIsActive(false)
   {
     qApp->installEventFilter(this);
   }
@@ -58,15 +76,32 @@ public:
   virtual bool eventFilter(QObject *theObject, QEvent *theEvent)
   {
     bool isAccepted = false;
-    if (myIsActive && theEvent->type() == QEvent::KeyRelease) {
-      QKeyEvent* aKeyEvent = dynamic_cast<QKeyEvent*>(theEvent);
-      if(aKeyEvent) {
-        switch (aKeyEvent->key()) {
-          case Qt::Key_Delete: {
-            isAccepted = myOperationMgr->onProcessDelete(theObject);
+    if (myIsActive) {
+      if (theEvent->type() == QEvent::KeyRelease) {
+        QKeyEvent* aKeyEvent = dynamic_cast<QKeyEvent*>(theEvent);
+        if (aKeyEvent) {
+          myOperationMgr->setSHIFTPressed(aKeyEvent->modifiers() & Qt::ShiftModifier);
+          switch (aKeyEvent->key()) {
+            case Qt::Key_Delete:
+              isAccepted = myOperationMgr->onProcessDelete(theObject);
+            break;
+            default:
+              isAccepted = myOperationMgr->onKeyReleased(theObject, aKeyEvent);
+              break;
           }
         }
       }
+      else if (theEvent->type() == QEvent::KeyPress) {
+        QKeyEvent* aKeyEvent = dynamic_cast<QKeyEvent*>(theEvent);
+        myOperationMgr->setSHIFTPressed(aKeyEvent->modifiers() & Qt::ShiftModifier);
+        switch (aKeyEvent->key()) {
+          case Qt::Key_Escape:
+            isAccepted = myOperationMgr->onKeyPressed(theObject, aKeyEvent);
+          break;
+          default:
+            break;
+        }
+      }
     }
     if (!isAccepted)
       isAccepted = QObject::eventFilter(theObject, theEvent);
@@ -80,7 +115,7 @@ private:
 
 XGUI_OperationMgr::XGUI_OperationMgr(QObject* theParent,
                                      ModuleBase_IWorkshop* theWorkshop)
-: QObject(theParent), myWorkshop(theWorkshop)
+: QObject(theParent), myWorkshop(theWorkshop), mySHIFTPressed(false), myActiveMessageBox(0)
 {
   /// we need to install filter to the application in order to react to 'Delete' key button
   /// this key can not be a short cut for a corresponded action because we need to set
@@ -171,18 +206,16 @@ ModuleBase_Operation* XGUI_OperationMgr::previousOperation(ModuleBase_Operation*
   return myOperations.at(idx - 1);
 }
 
-bool XGUI_OperationMgr::eventFilter(QObject *theObject, QEvent *theEvent)
+ModuleBase_ModelWidget* XGUI_OperationMgr::activeWidget() const
 {
-  bool isAccepted = false;
-  if (theEvent->type() == QEvent::KeyRelease) {
-    QKeyEvent* aKeyEvent = dynamic_cast<QKeyEvent*>(theEvent);
-    if(aKeyEvent)
-      isAccepted = onKeyReleased(theObject, aKeyEvent);
+  ModuleBase_ModelWidget* anActiveWidget = 0;
+  ModuleBase_Operation* anOperation = currentOperation();
+  if (anOperation) {
+    ModuleBase_IPropertyPanel* aPanel = anOperation->propertyPanel();
+    if (aPanel)
+      anActiveWidget = aPanel->activeWidget();
   }
-  if (!isAccepted)
-    isAccepted = QObject::eventFilter(theObject, theEvent);
-
-  return isAccepted;
+  return anActiveWidget;
 }
 
 bool XGUI_OperationMgr::startOperation(ModuleBase_Operation* theOperation)
@@ -208,7 +241,7 @@ bool XGUI_OperationMgr::startOperation(ModuleBase_Operation* theOperation)
   return isStarted;
 }
 
-bool XGUI_OperationMgr::abortAllOperations()
+bool XGUI_OperationMgr::abortAllOperations(const XGUI_MessageKind& theMessageKind)
 {
   bool aResult = true;
   if(!hasOperation())
@@ -216,18 +249,25 @@ bool XGUI_OperationMgr::abortAllOperations()
 
   if (operationsCount() == 1) {
     ModuleBase_Operation* aCurrentOperation = currentOperation();
-    if (canStopOperation(aCurrentOperation)) {
+    if (canStopOperation(aCurrentOperation, theMessageKind)) {
       abortOperation(aCurrentOperation);
     }
     else
       aResult = false;
   }
   else {
-    aResult = QMessageBox::question(qApp->activeWindow(),
-                                    tr("Abort operation"),
-                                    tr("All active operations will be aborted."),
-                                    QMessageBox::Ok | QMessageBox::Cancel,
-                                    QMessageBox::Cancel) == QMessageBox::Ok;
+    if (theMessageKind == XGUI_AbortOperationMessage) {
+      myActiveMessageBox = createMessageBox(tr("All active operations will be aborted."));
+      aResult = myActiveMessageBox->exec() == QMessageBox::Ok;
+      myActiveMessageBox = 0;
+    }
+    else if (theMessageKind == XGUI_InformationMessage) {
+      QString aMessage = tr("Please validate all your active operations before saving.");
+      myActiveMessageBox = createInformationBox(aMessage);
+      myActiveMessageBox->exec();
+      myActiveMessageBox = 0;
+      aResult = false; // do not perform abort
+    }
     while(aResult && hasOperation()) {
       abortOperation(currentOperation());
     }
@@ -241,7 +281,7 @@ bool XGUI_OperationMgr::commitAllOperations()
   while (hasOperation()) {
     ModuleBase_Operation* anOperation = currentOperation();
     if (XGUI_Tools::workshop(myWorkshop)->errorMgr()->isApplyEnabled()) {
-      anOperationProcessed = onCommitOperation();
+      anOperationProcessed = commitOperation();
     } else {
       abortOperation(anOperation);
       anOperationProcessed = true;
@@ -295,33 +335,32 @@ void XGUI_OperationMgr::updateApplyOfOperations(ModuleBase_Operation* theOperati
   onValidateOperation();
 }
 
-bool XGUI_OperationMgr::canStopOperation(ModuleBase_Operation* theOperation)
+bool XGUI_OperationMgr::canStopOperation(ModuleBase_Operation* theOperation,
+                                         const XGUI_OperationMgr::XGUI_MessageKind& theMessageKind)
 {
   //in case of nested (sketch) operation no confirmation needed
   if (isGrantedOperation(theOperation->id()))
     return true;
   if (theOperation && theOperation->isModified()) {
-    QString aMessage = tr("%1 operation will be aborted.").arg(theOperation->id());
-    int anAnswer = QMessageBox::question(qApp->activeWindow(),
-                                         tr("Abort operation"),
-                                         aMessage,
-                                         QMessageBox::Ok | QMessageBox::Cancel,
-                                         QMessageBox::Cancel);
-    return anAnswer == QMessageBox::Ok;
+    QString aTitle = theOperation->getDescription()->description();
+    if (theMessageKind == XGUI_AbortOperationMessage) {
+      QString aMessage = tr("%1 operation will be aborted.").arg(aTitle);
+      myActiveMessageBox = createMessageBox(aMessage);
+      bool aResult = myActiveMessageBox->exec() == QMessageBox::Ok;
+      myActiveMessageBox = 0;
+      return aResult;
+    }
+    else if (theMessageKind == XGUI_InformationMessage) {
+      QString aMessage = tr("Please validate your %1 before saving.").arg(aTitle);
+      myActiveMessageBox = createInformationBox(aMessage);
+      myActiveMessageBox->exec();
+      myActiveMessageBox = 0;
+      return false;
+    }
   }
   return true;
 }
 
-bool XGUI_OperationMgr::commitOperation()
-{
-  //if (hasOperation() && currentOperation()->isValid()) {
-  //  onCommitOperation();
-  //  return true;
-  //}
-  //return false;
-  return onCommitOperation();
-}
-
 void XGUI_OperationMgr::resumeOperation(ModuleBase_Operation* theOperation)
 {
   theOperation->resume();
@@ -393,9 +432,10 @@ bool XGUI_OperationMgr::canStartOperation(const QString& theId, bool& isCommitte
 
 void XGUI_OperationMgr::stopOperation(ModuleBase_Operation* theOperation, bool& isCommitted)
 {
-  if (XGUI_Tools::workshop(myWorkshop)->errorMgr()->isApplyEnabled() && theOperation->isModified())
+  if (XGUI_Tools::workshop(myWorkshop)->errorMgr()->isApplyEnabled() &&
+      theOperation->isModified()) {
     isCommitted = theOperation->commit();
-  else {
+  else {
     isCommitted = false;
     abortOperation(theOperation);
   }
@@ -419,7 +459,7 @@ void XGUI_OperationMgr::abortOperation(ModuleBase_Operation* theOperation)
   }
 }
 
-bool XGUI_OperationMgr::onCommitOperation()
+bool XGUI_OperationMgr::commitOperation()
 {
   bool isCommitted = false;
   ModuleBase_Operation* anOperation = currentOperation();
@@ -436,6 +476,11 @@ void XGUI_OperationMgr::onAbortOperation()
   }
 }
 
+void XGUI_OperationMgr::onAbortAllOperation()
+{
+  abortAllOperations();
+}
+
 void XGUI_OperationMgr::onBeforeOperationStarted()
 {
   ModuleBase_Operation* aCurrentOperation = dynamic_cast<ModuleBase_Operation*>(sender());
@@ -467,9 +512,7 @@ void XGUI_OperationMgr::onBeforeOperationStarted()
             .arg(aFOperation->isEditOperation())
             .arg(ModuleBase_Tools::objectName(aFeature)).toStdString().c_str());
     qDebug(QString("   document->currentFeature(false) = %1 : DO: setPreviousCurrentFeature").arg(
-            ModuleBase_Tools::objectName(
-            ModelAPI_Session::get()->activeDocument()->currentFeature(false)))
-            .toStdString().c_str());
+            ModuleBase_Tools::objectName(aDoc->currentFeature(false))).toStdString().c_str());
 #endif
 
     if (aFOperation->isEditOperation()) {// it should be performed by the feature edit only
@@ -477,8 +520,7 @@ void XGUI_OperationMgr::onBeforeOperationStarted()
       aDoc->setCurrentFeature(aFOperation->feature(), false);
 #ifdef DEBUG_CURRENT_FEATURE
       qDebug(QString("   document->setCurrentFeature(false) = %1").arg(
-             ModuleBase_Tools::objectName(
-             ModelAPI_Session::get()->activeDocument()->currentFeature(false))).toStdString().c_str());
+             ModuleBase_Tools::objectName(aDoc->currentFeature(false))).toStdString().c_str());
 #endif
       // this is the only place where flushes must be called after setCurrentFeature for the
       // current moment: after this the opertion is not finished, so, the ObjectBrowser
@@ -508,6 +550,8 @@ void XGUI_OperationMgr::onBeforeOperationAborted()
 void XGUI_OperationMgr::onOperationAborted()
 {
   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
+  XGUI_Workshop* aWorkshop = XGUI_Tools::workshop(myWorkshop);
+  aWorkshop->setStatusBarMessage("");
   emit operationAborted(aSenderOperation);
 }
 
@@ -599,10 +643,30 @@ void XGUI_OperationMgr::onOperationStopped()
 
 bool XGUI_OperationMgr::onKeyReleased(QObject *theObject, QKeyEvent* theEvent)
 {
+  bool isAccepted = false;
+
   // Let the manager decide what to do with the given key combination.
   ModuleBase_Operation* anOperation = currentOperation();
-  bool isAccepted = false;
   switch (theEvent->key()) {
+    case Qt::Key_Tab:
+    case Qt::Key_Backtab:
+    {
+      ModuleBase_Operation* aOperation = currentOperation();
+      if (aOperation) {
+        ModuleBase_IPropertyPanel* aPanel = anOperation->propertyPanel();
+        if (aPanel) {
+          QWidget* aFocusedWidget = qApp->focusWidget();
+          bool isPPChildObject = aFocusedWidget && isChildObject(aFocusedWidget, aPanel);
+          if (!isPPChildObject) {
+            // check for case when the operation is started but property panel is not filled
+            XGUI_PropertyPanel* aPP = dynamic_cast<XGUI_PropertyPanel*>(aPanel);
+            aPP->setFocusNextPrevChild(theEvent->key() == Qt::Key_Tab);
+            isAccepted = true;
+          }
+        }
+      }
+    }
+    break;
     case Qt::Key_Return:
     case Qt::Key_Enter: {
       isAccepted = onProcessEnter(theObject);
@@ -620,6 +684,8 @@ bool XGUI_OperationMgr::onKeyReleased(QObject *theObject, QKeyEvent* theEvent)
             aContext->HilightNextDetected(aView);
           else if ((theEvent->key() == Qt::Key_P))
             aContext->HilightPreviousDetected(aView);
+          aViewer->updateHighlight();
+          isAccepted = true;
         }
       }
     }
@@ -635,6 +701,53 @@ bool XGUI_OperationMgr::onKeyReleased(QObject *theObject, QKeyEvent* theEvent)
   return isAccepted;
 }
 
+bool XGUI_OperationMgr::onKeyPressed(QObject *theObject, QKeyEvent* theEvent)
+{
+  // Let the manager decide what to do with the given key combination.
+  ModuleBase_Operation* anOperation = currentOperation();
+  bool isAccepted = false;
+  switch (theEvent->key()) {
+    case Qt::Key_Escape: {
+      // processing in message box
+      if (myActiveMessageBox)
+      {
+        myActiveMessageBox->reject();
+        isAccepted = true;
+      }
+      // processing in the active widget
+      ModuleBase_Operation* aOperation = currentOperation();
+      if (!isAccepted && aOperation) {
+        ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
+        ModuleBase_ModelWidget* anActiveWgt = aPanel->activeWidget();
+        if (anActiveWgt)
+        {
+          isAccepted = anActiveWgt && anActiveWgt->processAction(ActionEscape);
+          if (isAccepted) {
+            ModuleBase_OperationFeature* aFOperation =
+              dynamic_cast<ModuleBase_OperationFeature*>(currentOperation());
+            if (aFOperation)
+              aFOperation->setNeedToBeAborted(true);
+          }
+        }
+      }
+      if (!isAccepted)
+      {
+        XGUI_ActiveControlSelector* anActiveSelector =
+          XGUI_Tools::workshop(myWorkshop)->activeControlMgr()->activeSelector();
+        if (anActiveSelector && anActiveSelector->getType() == XGUI_FacesPanelSelector::Type())
+          isAccepted = XGUI_Tools::workshop(myWorkshop)->facesPanel()->processAction(ActionEscape);
+      }
+      // default Escape button functionality
+      if (!isAccepted && aOperation) {
+        onAbortOperation();
+        isAccepted = true;
+      }
+    }
+    break;
+  }
+  return isAccepted;
+}
+
 bool XGUI_OperationMgr::onProcessEnter(QObject* theObject)
 {
   bool isAccepted = false;
@@ -643,10 +756,13 @@ bool XGUI_OperationMgr::onProcessEnter(QObject* theObject)
   if (!aOperation)
     return isAccepted;
   ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
-  // only property panel enter is processed in order to do not process enter in application dialogs
-  bool isPPChild = isChildObject(theObject, aPanel);
-  if (!isPPChild)
+  if (!aPanel)
     return isAccepted;
+  // the next code is obsolete as we want to process Enter in property panel always
+  // only property panel enter is processed in order to do not process enter in application dialogs
+  //bool isPPChild = isChildObject(theObject, aPanel);
+  //if (!isPPChild)
+  //  return isAccepted;
 
   ModuleBase_ModelWidget* anActiveWgt = aPanel->activeWidget();
   bool isAborted = false;
@@ -661,7 +777,7 @@ bool XGUI_OperationMgr::onProcessEnter(QObject* theObject)
     }
   }
   if (!isAborted) {
-    isAccepted = anActiveWgt && anActiveWgt->processEnter();
+    isAccepted = anActiveWgt && anActiveWgt->processAction(ActionEnter);
     if (!isAccepted) {
       isAccepted =
         myWorkshop->module()->processEnter(anActiveWgt ? anActiveWgt->attributeID() : "");
@@ -708,11 +824,18 @@ bool XGUI_OperationMgr::onProcessDelete(QObject* theObject)
       if (isPPChildObject) {
         anActiveWgt = aPanel->activeWidget();
         if (anActiveWgt) {
-          isAccepted = anActiveWgt->processDelete();
+          isAccepted = anActiveWgt->processAction(ActionDelete);
         }
       }
     }
   }
+  if (!isAccepted)
+  {
+    XGUI_ActiveControlSelector* anActiveSelector =
+      XGUI_Tools::workshop(myWorkshop)->activeControlMgr()->activeSelector();
+    if (anActiveSelector && anActiveSelector->getType() == XGUI_FacesPanelSelector::Type())
+      isAccepted = XGUI_Tools::workshop(myWorkshop)->facesPanel()->processAction(ActionDelete);
+  }
   if (!isAccepted) {
     // after widget, object browser and viewer should process delete
     /// other widgets such as line edit controls should not lead to
@@ -762,3 +885,25 @@ bool XGUI_OperationMgr::isChildObject(const QObject* theObject, const QObject* t
   }
   return isPPChild;
 }
+
+QMessageBox* XGUI_OperationMgr::createMessageBox(const QString& theMessage)
+{
+  QMessageBox * aMessageBox = new QMessageBox(QMessageBox::Question,
+    QObject::tr("Abort operation"), theMessage, QMessageBox::Ok | QMessageBox::Cancel,
+    qApp->activeWindow());
+  aMessageBox->setDefaultButton(QMessageBox::Cancel);
+  aMessageBox->setEscapeButton(QMessageBox::No); // operation manager should process Esc key
+
+  return aMessageBox;
+}
+
+QMessageBox* XGUI_OperationMgr::createInformationBox(const QString& theMessage)
+{
+  QMessageBox * aMessageBox = new QMessageBox(QMessageBox::Question,
+    QObject::tr("Validate operation"), theMessage, QMessageBox::Ok,
+    qApp->activeWindow());
+  aMessageBox->setDefaultButton(QMessageBox::Ok);
+  aMessageBox->setEscapeButton(QMessageBox::No); // operation manager should process Esc key
+
+  return aMessageBox;
+}