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