Salome HOME
Edit name for group
[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 #include <ModuleBase_WidgetShapeSelector.h>
23 #include <ModuleBase_ISelection.h>
24 #include <ModuleBase_IWorkshop.h>
25 #include <ModuleBase_IPropertyPanel.h>
26 #include <ModuleBase_IViewer.h>
27 #include <ModuleBase_Tools.h>
28 #include <ModuleBase_Definitions.h>
29 #include <ModuleBase_IModule.h>
30 #include <ModuleBase_ViewerPrs.h>
31 #include <ModuleBase_IconFactory.h>
32 #include <ModuleBase_Events.h>
33 #include <ModuleBase_ChoiceCtrl.h>
34
35 #include <ModelAPI_Data.h>
36 #include <ModelAPI_Object.h>
37 #include <ModelAPI_AttributeSelectionList.h>
38 #include <ModelAPI_AttributeRefList.h>
39 #include <ModelAPI_AttributeRefAttrList.h>
40 #include <ModelAPI_Tools.h>
41 #include <ModelAPI_Events.h>
42
43 #include <Config_WidgetAPI.h>
44
45 #include <QGridLayout>
46 #include <QLabel>
47 #include <QListWidget>
48 #include <QObject>
49 #include <QString>
50 #include <QComboBox>
51 #include <QEvent>
52 #include <QAction>
53 #include <QApplication>
54 #include <QClipboard>
55 #include <QTimer>
56 #include <QMainWindow>
57
58 #include <memory>
59 #include <string>
60
61 const int ATTRIBUTE_SELECTION_INDEX_ROLE = Qt::UserRole + 1;
62
63 //#define DEBUG_UNDO_REDO
64
65 /**
66 * Customization of a List Widget to make it to be placed on full width of container
67 */
68 class CustomListWidget : public QListWidget
69 {
70 public:
71   /// Constructor
72   /// \param theParent a parent widget
73   CustomListWidget( QWidget* theParent )
74     : QListWidget( theParent )
75   {
76   }
77
78   /// Redefinition of virtual method
79   virtual QSize sizeHint() const
80   {
81     int aHeight = 2*QFontMetrics( font() ).height();
82     QSize aSize = QListWidget::sizeHint();
83     return QSize( aSize.width(), aHeight );
84   }
85
86   /// Redefinition of virtual method
87   virtual QSize minimumSizeHint() const
88   {
89     int aHeight = 4/*2*/*QFontMetrics( font() ).height();
90     QSize aSize = QListWidget::minimumSizeHint();
91     return QSize( aSize.width(), aHeight );
92   }
93
94 #ifndef WIN32
95 // The code is necessary only for Linux because
96 //it can not update viewport on widget resize
97 protected:
98   void resizeEvent(QResizeEvent* theEvent)
99   {
100     QListWidget::resizeEvent(theEvent);
101     QTimer::singleShot(5, viewport(), SLOT(repaint()));
102   }
103 #endif
104 };
105
106 #ifdef DEBUG_UNDO_REDO
107 void printHistoryInfo(const QString& theMethodName, int theCurrentHistoryIndex,
108   QList<QList<std::shared_ptr<ModuleBase_ViewerPrs> > > theSelectedHistoryValues)
109 {
110   QStringList aSizes;
111   for (int i = 0; i < theSelectedHistoryValues.size(); i++)
112     aSizes.append(QString::number(theSelectedHistoryValues[i].size()));
113
114   std::cout << theMethodName.toStdString()
115             << "  current = " << theCurrentHistoryIndex
116             << " size(history) =  " << theSelectedHistoryValues.size()
117             << " (" << aSizes.join(", ").toStdString() << ")"
118             << std::endl;
119 }
120 #endif
121
122
123 QStringList getIconsList(const QStringList& theNames)
124 {
125   QStringList aIcons;
126   foreach (QString aName, theNames) {
127     QString aUName = aName.toUpper();
128     if ((aUName == "VERTICES") || (aUName == "VERTEX"))
129       aIcons << ":pictures/vertex32.png";
130     else if ((aUName == "EDGES") || (aUName == "EDGE"))
131       aIcons << ":pictures/edge32.png";
132     else if ((aUName == "FACES") || (aUName == "FACE"))
133       aIcons << ":pictures/face32.png";
134     else if ((aUName == "SOLIDS") || (aUName == "SOLID"))
135       aIcons << ":pictures/solid32.png";
136   }
137   return aIcons;
138 }
139
140
141
142 ModuleBase_WidgetMultiSelector::ModuleBase_WidgetMultiSelector(QWidget* theParent,
143                                                                ModuleBase_IWorkshop* theWorkshop,
144                                                                const Config_WidgetAPI* theData)
145 : ModuleBase_WidgetSelector(theParent, theWorkshop, theData),
146   myIsSetSelectionBlocked(false), myCurrentHistoryIndex(-1)
147 {
148   std::string aPropertyTypes = theData->getProperty("type_choice");
149   QString aTypesStr = aPropertyTypes.c_str();
150   myShapeTypes = aTypesStr.split(' ', QString::SkipEmptyParts);
151   myIsUseChoice = theData->getBooleanAttribute("use_choice", false);
152
153   QGridLayout* aMainLay = new QGridLayout(this);
154   ModuleBase_Tools::adjustMargins(aMainLay);
155
156   QStringList aIconsList = getIconsList(myShapeTypes);
157   myTypeCtrl = new ModuleBase_ChoiceCtrl(this, myShapeTypes, aIconsList);
158   myTypeCtrl->setLabel(tr("Type"));
159   myTypeCtrl->setValue(0);
160   aMainLay->addWidget(myTypeCtrl, 0, 0, 1, 2);
161
162   // There is no sense to parameterize list of types while we can not parameterize selection mode
163   // if the xml definition contains one type, the controls to select a type should not be shown
164   if (myShapeTypes.size() <= 1 || !myIsUseChoice) {
165     myTypeCtrl->setVisible(false);
166   }
167
168   QString aLabelText = translate(theData->getProperty("label"));
169   QLabel* aListLabel = new QLabel(aLabelText, this);
170   aMainLay->addWidget(aListLabel, 1, 0);
171   // if the xml definition contains one type, an information label
172   // should be shown near to the latest
173   if (myShapeTypes.size() <= 1) {
174     QString aLabelIcon = QString::fromStdString(theData->widgetIcon());
175     if (!aLabelIcon.isEmpty()) {
176       QLabel* aSelectedLabel = new QLabel("", this);
177       aSelectedLabel->setPixmap(ModuleBase_IconFactory::loadPixmap(aLabelIcon));
178       aMainLay->addWidget(aSelectedLabel, 1, 1);
179     }
180     aMainLay->setColumnStretch(2, 1);
181   }
182
183   QString aToolTip = QString::fromStdString(theData->widgetTooltip());
184   myListControl = new CustomListWidget(this);
185   QString anObjName = QString::fromStdString(attributeID());
186   myListControl->setObjectName(anObjName);
187   myListControl->setToolTip(aToolTip);
188   myListControl->setSelectionMode(QAbstractItemView::ExtendedSelection);
189
190   aMainLay->addWidget(myListControl, 2, 0, 1, -1);
191   aMainLay->setRowStretch(2, 1);
192   //aMainLay->addWidget(new QLabel(this)); //FIXME(sbh)???
193   //aMainLay->setRowMinimumHeight(3, 20);
194   //this->setLayout(aMainLay);
195   connect(myTypeCtrl, SIGNAL(valueChanged(int)), this, SLOT(onSelectionTypeChanged()));
196
197   myCopyAction = ModuleBase_Tools::createAction(QIcon(":pictures/copy.png"), tr("Copy"),
198                           myWorkshop->desktop(), this, SLOT(onCopyItem()));
199   myCopyAction->setShortcut(QKeySequence::Copy);
200   myCopyAction->setEnabled(false);
201   myListControl->addAction(myCopyAction);
202
203   myDeleteAction = ModuleBase_Tools::createAction(QIcon(":pictures/delete.png"), tr("Delete"),
204                           myWorkshop->desktop(), this, SLOT(onDeleteItem()));
205   myDeleteAction->setEnabled(false);
206   myListControl->addAction(myDeleteAction);
207
208   myListControl->setContextMenuPolicy(Qt::ActionsContextMenu);
209   connect(myListControl, SIGNAL(itemSelectionChanged()), SLOT(onListSelection()));
210
211   myIsNeutralPointClear = theData->getBooleanAttribute("clear_in_neutral_point", true);
212 }
213
214 ModuleBase_WidgetMultiSelector::~ModuleBase_WidgetMultiSelector()
215 {
216 }
217
218 //********************************************************************
219 void ModuleBase_WidgetMultiSelector::activateCustom()
220 {
221   ModuleBase_WidgetSelector::activateCustom();
222
223   myWorkshop->module()->activateCustomPrs(myFeature,
224                             ModuleBase_IModule::CustomizeHighlightedObjects, true);
225   clearSelectedHistory();
226   myWorkshop->updateCommandStatus();
227 }
228
229 //********************************************************************
230 void ModuleBase_WidgetMultiSelector::deactivate()
231 {
232   ModuleBase_WidgetSelector::deactivate();
233
234   myWorkshop->module()->deactivateCustomPrs(ModuleBase_IModule::CustomizeHighlightedObjects, true);
235   clearSelectedHistory();
236   myWorkshop->updateCommandStatus();
237 }
238
239 //********************************************************************
240 bool ModuleBase_WidgetMultiSelector::storeValueCustom()
241 {
242   // the value is stored on the selection changed signal processing
243   // A rare case when plugin was not loaded.
244   if (!myFeature)
245     return false;
246
247   AttributePtr anAttribute = myFeature->data()->attribute(attributeID());
248   std::string aType = anAttribute->attributeType();
249   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
250     AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
251     aSelectionListAttr->setSelectionType(myTypeCtrl->textValue().toStdString());
252   }
253   return true;
254 }
255
256 //********************************************************************
257 bool ModuleBase_WidgetMultiSelector::restoreValueCustom()
258 {
259   // A rare case when plugin was not loaded.
260   if (!myFeature)
261     return false;
262
263   AttributePtr anAttribute = myFeature->data()->attribute(attributeID());
264   std::string aType = anAttribute->attributeType();
265   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
266     AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
267     // Restore shape type
268     std::string aSelectionType = aSelectionListAttr->selectionType().c_str();
269     if (!aSelectionType.empty())
270       setCurrentShapeType(ModuleBase_Tools::shapeType(aSelectionType.c_str()));
271   }
272   updateSelectionList();
273   return true;
274 }
275
276 //********************************************************************
277 bool ModuleBase_WidgetMultiSelector::setSelection(QList<ModuleBase_ViewerPrsPtr>& theValues,
278                                                   const bool theToValidate)
279 {
280   if (myIsSetSelectionBlocked)
281     return false;
282
283   AttributeSelectionListPtr aSelectionListAttr;
284   if (attribute()->attributeType() == ModelAPI_AttributeSelectionList::typeId())
285     aSelectionListAttr = std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(attribute());
286   if (aSelectionListAttr.get())
287     aSelectionListAttr->cashValues(true);
288
289   /// remove unused objects from the model attribute.
290   /// It should be performed before new attributes append.
291   bool isDone = removeUnusedAttributeObjects(theValues);
292
293   QList<ModuleBase_ViewerPrsPtr> anInvalidValues;
294   QList<ModuleBase_ViewerPrsPtr> anAttributeValues;
295   QList<ModuleBase_ViewerPrsPtr>::const_iterator anIt = theValues.begin(), aLast = theValues.end();
296   for (; anIt != aLast; anIt++) {
297     ModuleBase_ViewerPrsPtr aValue = *anIt;
298     // do not validate and append to attribute selection presentation if it exists in the attribute
299     ObjectPtr anObject;
300     GeomShapePtr aShape;
301     getGeomSelection(aValue, anObject, aShape);
302     if (ModuleBase_Tools::hasObject(attribute(), anObject, aShape, myWorkshop, myIsInValidate)) {
303       anAttributeValues.append(aValue);
304       continue;
305     }
306     if (theToValidate && !isValidInFilters(aValue))
307       anInvalidValues.append(aValue);
308   }
309   bool aHasInvalidValues = anInvalidValues.size() > 0;
310
311   for (anIt = theValues.begin(); anIt != aLast; anIt++) {
312     ModuleBase_ViewerPrsPtr aValue = *anIt;
313     bool aProcessed = false;
314     if ((aHasInvalidValues && anInvalidValues.contains(aValue)) ||
315         anAttributeValues.contains(aValue))
316       continue;
317     aProcessed = setSelectionCustom(aValue); /// it is not optimal as hasObject() is already checked
318     // if there is at least one set, the result is true
319     isDone = isDone || aProcessed;
320   }
321   // updateObject - to update/redisplay feature
322   // it is commented in order to perfom it outside the method
323   //if (isDone) {
324     //updateObject(myFeature);
325     // this emit is necessary to call store/restore method an restore type of selection
326     //emit valuesChanged();
327   //}
328
329   if (aSelectionListAttr.get())
330     aSelectionListAttr->cashValues(false);
331
332   theValues.clear();
333   if (!anInvalidValues.empty())
334     theValues.append(anInvalidValues);
335
336   if (isDone) // may be the feature's result is not displayed, but attributes should be
337     myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeArguments,
338                              true);/// hope that something is redisplayed by object updated
339
340   return isDone;
341 }
342
343 //********************************************************************
344 void ModuleBase_WidgetMultiSelector::getHighlighted(QList<ModuleBase_ViewerPrsPtr>& theValues)
345 {
346   std::set<int> anAttributeIds;
347   getSelectedAttributeIndices(anAttributeIds);
348   if (!anAttributeIds.empty())
349     convertIndicesToViewerSelection(anAttributeIds, theValues);
350 }
351
352 //********************************************************************
353 bool ModuleBase_WidgetMultiSelector::canProcessAction(ModuleBase_ActionType theActionType,
354                                                       bool& isActionEnabled)
355 {
356   isActionEnabled = false;
357   bool aCanProcess = false;
358   switch (theActionType) {
359     case ActionUndo:
360     case ActionRedo: {
361       aCanProcess = true;
362       isActionEnabled = theActionType == ActionUndo ? myCurrentHistoryIndex > 0
363           : (mySelectedHistoryValues.size() > 0 &&
364              myCurrentHistoryIndex < mySelectedHistoryValues.size() - 1);
365     }
366     break;
367     default:
368     break;
369   }
370   return aCanProcess;
371 }
372
373 //********************************************************************
374 bool ModuleBase_WidgetMultiSelector::processAction(ModuleBase_ActionType theActionType)
375 {
376   switch (theActionType) {
377     case ActionUndo:
378     case ActionRedo: {
379       if (theActionType == ActionUndo)
380         myCurrentHistoryIndex--;
381       else
382         myCurrentHistoryIndex++;
383       QList<ModuleBase_ViewerPrsPtr> aSelected = mySelectedHistoryValues[myCurrentHistoryIndex];
384       // equal vertices should not be used here
385       ModuleBase_ISelection::filterSelectionOnEqualPoints(aSelected);
386       bool isDone = setSelection(aSelected,
387                                  false /*need not validate because values already was in list*/);
388       updateOnSelectionChanged(isDone);
389
390       myWorkshop->updateCommandStatus();
391 #ifdef DEBUG_UNDO_REDO
392       printHistoryInfo(QString("processAction %1").arg(theActionType == ActionUndo ? "Undo"
393         : "Redo"), myCurrentHistoryIndex, mySelectedHistoryValues);
394 #endif
395       return true;
396     }
397     default:
398       return ModuleBase_ModelWidget::processAction(theActionType);
399   }
400 }
401
402 //********************************************************************
403 bool ModuleBase_WidgetMultiSelector::activateSelectionAndFilters(bool toActivate)
404 {
405   myWorkshop->updateCommandStatus(); // update enable state of Undo/Redo application actions
406   return ModuleBase_WidgetSelector::activateSelectionAndFilters(toActivate);
407 }
408
409 //********************************************************************
410 bool ModuleBase_WidgetMultiSelector::isValidSelectionCustom(const ModuleBase_ViewerPrsPtr& thePrs)
411 {
412   bool aValid = ModuleBase_WidgetSelector::isValidSelectionCustom(thePrs);
413   if (aValid) {
414     ResultPtr aResult = myWorkshop->selection()->getResult(thePrs);
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 aIndexes = myListControl->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   int aRows = myListControl->model()->rowCount();
484   if (aRows > 0) {
485     foreach(QModelIndex aIndex, aIndexes) {
486       if (aIndex.row() < aRows)
487         myListControl->selectionModel()->select(aIndex, QItemSelectionModel::Select);
488       else {
489         QModelIndex aIdx = myListControl->model()->index(aRows - 1, 0);
490         myListControl->selectionModel()->select(aIdx, QItemSelectionModel::Select);
491       }
492     }
493   }
494   appendSelectionInHistory();
495   return aDone;
496 }
497
498 //********************************************************************
499 QList<QWidget*> ModuleBase_WidgetMultiSelector::getControls() const
500 {
501   QList<QWidget*> result;
502   result << myListControl;
503   return result;
504 }
505
506 //********************************************************************
507 void ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()
508 {
509   activateSelectionAndFilters(true);
510
511   if (!myFeature)
512     return;
513   /// store the selected type
514   AttributePtr anAttribute = myFeature->data()->attribute(attributeID());
515   std::string aType = anAttribute->attributeType();
516   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
517     AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
518     aSelectionListAttr->setSelectionType(myTypeCtrl->textValue().toStdString());
519   }
520
521   // clear attribute values
522   DataPtr aData = myFeature->data();
523   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
524     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
525     aSelectionListAttr->clear();
526   }
527   else if (aType == ModelAPI_AttributeRefList::typeId()) {
528     AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
529     aRefListAttr->clear();
530   }
531   else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
532     AttributeRefAttrListPtr aRefAttrListAttr = aData->refattrlist(attributeID());
533     aRefAttrListAttr->clear();
534   }
535
536   // update object is necessary to flush update signal. It leads to objects references map update
537   // and the operation presentation will not contain deleted items visualized as parameters of
538   // the feature.
539   updateObject(myFeature);
540   restoreValue();
541   myWorkshop->setSelected(getAttributeSelection());
542   // may be the feature's result is not displayed, but attributes should be
543   myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeArguments,
544                             true); /// hope that something is redisplayed by object updated
545   // clear history should follow after set selected to do not increase history by setSelected
546   clearSelectedHistory();
547
548   if (myWorkshop->propertyPanel()->activeWidget() != this)
549     myWorkshop->propertyPanel()->activateWidget(this);
550 }
551
552 //********************************************************************
553 void ModuleBase_WidgetMultiSelector::onSelectionChanged()
554 {
555   if (!myIsNeutralPointClear) {
556     QList<ModuleBase_ViewerPrsPtr> aSelected = getFilteredSelected();
557     // do not clear selected object
558     if (aSelected.size() == 0) {
559       if (!getAttributeSelection().empty()) {
560         // Restore selection in the viewer by the attribute selection list
561         // it should be postponed to exit from the selectionChanged processing
562         static Events_ID anEvent = Events_Loop::eventByName(EVENT_UPDATE_BY_WIDGET_SELECTION);
563         ModelAPI_EventCreator::get()->sendUpdated(myFeature, anEvent);
564         Events_Loop::loop()->flush(anEvent);
565         return;
566       }
567     }
568   }
569   appendFirstSelectionInHistory();
570   ModuleBase_WidgetSelector::onSelectionChanged();
571   appendSelectionInHistory();
572 }
573
574 void ModuleBase_WidgetMultiSelector::appendFirstSelectionInHistory()
575 {
576   if (mySelectedHistoryValues.empty()) {
577     myCurrentHistoryIndex++;
578     mySelectedHistoryValues.append(getAttributeSelection());
579
580 #ifdef DEBUG_UNDO_REDO
581     printHistoryInfo("appendSelectionInHistory", myCurrentHistoryIndex, mySelectedHistoryValues);
582 #endif
583   }
584 }
585
586 void ModuleBase_WidgetMultiSelector::appendSelectionInHistory()
587 {
588   while (myCurrentHistoryIndex != mySelectedHistoryValues.count() - 1)
589     mySelectedHistoryValues.removeLast();
590
591   QList<ModuleBase_ViewerPrsPtr> aSelected = getFilteredSelected();
592   myCurrentHistoryIndex++;
593   mySelectedHistoryValues.append(aSelected);
594   myWorkshop->updateCommandStatus();
595
596 #ifdef DEBUG_UNDO_REDO
597   printHistoryInfo("appendSelectionInHistory", myCurrentHistoryIndex, mySelectedHistoryValues);
598 #endif
599 }
600
601 void ModuleBase_WidgetMultiSelector::clearSelectedHistory()
602 {
603   mySelectedHistoryValues.clear();
604   myCurrentHistoryIndex = -1;
605   myWorkshop->updateCommandStatus();
606
607 #ifdef DEBUG_UNDO_REDO
608   printHistoryInfo("clearSelectedHistory", myCurrentHistoryIndex, mySelectedHistoryValues);
609 #endif
610 }
611
612 void ModuleBase_WidgetMultiSelector::updateFocus()
613 {
614   // Set focus to List control in order to make possible
615   // to use Tab key for transfer the focus to next widgets
616   ModuleBase_Tools::setFocus(myListControl,
617                              "ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()");
618 }
619
620 //********************************************************************
621 void ModuleBase_WidgetMultiSelector::updateSelectionName()
622 {
623 }
624
625 //********************************************************************
626 void ModuleBase_WidgetMultiSelector::updateOnSelectionChanged(const bool theDone)
627 {
628   if (myIsSetSelectionBlocked)
629     return;
630   ModuleBase_WidgetSelector::updateOnSelectionChanged(theDone);
631
632   // according to #2154 we need to update OB selection when selection in the viewer happens
633   // it is important to flush sinchronize selection signal after flush of Update/Create/Delete.
634   // because we need that Object Browser has been already updated when synchronize happens.
635
636   // Restore selection in the viewer by the attribute selection list
637   // it is possible that diring selection attribute filling, selection in Object Browser
638   // is changed(some items were removed/added) and as result, selection in the viewer
639   // differs from the selection come to this method. By next rows, we restore selection
640   // in the viewer according to content of selection attribute. Case is Edge selection in Group
641   myIsSetSelectionBlocked = true;
642   static Events_ID anEvent = Events_Loop::eventByName(EVENT_UPDATE_BY_WIDGET_SELECTION);
643   ModelAPI_EventCreator::get()->sendUpdated(myFeature, anEvent);
644   Events_Loop::loop()->flush(anEvent);
645   myIsSetSelectionBlocked = false;
646 }
647
648 //********************************************************************
649 QIntList ModuleBase_WidgetMultiSelector::shapeTypes() const
650 {
651   QIntList aShapeTypes;
652
653   if (myShapeTypes.length() > 1 && myIsUseChoice) {
654     aShapeTypes.append(ModuleBase_Tools::shapeType(myTypeCtrl->textValue()));
655   }
656   else {
657     foreach (QString aType, myShapeTypes) {
658       aShapeTypes.append(ModuleBase_Tools::shapeType(aType));
659     }
660   }
661   return aShapeTypes;
662 }
663
664 //********************************************************************
665 void ModuleBase_WidgetMultiSelector::setCurrentShapeType(const int theShapeType)
666 {
667   QString aShapeTypeName;
668
669   int idx = 0;
670   foreach (QString aShapeTypeName, myShapeTypes) {
671     int aRefType = ModuleBase_Tools::shapeType(aShapeTypeName);
672     if(aRefType == theShapeType && idx != myTypeCtrl->value()) {
673       bool aWasActivated = activateSelectionAndFilters(false);
674       bool isBlocked = myTypeCtrl->blockSignals(true);
675       myTypeCtrl->setValue(idx);
676       myTypeCtrl->blockSignals(isBlocked);
677       if (aWasActivated)
678         activateSelectionAndFilters(true);
679       break;
680     }
681     idx++;
682   }
683 }
684
685 QList<ModuleBase_ViewerPrsPtr> ModuleBase_WidgetMultiSelector::getAttributeSelection() const
686 {
687   QList<ModuleBase_ViewerPrsPtr> aSelected;
688   convertIndicesToViewerSelection(std::set<int>(), aSelected);
689   return aSelected;
690 }
691
692 //********************************************************************
693 void ModuleBase_WidgetMultiSelector::updateSelectionList()
694 {
695   myListControl->clear();
696
697   DataPtr aData = myFeature->data();
698   AttributePtr anAttribute = aData->attribute(attributeID());
699   std::string aType = anAttribute->attributeType();
700   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
701     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
702     for (int i = 0; i < aSelectionListAttr->size(); i++) {
703       AttributeSelectionPtr aAttr = aSelectionListAttr->value(i);
704       QListWidgetItem* anItem = new QListWidgetItem(aAttr->namingName().c_str(), myListControl);
705       anItem->setData(ATTRIBUTE_SELECTION_INDEX_ROLE, i);
706       myListControl->addItem(anItem);
707     }
708   }
709   else if (aType == ModelAPI_AttributeRefList::typeId()) {
710     AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
711     for (int i = 0; i < aRefListAttr->size(); i++) {
712       ObjectPtr anObject = aRefListAttr->object(i);
713       if (anObject.get()) {
714         QListWidgetItem* anItem = new QListWidgetItem(anObject->data()->name().c_str(),
715                                                       myListControl);
716         anItem->setData(ATTRIBUTE_SELECTION_INDEX_ROLE, i);
717         myListControl->addItem(anItem);
718       }
719     }
720   }
721   else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
722     AttributeRefAttrListPtr aRefAttrListAttr = aData->refattrlist(attributeID());
723     for (int i = 0; i < aRefAttrListAttr->size(); i++) {
724       AttributePtr anAttribute = aRefAttrListAttr->attribute(i);
725       QString aName;
726       if (anAttribute.get()) {
727         std::string anAttrName = generateName(anAttribute, myWorkshop);
728         aName = QString::fromStdString(anAttrName);
729       }
730       else {
731         ObjectPtr anObject = aRefAttrListAttr->object(i);
732         if (anObject.get()) {
733           aName = anObject->data()->name().c_str();
734         }
735       }
736       QListWidgetItem* anItem = new QListWidgetItem(aName, myListControl);
737       anItem->setData(ATTRIBUTE_SELECTION_INDEX_ROLE, i);
738       myListControl->addItem(anItem);
739     }
740   }
741
742   // We have to call repaint because sometimes the List control is not updated
743   myListControl->repaint();
744 }
745
746 //********************************************************************
747 std::string ModuleBase_WidgetMultiSelector::validatorType(const QString& theType) const
748 {
749   std::string aType;
750
751   if (theType == "Vertices")
752     aType = "vertex";
753   else if (theType == "Edges")
754     aType = "edge";
755   else if (theType == "Faces")
756     aType = "face";
757   else if (theType == "Solids")
758     aType = "solid";
759
760   return aType;
761 }
762
763 //********************************************************************
764 void ModuleBase_WidgetMultiSelector::clearSelection()
765 {
766   bool isClearInNeutralPoint = myIsNeutralPointClear;
767   myIsNeutralPointClear = true;
768
769   QList<ModuleBase_ViewerPrsPtr> anEmptyList;
770   // This method will call Selection changed event which will call onSelectionChanged
771   // To clear mySelection, myListControl and storeValue()
772   // So, we don't need to call it
773   myWorkshop->setSelected(anEmptyList);
774
775   myIsNeutralPointClear = isClearInNeutralPoint;
776 }
777
778 //********************************************************************
779 void ModuleBase_WidgetMultiSelector::onCopyItem()
780 {
781   QList<QListWidgetItem*> aItems = myListControl->selectedItems();
782   QString aRes;
783   foreach(QListWidgetItem* aItem, aItems) {
784     if (!aRes.isEmpty())
785       aRes += "\n";
786     aRes += aItem->text();
787   }
788   if (!aRes.isEmpty()) {
789     QClipboard *clipboard = QApplication::clipboard();
790     clipboard->setText(aRes);
791   }
792 }
793
794 //********************************************************************
795 void ModuleBase_WidgetMultiSelector::onDeleteItem()
796 {
797   processDelete();
798 }
799
800 //********************************************************************
801 void ModuleBase_WidgetMultiSelector::onListSelection()
802 {
803   QList<QListWidgetItem*> aItems = myListControl->selectedItems();
804   myCopyAction->setEnabled(!aItems.isEmpty());
805   myDeleteAction->setEnabled(!aItems.isEmpty());
806
807   myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeHighlightedObjects,
808                                         true);
809 }
810
811 //********************************************************************
812 void ModuleBase_WidgetMultiSelector::getSelectedAttributeIndices(std::set<int>& theAttributeIds)
813 {
814   QList<QListWidgetItem*> aItems = myListControl->selectedItems();
815   foreach(QListWidgetItem* anItem, aItems) {
816     int anIndex = anItem->data(ATTRIBUTE_SELECTION_INDEX_ROLE).toInt();
817     if (theAttributeIds.find(anIndex) == theAttributeIds.end())
818       theAttributeIds.insert(anIndex);
819   }
820 }
821
822 void ModuleBase_WidgetMultiSelector::convertIndicesToViewerSelection(std::set<int> theAttributeIds,
823                                                    QList<ModuleBase_ViewerPrsPtr>& theValues) const
824 {
825   if(myFeature.get() == NULL)
826     return;
827
828   DataPtr aData = myFeature->data();
829   AttributePtr anAttribute = aData->attribute(attributeID());
830   std::string aType = anAttribute->attributeType();
831   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
832     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
833     for (int i = 0; i < aSelectionListAttr->size(); i++) {
834       // filter by attribute indices only if the container is not empty otherwise return all items
835       if (!theAttributeIds.empty() && theAttributeIds.find(i) == theAttributeIds.end())
836         continue;
837       AttributeSelectionPtr anAttr = aSelectionListAttr->value(i);
838       ResultPtr anObject = anAttr->context();
839       if (anObject.get())
840         theValues.append(std::shared_ptr<ModuleBase_ViewerPrs>(
841                new ModuleBase_ViewerPrs(anObject, anAttr->value(), NULL)));
842     }
843   }
844   else if (aType == ModelAPI_AttributeRefList::typeId()) {
845     AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
846     for (int i = 0; i < aRefListAttr->size(); i++) {
847       // filter by attribute indices only if the container is not empty otherwise return all items
848       if (!theAttributeIds.empty() && theAttributeIds.find(i) == theAttributeIds.end())
849         continue;
850       ObjectPtr anObject = aRefListAttr->object(i);
851       if (anObject.get()) {
852         theValues.append(std::shared_ptr<ModuleBase_ViewerPrs>(
853                new ModuleBase_ViewerPrs(anObject, GeomShapePtr(), NULL)));
854       }
855     }
856   }
857   else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
858     AttributeRefAttrListPtr aRefAttrListAttr = aData->refattrlist(attributeID());
859     for (int i = 0; i < aRefAttrListAttr->size(); i++) {
860       // filter by attribute indices only if the container is not empty otherwise return all items
861       if (!theAttributeIds.empty() && theAttributeIds.find(i) == theAttributeIds.end())
862         continue;
863       ObjectPtr anObject = aRefAttrListAttr->object(i);
864       if (!anObject.get())
865         continue;
866       TopoDS_Shape aShape;
867       AttributePtr anAttribute = aRefAttrListAttr->attribute(i);
868       if (anAttribute.get()) {
869         GeomShapePtr aGeomShape = ModuleBase_Tools::getShape(anAttribute, myWorkshop);
870         theValues.append(std::shared_ptr<ModuleBase_ViewerPrs>(
871                new ModuleBase_ViewerPrs(anObject, aGeomShape, NULL)));
872       }
873     }
874   }
875 }
876
877 bool ModuleBase_WidgetMultiSelector::removeUnusedAttributeObjects
878                                                  (QList<ModuleBase_ViewerPrsPtr>& theValues)
879 {
880   bool isDone = false;
881
882   std::map<ObjectPtr, std::set<GeomShapePtr> > aGeomSelection = convertSelection(theValues);
883   DataPtr aData = myFeature->data();
884   AttributePtr anAttribute = aData->attribute(attributeID());
885   std::string aType = anAttribute->attributeType();
886   std::set<GeomShapePtr> aShapes;
887   std::set<int> anIndicesToBeRemoved;
888   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
889     // iteration through data model to find not selected elements to remove them
890     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
891     for (int i = 0; i < aSelectionListAttr->size(); i++) {
892       AttributeSelectionPtr anAttr = aSelectionListAttr->value(i);
893       bool aFound = findInSelection(anAttr->context(), anAttr->value(), aGeomSelection,
894                                     myWorkshop);
895       if (!aFound)
896         anIndicesToBeRemoved.insert(i);
897     }
898     isDone = anIndicesToBeRemoved.size() > 0;
899     aSelectionListAttr->remove(anIndicesToBeRemoved);
900   }
901   else if (aType == ModelAPI_AttributeRefList::typeId()) {
902     AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
903     for (int i = 0; i < aRefListAttr->size(); i++) {
904       ObjectPtr anObject = aRefListAttr->object(i);
905       if (anObject.get()) {
906         bool aFound = findInSelection(anObject, GeomShapePtr(), aGeomSelection,
907                                       myWorkshop);
908         if (!aFound)
909           anIndicesToBeRemoved.insert(i);
910       }
911     }
912     isDone = anIndicesToBeRemoved.size() > 0;
913     aRefListAttr->remove(anIndicesToBeRemoved);
914   }
915   else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
916     std::set<AttributePtr> anAttributes;
917     QList<ModuleBase_ViewerPrsPtr>::const_iterator
918       anIt = theValues.begin(), aLast = theValues.end();
919     ObjectPtr anObject;
920     GeomShapePtr aShape;
921     for (; anIt != aLast; anIt++) {
922       ModuleBase_ViewerPrsPtr aPrs = *anIt;
923       getGeomSelection(aPrs, anObject, aShape);
924       AttributePtr anAttr = myWorkshop->module()->findAttribute(anObject, aShape);
925       if (anAttr.get() && anAttributes.find(anAttr) == anAttributes.end())
926         anAttributes.insert(anAttr);
927     }
928
929     AttributeRefAttrListPtr aRefAttrListAttr = aData->refattrlist(attributeID());
930     for (int i = 0; i < aRefAttrListAttr->size(); i++) {
931       bool aFound = false;
932       if (aRefAttrListAttr->isAttribute(i)) {
933         AttributePtr anAttribute = aRefAttrListAttr->attribute(i);
934         aFound = anAttributes.find(anAttribute) != anAttributes.end();
935       }
936       else {
937         aFound = findInSelection(aRefAttrListAttr->object(i), GeomShapePtr(), aGeomSelection,
938                                  myWorkshop);
939       }
940       if (!aFound)
941         anIndicesToBeRemoved.insert(i);
942     }
943     isDone = anIndicesToBeRemoved.size() > 0;
944     aRefAttrListAttr->remove(anIndicesToBeRemoved);
945   }
946
947   return isDone;
948 }
949
950 std::map<ObjectPtr, std::set<GeomShapePtr> > ModuleBase_WidgetMultiSelector::convertSelection
951                                                      (QList<ModuleBase_ViewerPrsPtr>& theValues)
952 {
953   // convert prs list to objects map
954   std::map<ObjectPtr, std::set<GeomShapePtr> > aGeomSelection;
955   std::set<GeomShapePtr> aShapes;
956   QList<ModuleBase_ViewerPrsPtr>::const_iterator anIt = theValues.begin(), aLast = theValues.end();
957   ObjectPtr anObject;
958   GeomShapePtr aShape;
959   GeomShapePtr anEmptyShape(new GeomAPI_Shape());
960   for (; anIt != aLast; anIt++) {
961     ModuleBase_ViewerPrsPtr aPrs = *anIt;
962     getGeomSelection(aPrs, anObject, aShape);
963     aShapes.clear();
964     if (aGeomSelection.find(anObject) != aGeomSelection.end()) // found
965       aShapes = aGeomSelection[anObject];
966     // we need to know if there was an empty shape in selection for the object
967     if (!aShape.get())
968       aShape = anEmptyShape;
969     if (aShape.get() && aShapes.find(aShape) == aShapes.end()) // not found
970       aShapes.insert(aShape);
971     aGeomSelection[anObject] = aShapes;
972   }
973   return aGeomSelection;
974 }
975
976 bool ModuleBase_WidgetMultiSelector::findInSelection(const ObjectPtr& theObject,
977                               GeomShapePtr theShape,
978                               const std::map<ObjectPtr, std::set<GeomShapePtr> >& theGeomSelection,
979                               ModuleBase_IWorkshop* theWorkshop)
980 {
981   // issue #2154: we should not remove from list objects hidden in the viewer if selection
982   // was done with SHIFT button
983   if (theWorkshop->hasSHIFTPressed() && !theObject->isDisplayed())
984     return true;
985
986   bool aFound = false;
987   GeomShapePtr anEmptyShape(new GeomAPI_Shape());
988   if (theShape.get()) { // treat shape equal to context as null: 2219, keep order of shapes in list
989     const ResultPtr aContext = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
990     if (aContext.get() && aContext->shape()->isEqual(theShape))
991       theShape.reset();
992   }
993   GeomShapePtr aShape = theShape.get() ? theShape : anEmptyShape;
994   if (theGeomSelection.find(theObject) != theGeomSelection.end()) {// found
995     const std::set<GeomShapePtr>& aShapes = theGeomSelection.at(theObject);
996     std::set<GeomShapePtr>::const_iterator anIt = aShapes.begin(), aLast = aShapes.end();
997     for (; anIt != aLast && !aFound; anIt++) {
998       GeomShapePtr aCShape = *anIt;
999       if (aCShape.get())
1000         aFound = aCShape->isSame(aShape);
1001     }
1002   }
1003   return aFound;
1004 }