Salome HOME
Improvement of the Group Feature (Undo-Redo functionality inside the multiple selecti...
[modules/shaper.git] / src / ModuleBase / ModuleBase_WidgetMultiSelector.cpp
index fe5e99ecc75774a09de5d31c28f4b29f7b17edd5..409c2949b32815cc215f6140d6a7916066b788c2 100755 (executable)
@@ -1,11 +1,22 @@
-// Copyright (C) 2014-20xx CEA/DEN, EDF R&D
-
-/*
- * ModuleBase_WidgetMultiSelector.cpp
- *
- *  Created on: Aug 28, 2014
- *      Author: sbh
- */
+// Copyright (C) 2014-2017  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<mailto:webmaster.salome@opencascade.com>
+//
 
 #include <ModuleBase_WidgetMultiSelector.h>
 #include <ModuleBase_WidgetShapeSelector.h>
@@ -47,6 +58,8 @@
 
 const int ATTRIBUTE_SELECTION_INDEX_ROLE = Qt::UserRole + 1;
 
+//#define DEBUG_UNDO_REDO
+
 /**
 * Customization of a List Widget to make it to be placed on full width of container
 */
@@ -88,10 +101,27 @@ protected:
 #endif
 };
 
+#ifdef DEBUG_UNDO_REDO
+void printHistoryInfo(const QString& theMethodName, int theCurrentHistoryIndex,
+  QList<QList<std::shared_ptr<ModuleBase_ViewerPrs> > > theSelectedHistoryValues)
+{
+  QStringList aSizes;
+  for (int i = 0; i < theSelectedHistoryValues.size(); i++)
+    aSizes.append(QString::number(theSelectedHistoryValues[i].size()));
+
+  std::cout << theMethodName.toStdString()
+            << "  current = " << theCurrentHistoryIndex
+            << " size(history) =  " << theSelectedHistoryValues.size()
+            << " (" << aSizes.join(", ").toStdString() << ")"
+            << std::endl;
+}
+#endif
+
 ModuleBase_WidgetMultiSelector::ModuleBase_WidgetMultiSelector(QWidget* theParent,
                                                                ModuleBase_IWorkshop* theWorkshop,
                                                                const Config_WidgetAPI* theData)
-: ModuleBase_WidgetSelector(theParent, theWorkshop, theData)
+: ModuleBase_WidgetSelector(theParent, theWorkshop, theData),
+  myIsSetSelectionBlocked(false), myCurrentHistoryIndex(-1)
 {
   QGridLayout* aMainLay = new QGridLayout(this);
   ModuleBase_Tools::adjustMargins(aMainLay);
@@ -174,6 +204,8 @@ void ModuleBase_WidgetMultiSelector::activateCustom()
 
   myWorkshop->module()->activateCustomPrs(myFeature,
                             ModuleBase_IModule::CustomizeHighlightedObjects, true);
+  clearSelectedHistory();
+  myWorkshop->updateCommandStatus();
 }
 
 //********************************************************************
@@ -182,6 +214,8 @@ void ModuleBase_WidgetMultiSelector::deactivate()
   ModuleBase_WidgetSelector::deactivate();
 
   myWorkshop->module()->deactivateCustomPrs(ModuleBase_IModule::CustomizeHighlightedObjects, true);
+  clearSelectedHistory();
+  myWorkshop->updateCommandStatus();
 }
 
 //********************************************************************
