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