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