Salome HOME
A new event Visual Attributes Changed is defined for performance sake.
[modules/shaper.git] / src / ModuleBase / ModuleBase_WidgetMultiSelector.cpp
old mode 100755 (executable)
new mode 100644 (file)
index 80fb5cd..bb68bed
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2017  CEA/DEN, EDF R&D
+// 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
 //
 // 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
+// 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>
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 //
 
 #include <ModuleBase_WidgetMultiSelector.h>
-#include <ModuleBase_WidgetShapeSelector.h>
+
+#include <GeomAPI_AISObject.h>
+
+#include <ModuleBase_ActionIntParameter.h>
+#include <ModuleBase_Definitions.h>
+#include <ModuleBase_Events.h>
+#include <ModuleBase_IconFactory.h>
+#include <ModuleBase_IModule.h>
 #include <ModuleBase_ISelection.h>
-#include <ModuleBase_IWorkshop.h>
+#include <ModuleBase_ISelectionActivate.h>
+#include <ModuleBase_IPropertyPanel.h>
 #include <ModuleBase_IViewer.h>
+#include <ModuleBase_IWorkshop.h>
+#include <ModuleBase_ListView.h>
+#include <ModuleBase_ResultPrs.h>
 #include <ModuleBase_Tools.h>
-#include <ModuleBase_Definitions.h>
-#include <ModuleBase_IModule.h>
 #include <ModuleBase_ViewerPrs.h>
-#include <ModuleBase_IconFactory.h>
-#include <ModuleBase_Events.h>
+#include <ModuleBase_WidgetShapeSelector.h>
+#include <ModuleBase_ChoiceCtrl.h>
+#include <ModuleBase_WidgetSelectionFilter.h>
 
 #include <ModelAPI_Data.h>
 #include <ModelAPI_Object.h>
@@ -40,6 +49,8 @@
 
 #include <Config_WidgetAPI.h>
 
+#include <AIS_InteractiveObject.hxx>
+
 #include <QGridLayout>
 #include <QLabel>
 #include <QListWidget>
 #include <QString>
 #include <QComboBox>
 #include <QEvent>
-#include <QAction>
 #include <QApplication>
 #include <QClipboard>
 #include <QTimer>
 #include <QMainWindow>
+#include <QCheckBox>
+#include <QPushButton>
 
 #include <memory>
 #include <string>
 
