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