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