-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
-*/
-class CustomListWidget : public QListWidget
+#ifdef DEBUG_UNDO_REDO
+void printHistoryInfo(const QString& theMethodName, int theCurrentHistoryIndex,
+  QList<QList<std::shared_ptr<ModuleBase_ViewerPrs> > > theSelectedHistoryValues)
 {
-public:
-  /// Constructor
-  /// \param theParent a parent widget
-  CustomListWidget( QWidget* theParent )
-    : QListWidget( theParent )
-  {
-  }
+  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
 
-  /// Redefinition of virtual method
-  virtual QSize        sizeHint() const
-  {
-    int aHeight = 2*QFontMetrics( font() ).height();
-    QSize aSize = QListWidget::sizeHint();
-    return QSize( aSize.width(), aHeight );
-  }
 
-  /// Redefinition of virtual method
-  virtual QSize        minimumSizeHint() const
-  {
-    int aHeight = 4/*2*/*QFontMetrics( font() ).height();
-    QSize aSize = QListWidget::minimumSizeHint();
-    return QSize( aSize.width(), aHeight );
+QStringList getIconsList(const QStringList& theNames)
+{
+  QStringList aIcons;
+  foreach (QString aName, theNames) {
+    QString aUName = aName.toUpper();
+    if ((aUName == "VERTICES") || (aUName == "VERTEX"))
+      aIcons << ":pictures/vertex32.png";
+    else if ((aUName == "EDGES") || (aUName == "EDGE"))
+      aIcons << ":pictures/edge32.png";
+    else if ((aUName == "FACES") || (aUName == "FACE"))
+      aIcons << ":pictures/face32.png";
+    else if ((aUName == "SOLIDS") || (aUName == "SOLID"))
+      aIcons << ":pictures/solid32.png";
   }
+  return aIcons;
+}
+
+/// Stores default values of selected option (selection mode)
+/// It is used only in case if myTypeCtrl is used
+static QMap<std::string, std::string> defaultValues;
 
-#ifndef WIN32
-// The code is necessary only for Linux because
-//it can not update viewport on widget resize
-protected:
-  void resizeEvent(QResizeEvent* theEvent)
-  {
-    QListWidget::resizeEvent(theEvent);
-    QTimer::singleShot(5, viewport(), SLOT(repaint()));
-  }
-#endif
-};
 
 ModuleBase_WidgetMultiSelector::ModuleBase_WidgetMultiSelector(QWidget* theParent,
                                                                ModuleBase_IWorkshop* theWorkshop,
                                                                const Config_WidgetAPI* theData)
 : ModuleBase_WidgetSelector(theParent, theWorkshop, theData),
-  myIsSetSelectionBlocked(false)
+  myIsSetSelectionBlocked(false), myCurrentHistoryIndex(-1),
+  myIsFirst(true), myFiltersWgt(0)
 {
-  QGridLayout* aMainLay = new QGridLayout(this);
-  ModuleBase_Tools::adjustMargins(aMainLay);
+  std::string aPropertyTypes = theData->getProperty("shape_types");
+  QString aTypesStr = aPropertyTypes.c_str();
+  myShapeTypes = aTypesStr.split(' ', QString::SkipEmptyParts);
+  myIsUseChoice = theData->getBooleanAttribute("use_choice", false);
 
-  QLabel* aTypeLabel = new QLabel(tr("Type"), this);
-  aMainLay->addWidget(aTypeLabel, 0, 0);
+  QString aAllowedList(theData->getProperty("allow_objects").c_str());
+  if (!aAllowedList.isEmpty())
+    myAllowedObjects = aAllowedList.split(' ', QString::SkipEmptyParts);
 
-  myTypeCombo = new QComboBox(this);
-  // There is no sense to parameterize list of types while we can not parameterize selection mode
-
-  std::string aPropertyTypes = theData->getProperty("type_choice");
-  QString aTypesStr = aPropertyTypes.c_str();
-  QStringList aShapeTypes = aTypesStr.split(' ', QString::SkipEmptyParts);
+  QVBoxLayout* aMainLay = new QVBoxLayout(this);
+  ModuleBase_Tools::adjustMargins(aMainLay);
 
-  myIsUseChoice = theData->getBooleanAttribute("use_choice", false);
+  QStringList aIconsList = getIconsList(myShapeTypes);
+  myTypeCtrl = new ModuleBase_ChoiceCtrl(this, myShapeTypes, aIconsList);
+  myTypeCtrl->setLabel(tr("Type"));
+  if (!myShapeTypes.empty()) {
+    myTypeCtrl->setValue(0);
+    myDefMode = myShapeTypes.first().toStdString();
+  }
+  aMainLay->addWidget(myTypeCtrl);
 
-  if (!aShapeTypes.empty())
-    myTypeCombo->addItems(aShapeTypes);
-  aMainLay->addWidget(myTypeCombo, 0, 1);
+  // There is no sense to parameterize list of types while we can not parameterize selection mode
   // if the xml definition contains one type, the controls to select a type should not be shown
-  if (aShapeTypes.size() <= 1 || !myIsUseChoice) {
-    aTypeLabel->setVisible(false);
-    myTypeCombo->setVisible(false);
+  if (myShapeTypes.size() <= 1 || !myIsUseChoice) {
+    myTypeCtrl->setVisible(false);
   }
 
   QString aLabelText = translate(theData->getProperty("label"));
-  QLabel* aListLabel = new QLabel(aLabelText, this);
-  aMainLay->addWidget(aListLabel, 1, 0);
-  // if the xml definition contains one type, an information label
-  // should be shown near to the latest
-  if (aShapeTypes.size() <= 1) {
-    QString aLabelIcon = QString::fromStdString(theData->widgetIcon());
-    if (!aLabelIcon.isEmpty()) {
-      QLabel* aSelectedLabel = new QLabel("", this);
-      aSelectedLabel->setPixmap(ModuleBase_IconFactory::loadPixmap(aLabelIcon));
-      aMainLay->addWidget(aSelectedLabel, 1, 1);
+  if (aLabelText.size() > 0) {
+    QWidget* aLabelWgt = new QWidget(this);
+    QHBoxLayout* aLabelLayout = new QHBoxLayout(aLabelWgt);
+    aLabelLayout->setContentsMargins(0, 0, 0, 0);
+    aMainLay->addWidget(aLabelWgt);
+
+    QLabel* aListLabel = new QLabel(aLabelText, this);
+    aLabelLayout->addWidget(aListLabel);
+    // if the xml definition contains one type, an information label
+    // should be shown near to the latest
+    if (myShapeTypes.size() <= 1) {
+      QString aLabelIcon = QString::fromStdString(theData->widgetIcon());
+      if (!aLabelIcon.isEmpty()) {
+        QLabel* aSelectedLabel = new QLabel("", this);
+        aSelectedLabel->setPixmap(ModuleBase_IconFactory::loadPixmap(aLabelIcon));
+        aLabelLayout->addWidget(aSelectedLabel);
+        aLabelLayout->addStretch(1);
+      }
     }
-    aMainLay->setColumnStretch(2, 1);
   }
 
-  QString aToolTip = QString::fromStdString(theData->widgetTooltip());
-  myListControl = new CustomListWidget(this);
+  QString aToolTip = translate(theData->widgetTooltip());
   QString anObjName = QString::fromStdString(attributeID());
-  myListControl->setObjectName(anObjName);
-  myListControl->setToolTip(aToolTip);
-  myListControl->setSelectionMode(QAbstractItemView::ExtendedSelection);
-
-  aMainLay->addWidget(myListControl, 2, 0, 1, -1);
-  aMainLay->setRowStretch(2, 1);
-  //aMainLay->addWidget(new QLabel(this)); //FIXME(sbh)???
-  //aMainLay->setRowMinimumHeight(3, 20);
-  //this->setLayout(aMainLay);
-  connect(myTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onSelectionTypeChanged()));
-
-  myCopyAction = ModuleBase_Tools::createAction(QIcon(":pictures/copy.png"), tr("Copy"),
-                          myWorkshop->desktop(), this, SLOT(onCopyItem()));
-  myCopyAction->setShortcut(QKeySequence::Copy);
-  myCopyAction->setEnabled(false);
-  myListControl->addAction(myCopyAction);
-
-  myDeleteAction = ModuleBase_Tools::createAction(QIcon(":pictures/delete.png"), tr("Delete"),
-                          myWorkshop->desktop(), this, SLOT(onDeleteItem()));
-  myDeleteAction->setEnabled(false);
-  myListControl->addAction(myDeleteAction);
-
-  myListControl->setContextMenuPolicy(Qt::ActionsContextMenu);
-  connect(myListControl, SIGNAL(itemSelectionChanged()), SLOT(onListSelection()));
+  myListView = new ModuleBase_ListView(this, anObjName, aToolTip);
+  connect(myListView->getControl(), SIGNAL(itemSelectionChanged()), SLOT(onListSelection()));
+  connect(myListView, SIGNAL(deleteActionClicked()), SLOT(onDeleteItem()));
+  connect(myListView, SIGNAL(listActivated()), SLOT(onListActivated()));
+
+  aMainLay->addWidget(myListView->getControl());
+  connect(myTypeCtrl, SIGNAL(valueChanged(int)), this, SLOT(onSelectionTypeChanged()));
+
+  std::string aUseFilters = theData->getProperty("use_filters");
+  if (aUseFilters.length() > 0) {
+    QWidget* aFltrWgt = new QWidget(this);
+    QHBoxLayout* aFltrLayout = new QHBoxLayout(aFltrWgt);
+
+    myFiltersWgt = new ModuleBase_FilterStarter(aUseFilters.c_str(), aFltrWgt, theWorkshop);
+    aFltrLayout->addWidget(myFiltersWgt);
+
+    aFltrLayout->addStretch();
+
+    QPushButton* aShowBtn = new QPushButton(tr("Show only"), aFltrWgt);
+    aShowBtn->setCheckable(true);
+    aShowBtn->setChecked(false);
+    connect(aShowBtn, SIGNAL(toggled(bool)), SLOT(onShowOnly(bool)));
+    aFltrLayout->addWidget(aShowBtn);
+
+    aMainLay->addWidget(aFltrWgt);
+  }
+
+  bool aSameTop = theData->getBooleanAttribute("same_topology", false);
+  if (aSameTop) {
+    myGeomCheck = new QCheckBox(tr("Add elements that share the same topology"), this);
+    aMainLay->addWidget(myGeomCheck);
+    connect(myGeomCheck, SIGNAL(toggled(bool)), SLOT(onSameTopology(bool)));
+  }
+  else
+    myGeomCheck = 0;
 
   myIsNeutralPointClear = theData->getBooleanAttribute("clear_in_neutral_point", true);
+  if (myShapeTypes.size() > 1 || myIsUseChoice) {
+    if (defaultValues.contains(myFeatureId + attributeID())) {
+      myDefMode = defaultValues[myFeatureId + attributeID()];
+      myTypeCtrl->setValue(myDefMode.c_str());
+    }
+  }
 }
 
 ModuleBase_WidgetMultiSelector::~ModuleBase_WidgetMultiSelector()
@@ -184,16 +221,67 @@ void ModuleBase_WidgetMultiSelector::activateCustom()
 {
   ModuleBase_WidgetSelector::activateCustom();
 
-  myWorkshop->module()->activateCustomPrs(myFeature,
+  ModuleBase_IModule* aModule = myWorkshop->module();
+  aModule->activateCustomPrs(myFeature,
                             ModuleBase_IModule::CustomizeHighlightedObjects, true);
+  clearSelectedHistory();
+  if (myAllowedObjects.length() > 0) {
+    Handle(SelectMgr_Filter) aFilter = aModule->selectionFilter(SF_GlobalFilter);
+    if (!aFilter.IsNull()) {
+      Handle(ModuleBase_ShapeDocumentFilter) aDocFilter =
+        Handle(ModuleBase_ShapeDocumentFilter)::DownCast(aFilter);
+      if (!aDocFilter.IsNull()) {
+        QStringList aSelFilters = aDocFilter->nonSelectableTypes();
+        foreach(QString aType, aSelFilters) {
+          if (aSelFilters.contains(aType)) {
+            aDocFilter->removeNonSelectableType(aType);
+            myTmpAllowed.append(aType);
+          }
+        }
+      }
+    }
+  }
 }
 
 //********************************************************************
 void ModuleBase_WidgetMultiSelector::deactivate()
 {
+  myWorkshop->module()->enableCustomModes();
+
   ModuleBase_WidgetSelector::deactivate();
+  if (myVisibleObjects.size())
+    onShowOnly(false);
 
   myWorkshop->module()->deactivateCustomPrs(ModuleBase_IModule::CustomizeHighlightedObjects, true);
+  clearSelectedHistory();
+  if (myTmpAllowed.length() > 0) {
+    ModuleBase_IModule* aModule = myWorkshop->module();
+    Handle(SelectMgr_Filter) aFilter = aModule->selectionFilter(SF_GlobalFilter);
+    if (!aFilter.IsNull()) {
+      Handle(ModuleBase_ShapeDocumentFilter) aDocFilter =
+        Handle(ModuleBase_ShapeDocumentFilter)::DownCast(aFilter);
+      if (!aDocFilter.IsNull()) {
+        foreach(QString aType, myTmpAllowed) {
+          aDocFilter->addNonSelectableType(aType);
+        }
+      }
+    }
+    myTmpAllowed.clear();
+  }
+}
+
+//********************************************************************
+void ModuleBase_WidgetMultiSelector::updateAfterDeactivation()
+{
+  // restore previous Undo/Redo workshop state
+  myWorkshop->updateCommandStatus();
+}
+
+//********************************************************************
+void ModuleBase_WidgetMultiSelector::updateAfterActivation()
+{
+  // fill Undo/Redo actions with current information
+  myWorkshop->updateCommandStatus();
 }
 
 //********************************************************************
@@ -208,7 +296,23 @@ bool ModuleBase_WidgetMultiSelector::storeValueCustom()
   std::string aType = anAttribute->attributeType();
   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
     AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
-    aSelectionListAttr->setSelectionType(myTypeCombo->currentText().toStdString());
+    if (myTypeCtrl->isVisible()) {
+      std::string aMode = myTypeCtrl->textValue().toStdString();
+      if (myIsFirst && (!myDefMode.empty()))
+        aMode = myDefMode;
+
+      aSelectionListAttr->setSelectionType(aMode);
+      myIsFirst = false;
+    } else { // no type, set the type as a first element of the list shape type when it is appeared
+      if (aSelectionListAttr->size()) {
+        AttributeSelectionPtr aSel = aSelectionListAttr->value(0);
+        GeomShapePtr aFirstVal = aSel->value();
+        if (!aFirstVal.get() && aSel->context().get())
+          aFirstVal = aSel->context()->shape();
+        if (aFirstVal.get() && !aFirstVal->isNull())
+          aSelectionListAttr->setSelectionType(aFirstVal->shapeTypeStr());
+      }
+    }
   }
   return true;
 }
@@ -221,14 +325,19 @@ bool ModuleBase_WidgetMultiSelector::restoreValueCustom()
     return false;
 
   AttributePtr anAttribute = myFeature->data()->attribute(attributeID());
+  AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
   std::string aType = anAttribute->attributeType();
   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
-    AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
     // Restore shape type
     std::string aSelectionType = aSelectionListAttr->selectionType().c_str();
-    if (!aSelectionType.empty())
+    if (!aSelectionType.empty()) {
       setCurrentShapeType(ModuleBase_Tools::shapeType(aSelectionType.c_str()));
+      myDefMode = aSelectionType;
+      myIsFirst = false;
+    }
   }
+  if (myGeomCheck)
+    myGeomCheck->setChecked(aSelectionListAttr->isGeometricalSelection());
   updateSelectionList();
   return true;
 }
