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