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