@@ -278,6 +387,12 @@ bool ModuleBase_WidgetMultiSelector::setSelection(QList<ModuleBase_ViewerPrsPtr>
     // if there is at least one set, the result is true
     isDone = isDone || aProcessed;
   }
+  // Check the selection with validators
+  QString aError = getError();
+  if (aError.length() > 0) {
+    aSelectionListAttr->clear();
+    isDone = false;
+  }
   // updateObject - to update/redisplay feature
   // it is commented in order to perfom it outside the method
   //if (isDone) {
@@ -294,7 +409,7 @@ bool ModuleBase_WidgetMultiSelector::setSelection(QList<ModuleBase_ViewerPrsPtr>
     theValues.append(anInvalidValues);
 
   if (isDone) // may be the feature's result is not displayed, but attributes should be
-    myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeArguments,
+    myWorkshop->module()->customizeFeature(myFeature, ModuleBase_IModule::CustomizeArguments,
                              true);/// hope that something is redisplayed by object updated
 
   return isDone;
@@ -309,12 +424,72 @@ 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:
+      aCanProcess = ModuleBase_WidgetSelector::canProcessAction(theActionType, isActionEnabled);
+    break;
+  }
+  return aCanProcess;
+}
+
+//********************************************************************
+bool ModuleBase_WidgetMultiSelector::processAction(ModuleBase_ActionType theActionType,
+                                                   const ActionParamPtr& theParam)
+{
+  switch (theActionType) {
+    case ActionUndo:
+    case ActionRedo: {
+      ActionIntParamPtr aParam =
+        std::dynamic_pointer_cast<ModuleBase_ActionIntParameter>(theParam);
+      int aNb = aParam->value();
+      if (theActionType == ActionUndo)
+        myCurrentHistoryIndex -= aNb;
+      else
+        myCurrentHistoryIndex += aNb;
+      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, theParam);
+  }
+}
+
 //********************************************************************
 bool ModuleBase_WidgetMultiSelector::isValidSelectionCustom(const ModuleBase_ViewerPrsPtr& thePrs)
 {
   bool aValid = ModuleBase_WidgetSelector::isValidSelectionCustom(thePrs);
   if (aValid) {
     ResultPtr aResult = myWorkshop->selection()->getResult(thePrs);
+    if (!aResult.get()) { // In case if a feature was selected
+      FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(thePrs->object());
+      if (aFeature.get())
+        aResult = aFeature->firstResult();
+    }
     aValid = aResult.get() != NULL;
     if (aValid) {
       if (myFeature) {
@@ -340,11 +515,13 @@ bool ModuleBase_WidgetMultiSelector::isValidSelectionCustom(const ModuleBase_Vie
 //********************************************************************
 bool ModuleBase_WidgetMultiSelector::processDelete()
 {
+  appendFirstSelectionInHistory();
+
   // find attribute indices to delete
   std::set<int> anAttributeIds;
   getSelectedAttributeIndices(anAttributeIds);
 
-  QModelIndexList aIndexes = myListControl->selectionModel()->selectedIndexes();
+  QModelIndexList anIndices = myListView->getControl()->selectionModel()->selectedIndexes();
 
   // refill attribute by the items which indices are not in the list of ids
   bool aDone = false;
@@ -376,38 +553,36 @@ bool ModuleBase_WidgetMultiSelector::processDelete()
     myWorkshop->setSelected(getAttributeSelection());
 
     // may be the feature's result is not displayed, but attributes should be
-    myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeArguments,
+    myWorkshop->module()->customizeFeature(myFeature, ModuleBase_IModule::CustomizeArguments,
                               true); /// hope that something is redisplayed by object updated
   }
 
   // Restore selection
-  int aRows = myListControl->model()->rowCount();
-  if (aRows > 0) {
-    foreach(QModelIndex aIndex, aIndexes) {
-      if (aIndex.row() < aRows)
-        myListControl->selectionModel()->select(aIndex, QItemSelectionModel::Select);
-      else {
-        QModelIndex aIdx = myListControl->model()->index(aRows - 1, 0);
-        myListControl->selectionModel()->select(aIdx, QItemSelectionModel::Select);
-      }
-    }
-  }
-  return aDone;
+  myListView->restoreSelection(anIndices);
+
+  appendSelectionInHistory();
+  return true/*aDone*/; // following #2438 Delete should be processed even if nothing is delete
 }
 
 //********************************************************************
 QList<QWidget*> ModuleBase_WidgetMultiSelector::getControls() const
 {
   QList<QWidget*> result;
-  //result << myTypeCombo;
-  result << myListControl;
+  result << myListView->getControl();
   return result;
 }
 
 //********************************************************************
 void ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()
 {
-  activateSelectionAndFilters(true);
+  // Clear current selection in order to avoid updating of object browser with obsolete indexes
+  // which can appear because of results deletetion after changing a type of selection
+  QString aSelectionType = myTypeCtrl->textValue();
+  QList<ModuleBase_ViewerPrsPtr> aEmptyList;
+  myWorkshop->setSelected(aEmptyList);
+
+  updateSelectionModesAndFilters(true);
+  myWorkshop->selectionActivate()->updateSelectionModes();
 
   if (!myFeature)
     return;
@@ -416,7 +591,7 @@ void ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()
   std::string aType = anAttribute->attributeType();
   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
     AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
-    aSelectionListAttr->setSelectionType(myTypeCombo->currentText().toStdString());
+    aSelectionListAttr->setSelectionType(aSelectionType.toStdString());
   }
 
   // clear attribute values
@@ -441,12 +616,17 @@ void ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()
   restoreValue();
   myWorkshop->setSelected(getAttributeSelection());
   // may be the feature's result is not displayed, but attributes should be
-  myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeArguments,
+  myWorkshop->module()->customizeFeature(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();
+
+  if (myWorkshop->propertyPanel()->activeWidget() != this)
+    myWorkshop->propertyPanel()->activateWidget(this);
 }
 
 //********************************************************************
-void ModuleBase_WidgetMultiSelector::onSelectionChanged()
+bool ModuleBase_WidgetMultiSelector::processSelection()
 {
   if (!myIsNeutralPointClear) {
     QList<ModuleBase_ViewerPrsPtr> aSelected = getFilteredSelected();
@@ -458,19 +638,59 @@ void ModuleBase_WidgetMultiSelector::onSelectionChanged()
         static Events_ID anEvent = Events_Loop::eventByName(EVENT_UPDATE_BY_WIDGET_SELECTION);
         ModelAPI_EventCreator::get()->sendUpdated(myFeature, anEvent);
         Events_Loop::loop()->flush(anEvent);
-        return;
+        return true;
       }
     }
   }
-  ModuleBase_WidgetSelector::onSelectionChanged();
+  appendFirstSelectionInHistory();
+  bool aDone = ModuleBase_WidgetSelector::processSelection();
+  appendSelectionInHistory();
+  return aDone;
+}
+
+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_Tools::setFocus(myListView->getControl(),
                              "ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()");
 }
 
@@ -507,12 +727,13 @@ QIntList ModuleBase_WidgetMultiSelector::shapeTypes() const
 {
   QIntList aShapeTypes;
 
-  if (myTypeCombo->count() > 1 && myIsUseChoice) {
-    aShapeTypes.append(ModuleBase_Tools::shapeType(myTypeCombo->currentText()));
+  if (myShapeTypes.length() > 1 && myIsUseChoice) {
+    aShapeTypes.append(ModuleBase_Tools::shapeType(myTypeCtrl->textValue()));
   }
   else {
-    for (int i = 0, aCount = myTypeCombo->count(); i < aCount; i++)
-      aShapeTypes.append(ModuleBase_Tools::shapeType(myTypeCombo->itemText(i)));
+    foreach (QString aType, myShapeTypes) {
+      aShapeTypes.append(ModuleBase_Tools::shapeType(aType));
+    }
   }
   return aShapeTypes;
 }
@@ -522,18 +743,18 @@ void ModuleBase_WidgetMultiSelector::setCurrentShapeType(const int theShapeType)
 {
   QString aShapeTypeName;
 
-  for (int idx = 0; idx < myTypeCombo->count(); ++idx) {
-    aShapeTypeName = myTypeCombo->itemText(idx);
+  int idx = 0;
+  foreach (QString aShapeTypeName, myShapeTypes) {
     int aRefType = ModuleBase_Tools::shapeType(aShapeTypeName);
-    if(aRefType == theShapeType && idx != myTypeCombo->currentIndex()) {
-      bool aWasActivated = activateSelectionAndFilters(false);
-      bool isBlocked = myTypeCombo->blockSignals(true);
-      myTypeCombo->setCurrentIndex(idx);
-      myTypeCombo->blockSignals(isBlocked);
-      if (aWasActivated)
-        activateSelectionAndFilters(true);
+    if(aRefType == theShapeType && idx != myTypeCtrl->value()) {
+      updateSelectionModesAndFilters(false);
+      bool isBlocked = myTypeCtrl->blockSignals(true);
+      myTypeCtrl->setValue(idx);
+      myTypeCtrl->blockSignals(isBlocked);
+      updateSelectionModesAndFilters(true);
       break;
     }
+    idx++;
   }
 }
 
@@ -547,7 +768,7 @@ QList<ModuleBase_ViewerPrsPtr> ModuleBase_WidgetMultiSelector::getAttributeSelec
 //********************************************************************
 void ModuleBase_WidgetMultiSelector::updateSelectionList()
 {
-  myListControl->clear();
+  myListView->getControl()->clear();
 
   DataPtr aData = myFeature->data();
   AttributePtr anAttribute = aData->attribute(attributeID());
@@ -556,9 +777,7 @@ void ModuleBase_WidgetMultiSelector::updateSelectionList()
     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
     for (int i = 0; i < aSelectionListAttr->size(); i++) {
       AttributeSelectionPtr aAttr = aSelectionListAttr->value(i);
-      QListWidgetItem* anItem = new QListWidgetItem(aAttr->namingName().c_str(), myListControl);
-      anItem->setData(ATTRIBUTE_SELECTION_INDEX_ROLE, i);
-      myListControl->addItem(anItem);
+      myListView->addItem(aAttr->namingName().c_str(), i);
     }
   }
   else if (aType == ModelAPI_AttributeRefList::typeId()) {
@@ -566,10 +785,7 @@ void ModuleBase_WidgetMultiSelector::updateSelectionList()
     for (int i = 0; i < aRefListAttr->size(); i++) {
       ObjectPtr anObject = aRefListAttr->object(i);
       if (anObject.get()) {
-        QListWidgetItem* anItem = new QListWidgetItem(anObject->data()->name().c_str(),
-                                                      myListControl);
-        anItem->setData(ATTRIBUTE_SELECTION_INDEX_ROLE, i);
-        myListControl->addItem(anItem);
+        myListView->addItem(anObject->data()->name().c_str(), i);
       }
     }
   }
@@ -579,7 +795,7 @@ void ModuleBase_WidgetMultiSelector::updateSelectionList()
       AttributePtr anAttribute = aRefAttrListAttr->attribute(i);
       QString aName;
       if (anAttribute.get()) {
-        std::string anAttrName = generateName(anAttribute, myWorkshop);
+        std::string anAttrName = ModuleBase_Tools::generateName(anAttribute, myWorkshop);
         aName = QString::fromStdString(anAttrName);
       }
       else {
@@ -588,14 +804,12 @@ void ModuleBase_WidgetMultiSelector::updateSelectionList()
           aName = anObject->data()->name().c_str();
         }
       }
-      QListWidgetItem* anItem = new QListWidgetItem(aName, myListControl);
-      anItem->setData(ATTRIBUTE_SELECTION_INDEX_ROLE, i);
-      myListControl->addItem(anItem);
+      myListView->addItem(aName, i);
     }
   }
 
   // We have to call repaint because sometimes the List control is not updated
-  myListControl->repaint();
+  myListView->getControl()->update();
 }
 
 //********************************************************************
