-// 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>
#include <ModuleBase_IModule.h>
#include <ModuleBase_ViewerPrs.h>
#include <ModuleBase_IconFactory.h>
+#include <ModuleBase_Events.h>
#include <ModelAPI_Data.h>
#include <ModelAPI_Object.h>
#include <ModelAPI_AttributeRefList.h>
#include <ModelAPI_AttributeRefAttrList.h>
#include <ModelAPI_Tools.h>
+#include <ModelAPI_Events.h>
#include <Config_WidgetAPI.h>
#include <QApplication>
#include <QClipboard>
#include <QTimer>
+#include <QMainWindow>
#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
*/
#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);
myTypeCombo->setVisible(false);
}
- std::string aLabelText = theData->getProperty("label");
- QLabel* aListLabel = new QLabel(!aLabelText.empty() ? aLabelText.c_str()
- : tr("Selected objects:"), this);
+ 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 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()) {
//this->setLayout(aMainLay);
connect(myTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onSelectionTypeChanged()));
- myCopyAction = new QAction(QIcon(":pictures/copy.png"), tr("Copy"), this);
+ myCopyAction = ModuleBase_Tools::createAction(QIcon(":pictures/copy.png"), tr("Copy"),
+ myWorkshop->desktop(), this, SLOT(onCopyItem()));
myCopyAction->setShortcut(QKeySequence::Copy);
myCopyAction->setEnabled(false);
- connect(myCopyAction, SIGNAL(triggered(bool)), SLOT(onCopyItem()));
myListControl->addAction(myCopyAction);
- myDeleteAction = new QAction(QIcon(":pictures/delete.png"), tr("Delete"), this);
+ myDeleteAction = ModuleBase_Tools::createAction(QIcon(":pictures/delete.png"), tr("Delete"),
+ myWorkshop->desktop(), this, SLOT(onDeleteItem()));
myDeleteAction->setEnabled(false);
- connect(myDeleteAction, SIGNAL(triggered(bool)), SLOT(onDeleteItem()));
myListControl->addAction(myDeleteAction);
myListControl->setContextMenuPolicy(Qt::ActionsContextMenu);
myWorkshop->module()->activateCustomPrs(myFeature,
ModuleBase_IModule::CustomizeHighlightedObjects, true);
+ clearSelectedHistory();
+ myWorkshop->updateCommandStatus();
}
//********************************************************************
ModuleBase_WidgetSelector::deactivate();
myWorkshop->module()->deactivateCustomPrs(ModuleBase_IModule::CustomizeHighlightedObjects, true);
+ clearSelectedHistory();
+ myWorkshop->updateCommandStatus();
}
//********************************************************************
bool ModuleBase_WidgetMultiSelector::storeValueCustom()
{
- // the value is stored on the selection changed signal processing
- // A rare case when plugin was not loaded.
+ // the value is stored on the selection changed signal processing
+ // A rare case when plugin was not loaded.
if (!myFeature)
return false;
//********************************************************************
bool ModuleBase_WidgetMultiSelector::restoreValueCustom()
{
- // A rare case when plugin was not loaded.
+ // A rare case when plugin was not loaded.
if (!myFeature)
return false;
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());
if (isDone) // 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
+ true);/// hope that something is redisplayed by object updated
return isDone;
}
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)
{
if (aValid) {
if (myFeature) {
// We can not select a result of our feature
- const std::list<ResultPtr>& aResList = myFeature->results();
+ std::list<ResultPtr> aResults;
+ ModelAPI_Tools::allResults(myFeature, aResults);
std::list<ResultPtr>::const_iterator aIt;
bool isSkipSelf = false;
- for (aIt = aResList.cbegin(); aIt != aResList.cend(); ++aIt) {
+ for (aIt = aResults.cbegin(); aIt != aResults.cend(); ++aIt) {
if ((*aIt) == aResult) {
isSkipSelf = true;
break;
//********************************************************************
bool ModuleBase_WidgetMultiSelector::processDelete()
{
+ appendFirstSelectionInHistory();
+
// find attribute indices to delete
std::set<int> anAttributeIds;
getSelectedAttributeIndices(anAttributeIds);
QModelIndexList aIndexes = myListControl->selectionModel()->selectedIndexes();
- //foreach(QModelIndex aIndex, aIndexes) {
- // aIndex.row();
- //}
// refill attribute by the items which indices are not in the list of ids
bool aDone = false;
// 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
+ true); /// hope that something is redisplayed by object updated
}
- foreach(QModelIndex aIndex, aIndexes) {
- myListControl->selectionModel()->select(aIndex, QItemSelectionModel::Select);
+
+ // 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);
+ }
+ }
}
+ appendSelectionInHistory();
return aDone;
}
void ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()
{
activateSelectionAndFilters(true);
- QList<ModuleBase_ViewerPrsPtr> anEmptyList;
- // This method will call Selection changed event which will call onSelectionChanged
- // To clear mySelection, myListControl and storeValue()
- // So, we don't need to call it
- myWorkshop->setSelected(anEmptyList);
+
+ if (!myFeature)
+ return;
+ /// store the selected type
+ AttributePtr anAttribute = myFeature->data()->attribute(attributeID());
+ std::string aType = anAttribute->attributeType();
+ if (aType == ModelAPI_AttributeSelectionList::typeId()) {
+ AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
+ aSelectionListAttr->setSelectionType(myTypeCombo->currentText().toStdString());
+ }
+
+ // clear attribute values
+ DataPtr aData = myFeature->data();
+ if (aType == ModelAPI_AttributeSelectionList::typeId()) {
+ AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
+ aSelectionListAttr->clear();
+ }
+ else if (aType == ModelAPI_AttributeRefList::typeId()) {
+ AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
+ aRefListAttr->clear();
+ }
+ else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
+ AttributeRefAttrListPtr aRefAttrListAttr = aData->refattrlist(attributeID());
+ aRefAttrListAttr->clear();
+ }
+
+ // update object is necessary to flush update signal. It leads to objects references map update
+ // and the operation presentation will not contain deleted items visualized as parameters of
+ // the feature.
+ updateObject(myFeature);
+ restoreValue();
+ myWorkshop->setSelected(getAttributeSelection());
+ // 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();
}
//********************************************************************
{
if (!myIsNeutralPointClear) {
QList<ModuleBase_ViewerPrsPtr> aSelected = getFilteredSelected();
- if (aSelected.size() == 0)
- return;
+ // do not clear selected object
+ if (aSelected.size() == 0) {
+ if (!getAttributeSelection().empty()) {
+ // Restore selection in the viewer by the attribute selection list
+ // it should be postponed to exit from the selectionChanged processing
+ static Events_ID anEvent = Events_Loop::eventByName(EVENT_UPDATE_BY_WIDGET_SELECTION);
+ ModelAPI_EventCreator::get()->sendUpdated(myFeature, anEvent);
+ Events_Loop::loop()->flush(anEvent);
+ return;
+ }
+ }
}
+ 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
+ // 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()");
}
}
//********************************************************************
-QIntList ModuleBase_WidgetMultiSelector::getShapeTypes() const
+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
{
QIntList aShapeTypes;
aShapeTypes.append(ModuleBase_Tools::shapeType(myTypeCombo->currentText()));
}
else {
- for (int i = 0, aCount = myTypeCombo->count(); i < aCount; i++) {
- TopAbs_ShapeEnum aType = ModuleBase_Tools::shapeType(myTypeCombo->itemText(i));
- aShapeTypes.append(aType);
- if (aType == TopAbs_SOLID)
- aShapeTypes.append(TopAbs_COMPSOLID);
- }
+ for (int i = 0, aCount = myTypeCombo->count(); i < aCount; i++)
+ aShapeTypes.append(ModuleBase_Tools::shapeType(myTypeCombo->itemText(i)));
}
return aShapeTypes;
}
//********************************************************************
-void ModuleBase_WidgetMultiSelector::setCurrentShapeType(const TopAbs_ShapeEnum theShapeType)
+void ModuleBase_WidgetMultiSelector::setCurrentShapeType(const int theShapeType)
{
QString aShapeTypeName;
-
+
for (int idx = 0; idx < myTypeCombo->count(); ++idx) {
aShapeTypeName = myTypeCombo->itemText(idx);
- TopAbs_ShapeEnum aRefType = ModuleBase_Tools::shapeType(aShapeTypeName);
+ int aRefType = ModuleBase_Tools::shapeType(aShapeTypeName);
if(aRefType == theShapeType && idx != myTypeCombo->currentIndex()) {
bool aWasActivated = activateSelectionAndFilters(false);
bool isBlocked = myTypeCombo->blockSignals(true);
return aType;
}
+//********************************************************************
+void ModuleBase_WidgetMultiSelector::clearSelection()
+{
+ bool isClearInNeutralPoint = myIsNeutralPointClear;
+ myIsNeutralPointClear = true;
+
+ QList<ModuleBase_ViewerPrsPtr> anEmptyList;
+ // This method will call Selection changed event which will call onSelectionChanged
+ // To clear mySelection, myListControl and storeValue()
+ // So, we don't need to call it
+ myWorkshop->setSelected(anEmptyList);
+
+ myIsNeutralPointClear = isClearInNeutralPoint;
+}
+
//********************************************************************
void ModuleBase_WidgetMultiSelector::onCopyItem()
{
QList<QListWidgetItem*> aItems = myListControl->selectedItems();
myCopyAction->setEnabled(!aItems.isEmpty());
myDeleteAction->setEnabled(!aItems.isEmpty());
-
+
myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeHighlightedObjects,
true);
}
}
void ModuleBase_WidgetMultiSelector::convertIndicesToViewerSelection(std::set<int> theAttributeIds,
- QList<ModuleBase_ViewerPrsPtr>& theValues) const
+ QList<ModuleBase_ViewerPrsPtr>& theValues) const
{
if(myFeature.get() == NULL)
return;
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);
}
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);
}
}
else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
std::set<AttributePtr> anAttributes;
- QList<ModuleBase_ViewerPrsPtr>::const_iterator anIt = theValues.begin(), aLast = theValues.end();
+ QList<ModuleBase_ViewerPrsPtr>::const_iterator
+ anIt = theValues.begin(), aLast = theValues.end();
ObjectPtr anObject;
GeomShapePtr aShape;
for (; anIt != aLast; anIt++) {
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);
}
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);
for (; anIt != aLast && !aFound; anIt++) {
GeomShapePtr aCShape = *anIt;
if (aCShape.get())
- aFound = aCShape->isEqual(aShape);
+ aFound = aCShape->isSame(aShape);
}
}
return aFound;