@@ -225,6 +259,9 @@ bool ModuleBase_WidgetMultiSelector::restoreValueCustom()
 bool ModuleBase_WidgetMultiSelector::setSelection(QList<ModuleBase_ViewerPrsPtr>& theValues,
                                                   const bool theToValidate)
 {
+  if (myIsSetSelectionBlocked)
+    return false;
+
   AttributeSelectionListPtr aSelectionListAttr;
   if (attribute()->attributeType() == ModelAPI_AttributeSelectionList::typeId())
     aSelectionListAttr = std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(attribute());
@@ -294,6 +331,63 @@ void ModuleBase_WidgetMultiSelector::getHighlighted(QList<ModuleBase_ViewerPrsPt
     convertIndicesToViewerSelection(anAttributeIds, theValues);
 }
 
+//********************************************************************
+bool ModuleBase_WidgetMultiSelector::canProcessAction(ModuleBase_ActionType theActionType,
+                                                      bool& isActionEnabled)
+{
+  isActionEnabled = false;
+  bool aCanProcess = false;
+  switch (theActionType) {
+    case ActionUndo:
+    case ActionRedo: {
+      aCanProcess = true;
+      isActionEnabled = theActionType == ActionUndo ? myCurrentHistoryIndex > 0
+          : (mySelectedHistoryValues.size() > 0 &&
+             myCurrentHistoryIndex < mySelectedHistoryValues.size() - 1);
+    }
+    break;
+    default:
+    break;
+  }
+  return aCanProcess;
+}
+
+//********************************************************************
+bool ModuleBase_WidgetMultiSelector::processAction(ModuleBase_ActionType theActionType)
+{
+  switch (theActionType) {
+    case ActionUndo:
+    case ActionRedo: {
+      if (theActionType == ActionUndo)
+        myCurrentHistoryIndex--;
+      else
+        myCurrentHistoryIndex++;
+      QList<ModuleBase_ViewerPrsPtr> aSelected = mySelectedHistoryValues[myCurrentHistoryIndex];
+      // equal vertices should not be used here
+      ModuleBase_ISelection::filterSelectionOnEqualPoints(aSelected);
+      bool isDone = setSelection(aSelected,
+                                 false /*need not validate because values already was in list*/);
+      updateOnSelectionChanged(isDone);
+
+      myWorkshop->updateCommandStatus();
+#ifdef DEBUG_UNDO_REDO
+      printHistoryInfo(QString("processAction %1").arg(theActionType == ActionUndo ? "Undo" : "Redo"),
+        myCurrentHistoryIndex, mySelectedHistoryValues);
+#endif
+      return true;
+    }
+    default:
+      return ModuleBase_ModelWidget::processAction(theActionType);
+  }
+}
+
+//********************************************************************
+bool ModuleBase_WidgetMultiSelector::activateSelectionAndFilters(bool toActivate)
+{
+  myWorkshop->updateCommandStatus(); // update enable state of Undo/Redo application actions
+  return ModuleBase_WidgetSelector::activateSelectionAndFilters(toActivate);
+}
+
 //********************************************************************
 bool ModuleBase_WidgetMultiSelector::isValidSelectionCustom(const ModuleBase_ViewerPrsPtr& thePrs)
 {
@@ -325,6 +419,8 @@ bool ModuleBase_WidgetMultiSelector::isValidSelectionCustom(const ModuleBase_Vie
 //********************************************************************
 bool ModuleBase_WidgetMultiSelector::processDelete()
 {
+  appendFirstSelectionInHistory();
+
   // find attribute indices to delete
   std::set<int> anAttributeIds;
   getSelectedAttributeIndices(anAttributeIds);
@@ -377,6 +473,7 @@ bool ModuleBase_WidgetMultiSelector::processDelete()
       }
     }
   }
+  appendSelectionInHistory();
   return aDone;
 }
 
@@ -428,6 +525,8 @@ void ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()
   // may be the feature's result is not displayed, but attributes should be
   myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeArguments,
                             true); /// hope that something is redisplayed by object updated
+  // clear history should follow after set selected to do not increase history by setSelected
+  clearSelectedHistory();
 }
 
 //********************************************************************
@@ -447,14 +546,53 @@ void ModuleBase_WidgetMultiSelector::onSelectionChanged()
       }
     }
   }
+  appendFirstSelectionInHistory();
   ModuleBase_WidgetSelector::onSelectionChanged();
+  appendSelectionInHistory();
+}
+
+void ModuleBase_WidgetMultiSelector::appendFirstSelectionInHistory()
+{
+  if (mySelectedHistoryValues.empty()) {
+    myCurrentHistoryIndex++;
+    mySelectedHistoryValues.append(getAttributeSelection());
+
+#ifdef DEBUG_UNDO_REDO
+    printHistoryInfo("appendSelectionInHistory", myCurrentHistoryIndex, mySelectedHistoryValues);
+#endif
+  }
+}
+
+void ModuleBase_WidgetMultiSelector::appendSelectionInHistory()
+{
+  while (myCurrentHistoryIndex != mySelectedHistoryValues.count() - 1)
+    mySelectedHistoryValues.removeLast();
+
+  QList<ModuleBase_ViewerPrsPtr> aSelected = getFilteredSelected();
+  myCurrentHistoryIndex++;
+  mySelectedHistoryValues.append(aSelected);
+  myWorkshop->updateCommandStatus();
+
+#ifdef DEBUG_UNDO_REDO
+  printHistoryInfo("appendSelectionInHistory", myCurrentHistoryIndex, mySelectedHistoryValues);
+#endif
+}
+
+void ModuleBase_WidgetMultiSelector::clearSelectedHistory()
+{
+  mySelectedHistoryValues.clear();
+  myCurrentHistoryIndex = -1;
+  myWorkshop->updateCommandStatus();
+
+#ifdef DEBUG_UNDO_REDO
+  printHistoryInfo("clearSelectedHistory", myCurrentHistoryIndex, mySelectedHistoryValues);
+#endif
 }
 
 void ModuleBase_WidgetMultiSelector::updateFocus()
 {
   // Set focus to List control in order to make possible
   // to use Tab key for transfer the focus to next widgets
-  myListControl->setCurrentRow(myListControl->model()->rowCount() - 1);
   ModuleBase_Tools::setFocus(myListControl,
                              "ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()");
 }
@@ -464,6 +602,29 @@ void ModuleBase_WidgetMultiSelector::updateSelectionName()
 {
 }
 