@@ -623,29 +837,13 @@ void ModuleBase_WidgetMultiSelector::clearSelection()
 
   QList<ModuleBase_ViewerPrsPtr> anEmptyList;
   // This method will call Selection changed event which will call onSelectionChanged
-  // To clear mySelection, myListControl and storeValue()
+  // To clear mySelection, myListView and storeValue()
   // So, we don't need to call it
   myWorkshop->setSelected(anEmptyList);
 
   myIsNeutralPointClear = isClearInNeutralPoint;
 }
 
-//********************************************************************
-void ModuleBase_WidgetMultiSelector::onCopyItem()
-{
-  QList<QListWidgetItem*> aItems = myListControl->selectedItems();
-  QString aRes;
-  foreach(QListWidgetItem* aItem, aItems) {
-    if (!aRes.isEmpty())
-      aRes += "\n";
-    aRes += aItem->text();
-  }
-  if (!aRes.isEmpty()) {
-    QClipboard *clipboard = QApplication::clipboard();
-    clipboard->setText(aRes);
-  }
-}
-
 //********************************************************************
 void ModuleBase_WidgetMultiSelector::onDeleteItem()
 {
@@ -655,23 +853,14 @@ void ModuleBase_WidgetMultiSelector::onDeleteItem()
 //********************************************************************
 void ModuleBase_WidgetMultiSelector::onListSelection()
 {
-  QList<QListWidgetItem*> aItems = myListControl->selectedItems();
-  myCopyAction->setEnabled(!aItems.isEmpty());
-  myDeleteAction->setEnabled(!aItems.isEmpty());
-
-  myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeHighlightedObjects,
-                                        true);
+  myWorkshop->module()->customizeFeature(myFeature, ModuleBase_IModule::CustomizeHighlightedObjects,
+                                         true);
 }
 
 //********************************************************************
 void ModuleBase_WidgetMultiSelector::getSelectedAttributeIndices(std::set<int>& theAttributeIds)
 {
-  QList<QListWidgetItem*> aItems = myListControl->selectedItems();
-  foreach(QListWidgetItem* anItem, aItems) {
-    int anIndex = anItem->data(ATTRIBUTE_SELECTION_INDEX_ROLE).toInt();
-    if (theAttributeIds.find(anIndex) == theAttributeIds.end())
-      theAttributeIds.insert(anIndex);
-  }
+  myListView->getSelectedIndices(theAttributeIds);
 }
 
 void ModuleBase_WidgetMultiSelector::convertIndicesToViewerSelection(std::set<int> theAttributeIds,
@@ -690,7 +879,7 @@ void ModuleBase_WidgetMultiSelector::convertIndicesToViewerSelection(std::set<in
       if (!theAttributeIds.empty() && theAttributeIds.find(i) == theAttributeIds.end())
         continue;
       AttributeSelectionPtr anAttr = aSelectionListAttr->value(i);
-      ResultPtr anObject = anAttr->context();
+      ObjectPtr anObject = anAttr->contextObject();
       if (anObject.get())
         theValues.append(std::shared_ptr<ModuleBase_ViewerPrs>(
                new ModuleBase_ViewerPrs(anObject, anAttr->value(), NULL)));
@@ -740,24 +929,31 @@ bool ModuleBase_WidgetMultiSelector::removeUnusedAttributeObjects
   std::string aType = anAttribute->attributeType();
   std::set<GeomShapePtr> aShapes;
   std::set<int> anIndicesToBeRemoved;
+  FeaturePtr aFeature;
   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
     // iteration through data model to find not selected elements to remove them
     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);
-      if (!aFound)
-        anIndicesToBeRemoved.insert(i);
+      //aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->contextObject());
+      //if (!aFeature.get()) { // Feature can not be found as geometry selection
+        bool aFound = findInSelection(
+          anAttr->contextObject(), anAttr->value(), aGeomSelection, myWorkshop);
+        if (!aFound)
+          anIndicesToBeRemoved.insert(i);
+//      }
     }
     isDone = anIndicesToBeRemoved.size() > 0;
