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