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