-    aSelectionListAttr->remove(anIndicesToBeRemoved);
+    if (isDone)
+      aSelectionListAttr->remove(anIndicesToBeRemoved);
   }
   else if (aType == ModelAPI_AttributeRefList::typeId()) {
     AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
     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);
       }
@@ -787,7 +983,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);
@@ -826,20 +1023,158 @@ 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)
 {
+  if (!theObject.get())
+    return false;
+  // 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());
-  GeomShapePtr aShape = theShape.get() ? theShape : anEmptyShape;
+  GeomShapePtr aShape = theShape;
+  if (!aShape.get()) {
+    // #2429 (the preselection of a sketch is not taken into account)
+    ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
+    if (aResult.get())
+      aShape = aResult->shape();
+  }
   if (theGeomSelection.find(theObject) != theGeomSelection.end()) {// found
     const std::set<GeomShapePtr>& aShapes = theGeomSelection.at(theObject);
     std::set<GeomShapePtr>::const_iterator anIt = aShapes.begin(), aLast = aShapes.end();
     for (; anIt != aLast && !aFound; anIt++) {
       GeomShapePtr aCShape = *anIt;
       if (aCShape.get())
+      {
+        // treat shape equal to context as null: 2219, keep order of shapes in list
+        if (aCShape->isNull()) { // in selection, shape of result is equal to selected shape
+          // if so, here we need to check shape of result
+          ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
+          if (aResult.get())
+            aCShape = aResult->shape();
+        }
         aFound = aCShape->isSame(aShape);
+      }
     }
   }
+
+  // issue #2903: (Possibility to hide faces) - check whether given shape is a hidden sub-shape
+  if (!aFound && theShape.get() && theWorkshop->hasSHIFTPressed() && theObject->isDisplayed()) {
+    AISObjectPtr anAIS = theWorkshop->findPresentation(theObject);
+    if (anAIS.get() != NULL) {
+      Handle(AIS_InteractiveObject) anAISIO = anAIS->impl<Handle(AIS_InteractiveObject)>();
+
+      Handle(ModuleBase_ResultPrs) aResultPrs = Handle(ModuleBase_ResultPrs)::DownCast(anAISIO);
+      if (!aResultPrs.IsNull() && aResultPrs->isSubShapeHidden(theShape->impl<TopoDS_Shape>()))
+        return true;
+    }
+  }
+
   return aFound;
 }
