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