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