+
+QList<ActionInfo>
+  ModuleBase_WidgetMultiSelector::actionsList(ModuleBase_ActionType theActionType) const
+{
+  QList<ActionInfo> aList;
+  if (myCurrentHistoryIndex > -1) {
+    int i = 0;
+    QString aTitle("Selection %1 items");
+    QString aTit("Selection %1 item");
+    QIcon aIcon(":pictures/selection.png");
+    int aNb;
+    switch (theActionType) {
+    case ActionUndo:
+      i = 1;
+      while (i <= myCurrentHistoryIndex) {
+        aNb = mySelectedHistoryValues.at(i).count();
+        if (aNb == 1) {
+          ActionInfo aInfo(aIcon, aTit.arg(aNb));
+          aList.insert(0, aInfo);
+        } else {
+          ActionInfo aInfo(aIcon, aTitle.arg(aNb));
+          aList.insert(0, aInfo);
+        }
+        i++;
+      }
+      break;
+    case ActionRedo:
+      i = mySelectedHistoryValues.length() - 1;
+      while (i > myCurrentHistoryIndex) {
+        aNb = mySelectedHistoryValues.at(i).count();
+        if (aNb == 1) {
+          ActionInfo aInfo(aIcon, aTit.arg(mySelectedHistoryValues.at(i).count()));
+          aList.insert(0, aInfo);
+        } else {
+          ActionInfo aInfo(aIcon, aTitle.arg(mySelectedHistoryValues.at(i).count()));
+          aList.insert(0, aInfo);
+        }
+        i--;
+      }
+      break;
+    }
+  }
+  return aList;
+}
+
+
+void ModuleBase_WidgetMultiSelector::onFeatureAccepted()
+{
+  defaultValues[myFeatureId + attributeID()] = myDefMode;
+}
+
+void ModuleBase_WidgetMultiSelector::onListActivated()
+{
+  //focusTo();
+  emitFocusInWidget();
+}
+
+void ModuleBase_WidgetMultiSelector::onSameTopology(bool theOn)
+{
+  AttributePtr anAttribute = myFeature->data()->attribute(attributeID());
+  std::string aType = anAttribute->attributeType();
+  if (aType == ModelAPI_AttributeSelectionList::typeId()) {
+    AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
+    aSelectionListAttr->setGeometricalSelection(theOn);
+    updateObject(myFeature);
+  }
+}
+
+void ModuleBase_WidgetMultiSelector::onShowOnly(bool theChecked)
+{
+  std::list<ResultPtr> aResults = myFeature->results();
+   std::list<ResultPtr>::const_iterator aIt;
+  if (theChecked) {
+    myVisibleObjects = myWorkshop->displayedObjects();
+    for (aIt = aResults.cbegin(); aIt != aResults.cend(); aIt++) {
+      myVisibleObjects.removeAll(*aIt);
+    }
+    myWorkshop->module()->disableCustomMode(ModuleBase_IModule::CustomizeArguments);
+  }
+  else
+    myWorkshop->module()->enableCustomModes();
+
+  foreach(ObjectPtr aObj, myVisibleObjects) {
+    aObj->setDisplayed(!theChecked);
+  }
+
+  if (!theChecked) {
+    // Hide and show the group result in order to make it above all objects
+    bool aOldState = myWorkshop->enableUpdateViewer(false);
+    for (aIt = aResults.cbegin(); aIt != aResults.cend(); aIt++) {
+      (*aIt)->setDisplayed(false);
+    }
+    Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
+    for (aIt = aResults.cbegin(); aIt != aResults.cend(); aIt++) {
+      (*aIt)->setDisplayed(true);
+    }
+    Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
+    myWorkshop->enableUpdateViewer(aOldState);
+
+    myVisibleObjects.clear();
+  } else
+    Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
+}