+//********************************************************************
+void ModuleBase_WidgetMultiSelector::updateOnSelectionChanged(const bool theDone)
+{
+  if (myIsSetSelectionBlocked)
+    return;
+  ModuleBase_WidgetSelector::updateOnSelectionChanged(theDone);
+
+  // according to #2154 we need to update OB selection when selection in the viewer happens
+  // it is important to flush sinchronize selection signal after flush of Update/Create/Delete.
+  // because we need that Object Browser has been already updated when synchronize happens.
+
+  // Restore selection in the viewer by the attribute selection list
+  // it is possible that diring selection attribute filling, selection in Object Browser
+  // is changed(some items were removed/added) and as result, selection in the viewer
+  // differs from the selection come to this method. By next rows, we restore selection
+  // in the viewer according to content of selection attribute. Case is Edge selection in Group
+  myIsSetSelectionBlocked = true;
+  static Events_ID anEvent = Events_Loop::eventByName(EVENT_UPDATE_BY_WIDGET_SELECTION);
+  ModelAPI_EventCreator::get()->sendUpdated(myFeature, anEvent);
+  Events_Loop::loop()->flush(anEvent);
+  myIsSetSelectionBlocked = false;
+}
+
 //********************************************************************
 QIntList ModuleBase_WidgetMultiSelector::shapeTypes() const
 {
@@ -707,7 +868,8 @@ bool ModuleBase_WidgetMultiSelector::removeUnusedAttributeObjects
     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
     for (int i = 0; i < aSelectionListAttr->size(); i++) {
       AttributeSelectionPtr anAttr = aSelectionListAttr->value(i);
-      bool aFound = findInSelection(anAttr->context(), anAttr->value(), aGeomSelection);
+      bool aFound = findInSelection(anAttr->context(), anAttr->value(), aGeomSelection,
+                                    myWorkshop);
       if (!aFound)
         anIndicesToBeRemoved.insert(i);
     }
@@ -719,7 +881,8 @@ bool ModuleBase_WidgetMultiSelector::removeUnusedAttributeObjects
     for (int i = 0; i < aRefListAttr->size(); i++) {
       ObjectPtr anObject = aRefListAttr->object(i);
       if (anObject.get()) {
-        bool aFound = findInSelection(anObject, GeomShapePtr(), aGeomSelection);
+        bool aFound = findInSelection(anObject, GeomShapePtr(), aGeomSelection,
+                                      myWorkshop);
         if (!aFound)
           anIndicesToBeRemoved.insert(i);
       }
@@ -749,7 +912,8 @@ bool ModuleBase_WidgetMultiSelector::removeUnusedAttributeObjects
         aFound = anAttributes.find(anAttribute) != anAttributes.end();
       }
       else {
-        aFound = findInSelection(aRefAttrListAttr->object(i), GeomShapePtr(), aGeomSelection);
+        aFound = findInSelection(aRefAttrListAttr->object(i), GeomShapePtr(), aGeomSelection,
+                                 myWorkshop);
       }
       if (!aFound)
         anIndicesToBeRemoved.insert(i);
@@ -788,11 +952,22 @@ std::map<ObjectPtr, std::set<GeomShapePtr> > ModuleBase_WidgetMultiSelector::con
 }
 
 bool ModuleBase_WidgetMultiSelector::findInSelection(const ObjectPtr& theObject,
-                              const GeomShapePtr& theShape,
-                              const std::map<ObjectPtr, std::set<GeomShapePtr> >& theGeomSelection)
+                              GeomShapePtr theShape,
+                              const std::map<ObjectPtr, std::set<GeomShapePtr> >& theGeomSelection,
+                              ModuleBase_IWorkshop* theWorkshop)
 {
+  // issue #2154: we should not remove from list objects hidden in the viewer if selection
+  // was done with SHIFT button
+  if (theWorkshop->hasSHIFTPressed() && !theObject->isDisplayed())
+    return true;
+
   bool aFound = false;
   GeomShapePtr anEmptyShape(new GeomAPI_Shape());
+  if (theShape.get()) { // treat shape equal to context as null: 2219, keep order of shapes in list
+    const ResultPtr aContext = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
+    if (aContext.get() && aContext->shape()->isEqual(theShape))
+      theShape.reset();
+  }
   GeomShapePtr aShape = theShape.get() ? theShape : anEmptyShape;
   if (theGeomSelection.find(theObject) != theGeomSelection.end()) {// found
     const std::set<GeomShapePtr>& aShapes = theGeomSelection.at(theObject);
@@ -800,7 +975,7 @@ bool ModuleBase_WidgetMultiSelector::findInSelection(const ObjectPtr& theObject,
     for (; anIt != aLast && !aFound; anIt++) {
       GeomShapePtr aCShape = *anIt;
       if (aCShape.get())
-        aFound = aCShape->isEqual(aShape);
+        aFound = aCShape->isSame(aShape);
     }
   }
   return aFound;