Salome HOME
Update of GUI
[modules/shaper.git] / src / ModuleBase / ModuleBase_WidgetMultiSelector.cpp
1 // Copyright (C) 2014-2019  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include <ModuleBase_WidgetMultiSelector.h>
21
22 #include <GeomAPI_AISObject.h>
23
24 #include <ModuleBase_ActionIntParameter.h>
25 #include <ModuleBase_Definitions.h>
26 #include <ModuleBase_Events.h>
27 #include <ModuleBase_IconFactory.h>
28 #include <ModuleBase_IModule.h>
29 #include <ModuleBase_ISelection.h>
30 #include <ModuleBase_ISelectionActivate.h>
31 #include <ModuleBase_IPropertyPanel.h>
32 #include <ModuleBase_IViewer.h>
33 #include <ModuleBase_IWorkshop.h>
34 #include <ModuleBase_ListView.h>
35 #include <ModuleBase_ResultPrs.h>
36 #include <ModuleBase_Tools.h>
37 #include <ModuleBase_ViewerPrs.h>
38 #include <ModuleBase_WidgetShapeSelector.h>
39 #include <ModuleBase_ChoiceCtrl.h>
40 #include <ModuleBase_WidgetSelectionFilter.h>
41
42 #include <ModelAPI_Data.h>
43 #include <ModelAPI_Object.h>
44 #include <ModelAPI_AttributeSelectionList.h>
45 #include <ModelAPI_AttributeRefList.h>
46 #include <ModelAPI_AttributeRefAttrList.h>
47 #include <ModelAPI_Tools.h>
48 #include <ModelAPI_Events.h>
49
50 #include <Config_WidgetAPI.h>
51
52 #include <AIS_InteractiveObject.hxx>
53
54 #include <QGridLayout>
55 #include <QLabel>
56 #include <QListWidget>
57 #include <QObject>
58 #include <QString>
59 #include <QComboBox>
60 #include <QEvent>
61 #include <QApplication>
62 #include <QClipboard>
63 #include <QTimer>
64 #include <QMainWindow>
65 #include <QCheckBox>
66 #include <QPushButton>
67
68 #include <memory>
69 #include <string>
70
71 //#define DEBUG_UNDO_REDO
72
73 #ifdef DEBUG_UNDO_REDO
74 void printHistoryInfo(const QString& theMethodName, int theCurrentHistoryIndex,
75   QList<QList<std::shared_ptr<ModuleBase_ViewerPrs> > > theSelectedHistoryValues)
76 {
77   QStringList aSizes;
78   for (int i = 0; i < theSelectedHistoryValues.size(); i++)
79     aSizes.append(QString::number(theSelectedHistoryValues[i].size()));
80
81   std::cout << theMethodName.toStdString()
82             << "  current = " << theCurrentHistoryIndex
83             << " size(history) =  " << theSelectedHistoryValues.size()
84             << " (" << aSizes.join(", ").toStdString() << ")"
85             << std::endl;
86 }
87 #endif
88
89
90 QStringList getIconsList(const QStringList& theNames)
91 {
92   QStringList aIcons;
93   foreach (QString aName, theNames) {
94     QString aUName = aName.toUpper();
95     if ((aUName == "VERTICES") || (aUName == "VERTEX"))
96       aIcons << ":pictures/vertex32.png";
97     else if ((aUName == "EDGES") || (aUName == "EDGE"))
98       aIcons << ":pictures/edge32.png";
99     else if ((aUName == "FACES") || (aUName == "FACE"))
100       aIcons << ":pictures/face32.png";
101     else if ((aUName == "SOLIDS") || (aUName == "SOLID"))
102       aIcons << ":pictures/solid32.png";
103   }
104   return aIcons;
105 }
106
107 /// Stores default values of selected option (selection mode)
108 /// It is used only in case if myTypeCtrl is used
109 static QMap<std::string, std::string> defaultValues;
110
111
112 ModuleBase_WidgetMultiSelector::ModuleBase_WidgetMultiSelector(QWidget* theParent,
113                                                                ModuleBase_IWorkshop* theWorkshop,
114                                                                const Config_WidgetAPI* theData)
115 : ModuleBase_WidgetSelector(theParent, theWorkshop, theData),
116   myIsSetSelectionBlocked(false), myCurrentHistoryIndex(-1),
117   myIsFirst(true), myFiltersWgt(0)
118 {
119   std::string aPropertyTypes = theData->getProperty("type_choice");
120   QString aTypesStr = aPropertyTypes.c_str();
121   myShapeTypes = aTypesStr.split(' ', QString::SkipEmptyParts);
122   myIsUseChoice = theData->getBooleanAttribute("use_choice", false);
123
124   QVBoxLayout* aMainLay = new QVBoxLayout(this);
125   ModuleBase_Tools::adjustMargins(aMainLay);
126
127   QStringList aIconsList = getIconsList(myShapeTypes);
128   myTypeCtrl = new ModuleBase_ChoiceCtrl(this, myShapeTypes, aIconsList);
129   myTypeCtrl->setLabel(tr("Type"));
130   myTypeCtrl->setValue(0);
131   aMainLay->addWidget(myTypeCtrl);
132   myDefMode = myShapeTypes.first().toStdString();
133
134   // There is no sense to parameterize list of types while we can not parameterize selection mode
135   // if the xml definition contains one type, the controls to select a type should not be shown
136   if (myShapeTypes.size() <= 1 || !myIsUseChoice) {
137     myTypeCtrl->setVisible(false);
138   }
139
140   QString aLabelText = translate(theData->getProperty("label"));
141   if (aLabelText.size() > 0) {
142     QWidget* aLabelWgt = new QWidget(this);
143     QHBoxLayout* aLabelLayout = new QHBoxLayout(aLabelWgt);
144     aLabelLayout->setContentsMargins(0, 0, 0, 0);
145     aMainLay->addWidget(aLabelWgt);
146
147     QLabel* aListLabel = new QLabel(aLabelText, this);
148     aLabelLayout->addWidget(aListLabel);
149     // if the xml definition contains one type, an information label
150     // should be shown near to the latest
151     if (myShapeTypes.size() <= 1) {
152       QString aLabelIcon = QString::fromStdString(theData->widgetIcon());
153       if (!aLabelIcon.isEmpty()) {
154         QLabel* aSelectedLabel = new QLabel("", this);
155         aSelectedLabel->setPixmap(ModuleBase_IconFactory::loadPixmap(aLabelIcon));
156         aLabelLayout->addWidget(aSelectedLabel);
157         aLabelLayout->addStretch(1);
158       }
159     }
160   }
161
162   QString aToolTip = QString::fromStdString(theData->widgetTooltip());
163   QString anObjName = QString::fromStdString(attributeID());
164   myListView = new ModuleBase_ListView(this, anObjName, aToolTip);
165   connect(myListView->getControl(), SIGNAL(itemSelectionChanged()), SLOT(onListSelection()));
166   connect(myListView, SIGNAL(deleteActionClicked()), SLOT(onDeleteItem()));
167   connect(myListView, SIGNAL(listActivated()), SLOT(onListActivated()));
168
169   aMainLay->addWidget(myListView->getControl());
170   connect(myTypeCtrl, SIGNAL(valueChanged(int)), this, SLOT(onSelectionTypeChanged()));
171
172   std::string aUseFilters = theData->getProperty("use_filters");
173   if (aUseFilters.length() > 0) {
174     QWidget* aFltrWgt = new QWidget(this);
175     QHBoxLayout* aFltrLayout = new QHBoxLayout(aFltrWgt);
176
177     myFiltersWgt = new ModuleBase_FilterStarter(aUseFilters.c_str(), aFltrWgt, theWorkshop);
178     aFltrLayout->addWidget(myFiltersWgt);
179
180     aFltrLayout->addStretch();
181
182     QPushButton* aShowBtn = new QPushButton(tr("Show only"), aFltrWgt);
183     connect(aShowBtn, SIGNAL(clicked(bool)), SLOT(onShowOnly()));
184     aFltrLayout->addWidget(aShowBtn);
185
186     aMainLay->addWidget(aFltrWgt);
187   }
188
189   bool aSameTop = theData->getBooleanAttribute("same_topology", false);
190   if (aSameTop) {
191     myGeomCheck = new QCheckBox(tr("Add elements that share the same topology"), this);
192     aMainLay->addWidget(myGeomCheck);
193     connect(myGeomCheck, SIGNAL(toggled(bool)), SLOT(onSameTopology(bool)));
194   }
195   else
196     myGeomCheck = 0;
197
198   myIsNeutralPointClear = theData->getBooleanAttribute("clear_in_neutral_point", true);
199   if (myShapeTypes.size() > 1 || myIsUseChoice) {
200     if (defaultValues.contains(myFeatureId + attributeID())) {
201       myDefMode = defaultValues[myFeatureId + attributeID()];
202       myTypeCtrl->setValue(myDefMode.c_str());
203     }
204   }
205 }
206
207 ModuleBase_WidgetMultiSelector::~ModuleBase_WidgetMultiSelector()
208 {
209 }
210
211 //********************************************************************
212 void ModuleBase_WidgetMultiSelector::activateCustom()
213 {
214   ModuleBase_WidgetSelector::activateCustom();
215
216   myWorkshop->module()->activateCustomPrs(myFeature,
217                             ModuleBase_IModule::CustomizeHighlightedObjects, true);
218   clearSelectedHistory();
219 }
220
221 //********************************************************************
222 void ModuleBase_WidgetMultiSelector::deactivate()
223 {
224   ModuleBase_WidgetSelector::deactivate();
225
226   myWorkshop->module()->deactivateCustomPrs(ModuleBase_IModule::CustomizeHighlightedObjects, true);
227   clearSelectedHistory();
228 }
229
230 //********************************************************************
231 void ModuleBase_WidgetMultiSelector::updateAfterDeactivation()
232 {
233   // restore previous Undo/Redo workshop state
234   myWorkshop->updateCommandStatus();
235 }
236
237 //********************************************************************
238 void ModuleBase_WidgetMultiSelector::updateAfterActivation()
239 {
240   // fill Undo/Redo actions with current information
241   myWorkshop->updateCommandStatus();
242 }
243
244 //********************************************************************
245 bool ModuleBase_WidgetMultiSelector::storeValueCustom()
246 {
247   // the value is stored on the selection changed signal processing
248   // A rare case when plugin was not loaded.
249   if (!myFeature)
250     return false;
251
252   AttributePtr anAttribute = myFeature->data()->attribute(attributeID());
253   std::string aType = anAttribute->attributeType();
254   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
255     AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
256     std::string aMode = myTypeCtrl->textValue().toStdString();
257     if (myTypeCtrl->isVisible() && myIsFirst && (!myDefMode.empty()))
258       aMode = myDefMode;
259
260     aSelectionListAttr->setSelectionType(aMode);
261     myIsFirst = false;
262   }
263   return true;
264 }
265
266 //********************************************************************
267 bool ModuleBase_WidgetMultiSelector::restoreValueCustom()
268 {
269   // A rare case when plugin was not loaded.
270   if (!myFeature)
271     return false;
272
273   AttributePtr anAttribute = myFeature->data()->attribute(attributeID());
274   AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
275   std::string aType = anAttribute->attributeType();
276   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
277     // Restore shape type
278     std::string aSelectionType = aSelectionListAttr->selectionType().c_str();
279     if (!aSelectionType.empty()) {
280       setCurrentShapeType(ModuleBase_Tools::shapeType(aSelectionType.c_str()));
281       myDefMode = aSelectionType;
282       myIsFirst = false;
283     }
284   }
285   if (myGeomCheck)
286     myGeomCheck->setChecked(aSelectionListAttr->isGeometricalSelection());
287   updateSelectionList();
288   return true;
289 }
290
291 //********************************************************************
292 bool ModuleBase_WidgetMultiSelector::setSelection(QList<ModuleBase_ViewerPrsPtr>& theValues,
293                                                   const bool theToValidate)
294 {
295   if (myIsSetSelectionBlocked)
296     return false;
297
298   AttributeSelectionListPtr aSelectionListAttr;
299   if (attribute()->attributeType() == ModelAPI_AttributeSelectionList::typeId())
300     aSelectionListAttr = std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(attribute());
301   if (aSelectionListAttr.get())
302     aSelectionListAttr->cashValues(true);
303
304   /// remove unused objects from the model attribute.
305   /// It should be performed before new attributes append.
306   bool isDone = removeUnusedAttributeObjects(theValues);
307
308   QList<ModuleBase_ViewerPrsPtr> anInvalidValues;
309   QList<ModuleBase_ViewerPrsPtr> anAttributeValues;
310   QList<ModuleBase_ViewerPrsPtr>::const_iterator anIt = theValues.begin(), aLast = theValues.end();
311   for (; anIt != aLast; anIt++) {
312     ModuleBase_ViewerPrsPtr aValue = *anIt;
313     // do not validate and append to attribute selection presentation if it exists in the attribute
314     ObjectPtr anObject;
315     GeomShapePtr aShape;
316     getGeomSelection(aValue, anObject, aShape);
317     if (ModuleBase_Tools::hasObject(attribute(), anObject, aShape, myWorkshop, myIsInValidate)) {
318       anAttributeValues.append(aValue);
319       continue;
320     }
321     if (theToValidate && !isValidInFilters(aValue))
322       anInvalidValues.append(aValue);
323   }
324   bool aHasInvalidValues = anInvalidValues.size() > 0;
325
326   for (anIt = theValues.begin(); anIt != aLast; anIt++) {
327     ModuleBase_ViewerPrsPtr aValue = *anIt;
328     bool aProcessed = false;
329     if ((aHasInvalidValues && anInvalidValues.contains(aValue)) ||
330         anAttributeValues.contains(aValue))
331       continue;
332     aProcessed = setSelectionCustom(aValue); /// it is not optimal as hasObject() is already checked
333     // if there is at least one set, the result is true
334     isDone = isDone || aProcessed;
335   }
336   // Check the selection with validators
337   QString aError = getError();
338   if (aError.length() > 0) {
339     aSelectionListAttr->clear();
340     isDone = false;
341   }
342   // updateObject - to update/redisplay feature
343   // it is commented in order to perfom it outside the method
344   //if (isDone) {
345     //updateObject(myFeature);
346     // this emit is necessary to call store/restore method an restore type of selection
347     //emit valuesChanged();
348   //}
349
350   if (aSelectionListAttr.get())
351     aSelectionListAttr->cashValues(false);
352
353   theValues.clear();
354   if (!anInvalidValues.empty())
355     theValues.append(anInvalidValues);
356
357   if (isDone) // may be the feature's result is not displayed, but attributes should be
358     myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeArguments,
359                              true);/// hope that something is redisplayed by object updated
360
361   return isDone;
362 }
363
364 //********************************************************************
365 void ModuleBase_WidgetMultiSelector::getHighlighted(QList<ModuleBase_ViewerPrsPtr>& theValues)
366 {
367   std::set<int> anAttributeIds;
368   getSelectedAttributeIndices(anAttributeIds);
369   if (!anAttributeIds.empty())
370     convertIndicesToViewerSelection(anAttributeIds, theValues);
371 }
372
373 //********************************************************************
374 bool ModuleBase_WidgetMultiSelector::canProcessAction(ModuleBase_ActionType theActionType,
375                                                       bool& isActionEnabled)
376 {
377   isActionEnabled = false;
378   bool aCanProcess = false;
379   switch (theActionType) {
380     case ActionUndo:
381     case ActionRedo: {
382       aCanProcess = true;
383       isActionEnabled = theActionType == ActionUndo ? myCurrentHistoryIndex > 0
384           : (mySelectedHistoryValues.size() > 0 &&
385              myCurrentHistoryIndex < mySelectedHistoryValues.size() - 1);
386     }
387     break;
388     default:
389       aCanProcess = ModuleBase_WidgetSelector::canProcessAction(theActionType, isActionEnabled);
390     break;
391   }
392   return aCanProcess;
393 }
394
395 //********************************************************************
396 bool ModuleBase_WidgetMultiSelector::processAction(ModuleBase_ActionType theActionType,
397                                                    const ActionParamPtr& theParam)
398 {
399   switch (theActionType) {
400     case ActionUndo:
401     case ActionRedo: {
402       ActionIntParamPtr aParam =
403         std::dynamic_pointer_cast<ModuleBase_ActionIntParameter>(theParam);
404       int aNb = aParam->value();
405       if (theActionType == ActionUndo)
406         myCurrentHistoryIndex -= aNb;
407       else
408         myCurrentHistoryIndex += aNb;
409       QList<ModuleBase_ViewerPrsPtr> aSelected = mySelectedHistoryValues[myCurrentHistoryIndex];
410       // equal vertices should not be used here
411       ModuleBase_ISelection::filterSelectionOnEqualPoints(aSelected);
412       bool isDone = setSelection(aSelected,
413                                  false /*need not validate because values already was in list*/);
414       updateOnSelectionChanged(isDone);
415
416       myWorkshop->updateCommandStatus();
417 #ifdef DEBUG_UNDO_REDO
418       printHistoryInfo(QString("processAction %1").arg(theActionType == ActionUndo ? "Undo"
419         : "Redo"), myCurrentHistoryIndex, mySelectedHistoryValues);
420 #endif
421       return true;
422     }
423     default:
424       return ModuleBase_ModelWidget::processAction(theActionType, theParam);
425   }
426 }
427
428 //********************************************************************
429 bool ModuleBase_WidgetMultiSelector::isValidSelectionCustom(const ModuleBase_ViewerPrsPtr& thePrs)
430 {
431   bool aValid = ModuleBase_WidgetSelector::isValidSelectionCustom(thePrs);
432   if (aValid) {
433     ResultPtr aResult = myWorkshop->selection()->getResult(thePrs);
434     if (!aResult.get()) { // In case if a feature was selected
435       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(thePrs->object());
436       if (aFeature.get())
437         aResult = aFeature->firstResult();
438     }
439     aValid = aResult.get() != NULL;
440     if (aValid) {
441       if (myFeature) {
442         // We can not select a result of our feature
443         std::list<ResultPtr> aResults;
444         ModelAPI_Tools::allResults(myFeature, aResults);
445         std::list<ResultPtr>::const_iterator aIt;
446         bool isSkipSelf = false;
447         for (aIt = aResults.cbegin(); aIt != aResults.cend(); ++aIt) {
448           if ((*aIt) == aResult) {
449             isSkipSelf = true;
450             break;
451           }
452         }
453         if (isSkipSelf)
454           aValid = false;
455       }
456     }
457   }
458   return aValid;
459 }
460
461 //********************************************************************
462 bool ModuleBase_WidgetMultiSelector::processDelete()
463 {
464   appendFirstSelectionInHistory();
465
466   // find attribute indices to delete
467   std::set<int> anAttributeIds;
468   getSelectedAttributeIndices(anAttributeIds);
469
470   QModelIndexList anIndices = myListView->getControl()->selectionModel()->selectedIndexes();
471
472   // refill attribute by the items which indices are not in the list of ids
473   bool aDone = false;
474   DataPtr aData = myFeature->data();
475   AttributePtr anAttribute = aData->attribute(attributeID());
476   std::string aType = anAttribute->attributeType();
477   aDone = !anAttributeIds.empty();
478   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
479     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
480     aSelectionListAttr->remove(anAttributeIds);
481
482   }
483   else if (aType == ModelAPI_AttributeRefList::typeId()) {
484     AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
485     aRefListAttr->remove(anAttributeIds);
486   }
487   else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
488     AttributeRefAttrListPtr aRefAttrListAttr = aData->refattrlist(attributeID());
489     aRefAttrListAttr->remove(anAttributeIds);
490   }
491
492   if (aDone) {
493     // update object is necessary to flush update signal. It leads to objects references map update
494     // and the operation presentation will not contain deleted items visualized as parameters of
495     // the feature.
496     updateObject(myFeature);
497
498     restoreValue();
499     myWorkshop->setSelected(getAttributeSelection());
500
501     // may be the feature's result is not displayed, but attributes should be
502     myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeArguments,
503                               true); /// hope that something is redisplayed by object updated
504   }
505
506   // Restore selection
507   myListView->restoreSelection(anIndices);
508
509   appendSelectionInHistory();
510   return true/*aDone*/; // following #2438 Delete should be processed even if nothing is delete
511 }
512
513 //********************************************************************
514 QList<QWidget*> ModuleBase_WidgetMultiSelector::getControls() const
515 {
516   QList<QWidget*> result;
517   result << myListView->getControl();
518   return result;
519 }
520
521 //********************************************************************
522 void ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()
523 {
524   // Clear current selection in order to avoid updating of object browser with obsolete indexes
525   // which can appear because of results deletetion after changing a type of selection
526   QString aSelectionType = myTypeCtrl->textValue();
527   QList<ModuleBase_ViewerPrsPtr> aEmptyList;
528   myWorkshop->setSelected(aEmptyList);
529
530   updateSelectionModesAndFilters(true);
531   myWorkshop->selectionActivate()->updateSelectionModes();
532
533   if (!myFeature)
534     return;
535   /// store the selected type
536   AttributePtr anAttribute = myFeature->data()->attribute(attributeID());
537   std::string aType = anAttribute->attributeType();
538   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
539     AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
540     aSelectionListAttr->setSelectionType(aSelectionType.toStdString());
541   }
542
543   // clear attribute values
544   DataPtr aData = myFeature->data();
545   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
546     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
547     aSelectionListAttr->clear();
548   }
549   else if (aType == ModelAPI_AttributeRefList::typeId()) {
550     AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
551     aRefListAttr->clear();
552   }
553   else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
554     AttributeRefAttrListPtr aRefAttrListAttr = aData->refattrlist(attributeID());
555     aRefAttrListAttr->clear();
556   }
557
558   // update object is necessary to flush update signal. It leads to objects references map update
559   // and the operation presentation will not contain deleted items visualized as parameters of
560   // the feature.
561   updateObject(myFeature);
562   restoreValue();
563   myWorkshop->setSelected(getAttributeSelection());
564   // may be the feature's result is not displayed, but attributes should be
565   myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeArguments,
566                             true); /// hope that something is redisplayed by object updated
567   // clear history should follow after set selected to do not increase history by setSelected
568   clearSelectedHistory();
569
570   if (myWorkshop->propertyPanel()->activeWidget() != this)
571     myWorkshop->propertyPanel()->activateWidget(this);
572 }
573
574 //********************************************************************
575 bool ModuleBase_WidgetMultiSelector::processSelection()
576 {
577   if (!myIsNeutralPointClear) {
578     QList<ModuleBase_ViewerPrsPtr> aSelected = getFilteredSelected();
579     // do not clear selected object
580     if (aSelected.size() == 0) {
581       if (!getAttributeSelection().empty()) {
582         // Restore selection in the viewer by the attribute selection list
583         // it should be postponed to exit from the selectionChanged processing
584         static Events_ID anEvent = Events_Loop::eventByName(EVENT_UPDATE_BY_WIDGET_SELECTION);
585         ModelAPI_EventCreator::get()->sendUpdated(myFeature, anEvent);
586         Events_Loop::loop()->flush(anEvent);
587         return true;
588       }
589     }
590   }
591   appendFirstSelectionInHistory();
592   bool aDone = ModuleBase_WidgetSelector::processSelection();
593   appendSelectionInHistory();
594   return aDone;
595 }
596
597 void ModuleBase_WidgetMultiSelector::appendFirstSelectionInHistory()
598 {
599   if (mySelectedHistoryValues.empty()) {
600     myCurrentHistoryIndex++;
601     mySelectedHistoryValues.append(getAttributeSelection());
602
603 #ifdef DEBUG_UNDO_REDO
604     printHistoryInfo("appendSelectionInHistory", myCurrentHistoryIndex, mySelectedHistoryValues);
605 #endif
606   }
607 }
608
609 void ModuleBase_WidgetMultiSelector::appendSelectionInHistory()
610 {
611   while (myCurrentHistoryIndex != mySelectedHistoryValues.count() - 1)
612     mySelectedHistoryValues.removeLast();
613
614   QList<ModuleBase_ViewerPrsPtr> aSelected = getFilteredSelected();
615   myCurrentHistoryIndex++;
616   mySelectedHistoryValues.append(aSelected);
617   myWorkshop->updateCommandStatus();
618
619 #ifdef DEBUG_UNDO_REDO
620   printHistoryInfo("appendSelectionInHistory", myCurrentHistoryIndex, mySelectedHistoryValues);
621 #endif
622 }
623
624 void ModuleBase_WidgetMultiSelector::clearSelectedHistory()
625 {
626   mySelectedHistoryValues.clear();
627   myCurrentHistoryIndex = -1;
628   myWorkshop->updateCommandStatus();
629
630 #ifdef DEBUG_UNDO_REDO
631   printHistoryInfo("clearSelectedHistory", myCurrentHistoryIndex, mySelectedHistoryValues);
632 #endif
633 }
634
635 void ModuleBase_WidgetMultiSelector::updateFocus()
636 {
637   // Set focus to List control in order to make possible
638   // to use Tab key for transfer the focus to next widgets
639   ModuleBase_Tools::setFocus(myListView->getControl(),
640                              "ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()");
641 }
642
643 //********************************************************************
644 void ModuleBase_WidgetMultiSelector::updateSelectionName()
645 {
646 }
647
648 //********************************************************************
649 void ModuleBase_WidgetMultiSelector::updateOnSelectionChanged(const bool theDone)
650 {
651   if (myIsSetSelectionBlocked)
652     return;
653   ModuleBase_WidgetSelector::updateOnSelectionChanged(theDone);
654
655   // according to #2154 we need to update OB selection when selection in the viewer happens
656   // it is important to flush sinchronize selection signal after flush of Update/Create/Delete.
657   // because we need that Object Browser has been already updated when synchronize happens.
658
659   // Restore selection in the viewer by the attribute selection list
660   // it is possible that diring selection attribute filling, selection in Object Browser
661   // is changed(some items were removed/added) and as result, selection in the viewer
662   // differs from the selection come to this method. By next rows, we restore selection
663   // in the viewer according to content of selection attribute. Case is Edge selection in Group
664   myIsSetSelectionBlocked = true;
665   static Events_ID anEvent = Events_Loop::eventByName(EVENT_UPDATE_BY_WIDGET_SELECTION);
666   ModelAPI_EventCreator::get()->sendUpdated(myFeature, anEvent);
667   Events_Loop::loop()->flush(anEvent);
668   myIsSetSelectionBlocked = false;
669 }
670
671 //********************************************************************
672 QIntList ModuleBase_WidgetMultiSelector::shapeTypes() const
673 {
674   QIntList aShapeTypes;
675
676   if (myShapeTypes.length() > 1 && myIsUseChoice) {
677     aShapeTypes.append(ModuleBase_Tools::shapeType(myTypeCtrl->textValue()));
678   }
679   else {
680     foreach (QString aType, myShapeTypes) {
681       aShapeTypes.append(ModuleBase_Tools::shapeType(aType));
682     }
683   }
684   return aShapeTypes;
685 }
686
687 //********************************************************************
688 void ModuleBase_WidgetMultiSelector::setCurrentShapeType(const int theShapeType)
689 {
690   QString aShapeTypeName;
691
692   int idx = 0;
693   foreach (QString aShapeTypeName, myShapeTypes) {
694     int aRefType = ModuleBase_Tools::shapeType(aShapeTypeName);
695     if(aRefType == theShapeType && idx != myTypeCtrl->value()) {
696       updateSelectionModesAndFilters(false);
697       bool isBlocked = myTypeCtrl->blockSignals(true);
698       myTypeCtrl->setValue(idx);
699       myTypeCtrl->blockSignals(isBlocked);
700       updateSelectionModesAndFilters(true);
701       break;
702     }
703     idx++;
704   }
705 }
706
707 QList<ModuleBase_ViewerPrsPtr> ModuleBase_WidgetMultiSelector::getAttributeSelection() const
708 {
709   QList<ModuleBase_ViewerPrsPtr> aSelected;
710   convertIndicesToViewerSelection(std::set<int>(), aSelected);
711   return aSelected;
712 }
713
714 //********************************************************************
715 void ModuleBase_WidgetMultiSelector::updateSelectionList()
716 {
717   myListView->getControl()->clear();
718
719   DataPtr aData = myFeature->data();
720   AttributePtr anAttribute = aData->attribute(attributeID());
721   std::string aType = anAttribute->attributeType();
722   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
723     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
724     for (int i = 0; i < aSelectionListAttr->size(); i++) {
725       AttributeSelectionPtr aAttr = aSelectionListAttr->value(i);
726       myListView->addItem(aAttr->namingName().c_str(), i);
727     }
728   }
729   else if (aType == ModelAPI_AttributeRefList::typeId()) {
730     AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
731     for (int i = 0; i < aRefListAttr->size(); i++) {
732       ObjectPtr anObject = aRefListAttr->object(i);
733       if (anObject.get()) {
734         myListView->addItem(anObject->data()->name().c_str(), i);
735       }
736     }
737   }
738   else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
739     AttributeRefAttrListPtr aRefAttrListAttr = aData->refattrlist(attributeID());
740     for (int i = 0; i < aRefAttrListAttr->size(); i++) {
741       AttributePtr anAttribute = aRefAttrListAttr->attribute(i);
742       QString aName;
743       if (anAttribute.get()) {
744         std::string anAttrName = ModuleBase_Tools::generateName(anAttribute, myWorkshop);
745         aName = QString::fromStdString(anAttrName);
746       }
747       else {
748         ObjectPtr anObject = aRefAttrListAttr->object(i);
749         if (anObject.get()) {
750           aName = anObject->data()->name().c_str();
751         }
752       }
753       myListView->addItem(aName, i);
754     }
755   }
756
757   // We have to call repaint because sometimes the List control is not updated
758   myListView->getControl()->repaint();
759 }
760
761 //********************************************************************
762 std::string ModuleBase_WidgetMultiSelector::validatorType(const QString& theType) const
763 {
764   std::string aType;
765
766   if (theType == "Vertices")
767     aType = "vertex";
768   else if (theType == "Edges")
769     aType = "edge";
770   else if (theType == "Faces")
771     aType = "face";
772   else if (theType == "Solids")
773     aType = "solid";
774
775   return aType;
776 }
777
778 //********************************************************************
779 void ModuleBase_WidgetMultiSelector::clearSelection()
780 {
781   bool isClearInNeutralPoint = myIsNeutralPointClear;
782   myIsNeutralPointClear = true;
783
784   QList<ModuleBase_ViewerPrsPtr> anEmptyList;
785   // This method will call Selection changed event which will call onSelectionChanged
786   // To clear mySelection, myListView and storeValue()
787   // So, we don't need to call it
788   myWorkshop->setSelected(anEmptyList);
789
790   myIsNeutralPointClear = isClearInNeutralPoint;
791 }
792
793 //********************************************************************
794 void ModuleBase_WidgetMultiSelector::onDeleteItem()
795 {
796   processDelete();
797 }
798
799 //********************************************************************
800 void ModuleBase_WidgetMultiSelector::onListSelection()
801 {
802   myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeHighlightedObjects,
803                                         true);
804 }
805
806 //********************************************************************
807 void ModuleBase_WidgetMultiSelector::getSelectedAttributeIndices(std::set<int>& theAttributeIds)
808 {
809   myListView->getSelectedIndices(theAttributeIds);
810 }
811
812 void ModuleBase_WidgetMultiSelector::convertIndicesToViewerSelection(std::set<int> theAttributeIds,
813                                                    QList<ModuleBase_ViewerPrsPtr>& theValues) const
814 {
815   if(myFeature.get() == NULL)
816     return;
817
818   DataPtr aData = myFeature->data();
819   AttributePtr anAttribute = aData->attribute(attributeID());
820   std::string aType = anAttribute->attributeType();
821   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
822     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
823     for (int i = 0; i < aSelectionListAttr->size(); i++) {
824       // filter by attribute indices only if the container is not empty otherwise return all items
825       if (!theAttributeIds.empty() && theAttributeIds.find(i) == theAttributeIds.end())
826         continue;
827       AttributeSelectionPtr anAttr = aSelectionListAttr->value(i);
828       ObjectPtr anObject = anAttr->contextObject();
829       if (anObject.get())
830         theValues.append(std::shared_ptr<ModuleBase_ViewerPrs>(
831                new ModuleBase_ViewerPrs(anObject, anAttr->value(), NULL)));
832     }
833   }
834   else if (aType == ModelAPI_AttributeRefList::typeId()) {
835     AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
836     for (int i = 0; i < aRefListAttr->size(); i++) {
837       // filter by attribute indices only if the container is not empty otherwise return all items
838       if (!theAttributeIds.empty() && theAttributeIds.find(i) == theAttributeIds.end())
839         continue;
840       ObjectPtr anObject = aRefListAttr->object(i);
841       if (anObject.get()) {
842         theValues.append(std::shared_ptr<ModuleBase_ViewerPrs>(
843                new ModuleBase_ViewerPrs(anObject, GeomShapePtr(), NULL)));
844       }
845     }
846   }
847   else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
848     AttributeRefAttrListPtr aRefAttrListAttr = aData->refattrlist(attributeID());
849     for (int i = 0; i < aRefAttrListAttr->size(); i++) {
850       // filter by attribute indices only if the container is not empty otherwise return all items
851       if (!theAttributeIds.empty() && theAttributeIds.find(i) == theAttributeIds.end())
852         continue;
853       ObjectPtr anObject = aRefAttrListAttr->object(i);
854       if (!anObject.get())
855         continue;
856       TopoDS_Shape aShape;
857       AttributePtr anAttribute = aRefAttrListAttr->attribute(i);
858       if (anAttribute.get()) {
859         GeomShapePtr aGeomShape = ModuleBase_Tools::getShape(anAttribute, myWorkshop);
860         theValues.append(std::shared_ptr<ModuleBase_ViewerPrs>(
861                new ModuleBase_ViewerPrs(anObject, aGeomShape, NULL)));
862       }
863     }
864   }
865 }
866
867 bool ModuleBase_WidgetMultiSelector::removeUnusedAttributeObjects
868                                                  (QList<ModuleBase_ViewerPrsPtr>& theValues)
869 {
870   bool isDone = false;
871
872   std::map<ObjectPtr, std::set<GeomShapePtr> > aGeomSelection = convertSelection(theValues);
873   DataPtr aData = myFeature->data();
874   AttributePtr anAttribute = aData->attribute(attributeID());
875   std::string aType = anAttribute->attributeType();
876   std::set<GeomShapePtr> aShapes;
877   std::set<int> anIndicesToBeRemoved;
878   FeaturePtr aFeature;
879   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
880     // iteration through data model to find not selected elements to remove them
881     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
882     for (int i = 0; i < aSelectionListAttr->size(); i++) {
883       AttributeSelectionPtr anAttr = aSelectionListAttr->value(i);
884       //aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->contextObject());
885       //if (!aFeature.get()) { // Feature can not be found as geometry selection
886         bool aFound = findInSelection(
887           anAttr->contextObject(), anAttr->value(), aGeomSelection, myWorkshop);
888         if (!aFound)
889           anIndicesToBeRemoved.insert(i);
890 //      }
891     }
892     isDone = anIndicesToBeRemoved.size() > 0;
893     if (isDone)
894       aSelectionListAttr->remove(anIndicesToBeRemoved);
895   }
896   else if (aType == ModelAPI_AttributeRefList::typeId()) {
897     AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
898     for (int i = 0; i < aRefListAttr->size(); i++) {
899       ObjectPtr anObject = aRefListAttr->object(i);
900       if (anObject.get()) {
901         bool aFound = findInSelection(anObject, GeomShapePtr(), aGeomSelection,
902                                       myWorkshop);
903         if (!aFound)
904           anIndicesToBeRemoved.insert(i);
905       }
906     }
907     isDone = anIndicesToBeRemoved.size() > 0;
908     aRefListAttr->remove(anIndicesToBeRemoved);
909   }
910   else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
911     std::set<AttributePtr> anAttributes;
912     QList<ModuleBase_ViewerPrsPtr>::const_iterator
913       anIt = theValues.begin(), aLast = theValues.end();
914     ObjectPtr anObject;
915     GeomShapePtr aShape;
916     for (; anIt != aLast; anIt++) {
917       ModuleBase_ViewerPrsPtr aPrs = *anIt;
918       getGeomSelection(aPrs, anObject, aShape);
919       AttributePtr anAttr = myWorkshop->module()->findAttribute(anObject, aShape);
920       if (anAttr.get() && anAttributes.find(anAttr) == anAttributes.end())
921         anAttributes.insert(anAttr);
922     }
923
924     AttributeRefAttrListPtr aRefAttrListAttr = aData->refattrlist(attributeID());
925     for (int i = 0; i < aRefAttrListAttr->size(); i++) {
926       bool aFound = false;
927       if (aRefAttrListAttr->isAttribute(i)) {
928         AttributePtr anAttribute = aRefAttrListAttr->attribute(i);
929         aFound = anAttributes.find(anAttribute) != anAttributes.end();
930       }
931       else {
932         aFound = findInSelection(aRefAttrListAttr->object(i), GeomShapePtr(), aGeomSelection,
933                                  myWorkshop);
934       }
935       if (!aFound)
936         anIndicesToBeRemoved.insert(i);
937     }
938     isDone = anIndicesToBeRemoved.size() > 0;
939     aRefAttrListAttr->remove(anIndicesToBeRemoved);
940   }
941
942   return isDone;
943 }
944
945 std::map<ObjectPtr, std::set<GeomShapePtr> > ModuleBase_WidgetMultiSelector::convertSelection
946                                                      (QList<ModuleBase_ViewerPrsPtr>& theValues)
947 {
948   // convert prs list to objects map
949   std::map<ObjectPtr, std::set<GeomShapePtr> > aGeomSelection;
950   std::set<GeomShapePtr> aShapes;
951   QList<ModuleBase_ViewerPrsPtr>::const_iterator anIt = theValues.begin(), aLast = theValues.end();
952   ObjectPtr anObject;
953   GeomShapePtr aShape;
954   GeomShapePtr anEmptyShape(new GeomAPI_Shape());
955   for (; anIt != aLast; anIt++) {
956     ModuleBase_ViewerPrsPtr aPrs = *anIt;
957     getGeomSelection(aPrs, anObject, aShape);
958     aShapes.clear();
959     if (aGeomSelection.find(anObject) != aGeomSelection.end()) // found
960       aShapes = aGeomSelection[anObject];
961     // we need to know if there was an empty shape in selection for the object
962     if (!aShape.get())
963       aShape = anEmptyShape;
964     if (aShape.get() && aShapes.find(aShape) == aShapes.end()) // not found
965       aShapes.insert(aShape);
966     aGeomSelection[anObject] = aShapes;
967   }
968   return aGeomSelection;
969 }
970
971 bool ModuleBase_WidgetMultiSelector::findInSelection(const ObjectPtr& theObject,
972                               GeomShapePtr theShape,
973                               const std::map<ObjectPtr, std::set<GeomShapePtr> >& theGeomSelection,
974                               ModuleBase_IWorkshop* theWorkshop)
975 {
976   if (!theObject.get())
977     return false;
978   // issue #2154: we should not remove from list objects hidden in the viewer if selection
979   // was done with SHIFT button
980   if (theWorkshop->hasSHIFTPressed() && !theObject->isDisplayed())
981     return true;
982
983   bool aFound = false;
984   GeomShapePtr aShape = theShape;
985   if (!aShape.get()) {
986     // #2429 (the preselection of a sketch is not taken into account)
987     ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
988     if (aResult.get())
989       aShape = aResult->shape();
990   }
991   if (theGeomSelection.find(theObject) != theGeomSelection.end()) {// found
992     const std::set<GeomShapePtr>& aShapes = theGeomSelection.at(theObject);
993     std::set<GeomShapePtr>::const_iterator anIt = aShapes.begin(), aLast = aShapes.end();
994     for (; anIt != aLast && !aFound; anIt++) {
995       GeomShapePtr aCShape = *anIt;
996       if (aCShape.get())
997       {
998         // treat shape equal to context as null: 2219, keep order of shapes in list
999         if (aCShape->isNull()) { // in selection, shape of result is equal to selected shape
1000           // if so, here we need to check shape of result
1001           ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
1002           if (aResult.get())
1003             aCShape = aResult->shape();
1004         }
1005         aFound = aCShape->isSame(aShape);
1006       }
1007     }
1008   }
1009
1010   // issue #2903: (Possibility to hide faces) - check whether given shape is a hidden sub-shape
1011   if (!aFound && theShape.get() && theWorkshop->hasSHIFTPressed() && theObject->isDisplayed()) {
1012     AISObjectPtr anAIS = theWorkshop->findPresentation(theObject);
1013     if (anAIS.get() != NULL) {
1014       Handle(AIS_InteractiveObject) anAISIO = anAIS->impl<Handle(AIS_InteractiveObject)>();
1015
1016       Handle(ModuleBase_ResultPrs) aResultPrs = Handle(ModuleBase_ResultPrs)::DownCast(anAISIO);
1017       if (!aResultPrs.IsNull() && aResultPrs->isSubShapeHidden(theShape->impl<TopoDS_Shape>()))
1018         return true;
1019     }
1020   }
1021
1022   return aFound;
1023 }
1024
1025 QList<ActionInfo>
1026   ModuleBase_WidgetMultiSelector::actionsList(ModuleBase_ActionType theActionType) const
1027 {
1028   QList<ActionInfo> aList;
1029   if (myCurrentHistoryIndex > -1) {
1030     int i = 0;
1031     QString aTitle("Selection %1 items");
1032     QString aTit("Selection %1 item");
1033     QIcon aIcon(":pictures/selection.png");
1034     int aNb;
1035     switch (theActionType) {
1036     case ActionUndo:
1037       i = 1;
1038       while (i <= myCurrentHistoryIndex) {
1039         aNb = mySelectedHistoryValues.at(i).count();
1040         if (aNb == 1) {
1041           ActionInfo aInfo(aIcon, aTit.arg(aNb));
1042           aList.insert(0, aInfo);
1043         } else {
1044           ActionInfo aInfo(aIcon, aTitle.arg(aNb));
1045           aList.insert(0, aInfo);
1046         }
1047         i++;
1048       }
1049       break;
1050     case ActionRedo:
1051       i = mySelectedHistoryValues.length() - 1;
1052       while (i > myCurrentHistoryIndex) {
1053         aNb = mySelectedHistoryValues.at(i).count();
1054         if (aNb == 1) {
1055           ActionInfo aInfo(aIcon, aTit.arg(mySelectedHistoryValues.at(i).count()));
1056           aList.insert(0, aInfo);
1057         } else {
1058           ActionInfo aInfo(aIcon, aTitle.arg(mySelectedHistoryValues.at(i).count()));
1059           aList.insert(0, aInfo);
1060         }
1061         i--;
1062       }
1063       break;
1064     }
1065   }
1066   return aList;
1067 }
1068
1069
1070 void ModuleBase_WidgetMultiSelector::onFeatureAccepted()
1071 {
1072   defaultValues[myFeatureId + attributeID()] = myDefMode;
1073 }
1074
1075 void ModuleBase_WidgetMultiSelector::onListActivated()
1076 {
1077   //focusTo();
1078   emitFocusInWidget();
1079 }
1080
1081 void ModuleBase_WidgetMultiSelector::onSameTopology(bool theOn)
1082 {
1083   AttributePtr anAttribute = myFeature->data()->attribute(attributeID());
1084   std::string aType = anAttribute->attributeType();
1085   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
1086     AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
1087     aSelectionListAttr->setGeometricalSelection(theOn);
1088     updateObject(myFeature);
1089   }
1090 }
1091
1092 void ModuleBase_WidgetMultiSelector::onShowOnly()
1093 {
1094
1095 }