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