]> SALOME platform Git repositories - modules/shaper.git/blob - src/ModuleBase/ModuleBase_WidgetMultiSelector.cpp
Salome HOME
Issue #2352 - Undo list is disabled when change focus from multi-selection to ordinar...
[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   updateSelectionModesAndFilters(true);
439   myWorkshop->selectionActivate()->updateSelectionModes();
440
441   if (!myFeature)
442     return;
443   /// store the selected type
444   AttributePtr anAttribute = myFeature->data()->attribute(attributeID());
445   std::string aType = anAttribute->attributeType();
446   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
447     AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
448     aSelectionListAttr->setSelectionType(myTypeCombo->currentText().toStdString());
449   }
450
451   // clear attribute values
452   DataPtr aData = myFeature->data();
453   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
454     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
455     aSelectionListAttr->clear();
456   }
457   else if (aType == ModelAPI_AttributeRefList::typeId()) {
458     AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
459     aRefListAttr->clear();
460   }
461   else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
462     AttributeRefAttrListPtr aRefAttrListAttr = aData->refattrlist(attributeID());
463     aRefAttrListAttr->clear();
464   }
465
466   // update object is necessary to flush update signal. It leads to objects references map update
467   // and the operation presentation will not contain deleted items visualized as parameters of
468   // the feature.
469   updateObject(myFeature);
470   restoreValue();
471   myWorkshop->setSelected(getAttributeSelection());
472   // may be the feature's result is not displayed, but attributes should be
473   myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeArguments,
474                             true); /// hope that something is redisplayed by object updated
475   // clear history should follow after set selected to do not increase history by setSelected
476   clearSelectedHistory();
477 }
478
479 //********************************************************************
480 bool ModuleBase_WidgetMultiSelector::processSelection()
481 {
482   if (!myIsNeutralPointClear) {
483     QList<ModuleBase_ViewerPrsPtr> aSelected = getFilteredSelected();
484     // do not clear selected object
485     if (aSelected.size() == 0) {
486       if (!getAttributeSelection().empty()) {
487         // Restore selection in the viewer by the attribute selection list
488         // it should be postponed to exit from the selectionChanged processing
489         static Events_ID anEvent = Events_Loop::eventByName(EVENT_UPDATE_BY_WIDGET_SELECTION);
490         ModelAPI_EventCreator::get()->sendUpdated(myFeature, anEvent);
491         Events_Loop::loop()->flush(anEvent);
492         return true;
493       }
494     }
495   }
496   appendFirstSelectionInHistory();
497   bool aDone = ModuleBase_WidgetSelector::processSelection();
498   appendSelectionInHistory();
499   return aDone;
500 }
501
502 void ModuleBase_WidgetMultiSelector::appendFirstSelectionInHistory()
503 {
504   if (mySelectedHistoryValues.empty()) {
505     myCurrentHistoryIndex++;
506     mySelectedHistoryValues.append(getAttributeSelection());
507
508 #ifdef DEBUG_UNDO_REDO
509     printHistoryInfo("appendSelectionInHistory", myCurrentHistoryIndex, mySelectedHistoryValues);
510 #endif
511   }
512 }
513
514 void ModuleBase_WidgetMultiSelector::appendSelectionInHistory()
515 {
516   while (myCurrentHistoryIndex != mySelectedHistoryValues.count() - 1)
517     mySelectedHistoryValues.removeLast();
518
519   QList<ModuleBase_ViewerPrsPtr> aSelected = getFilteredSelected();
520   myCurrentHistoryIndex++;
521   mySelectedHistoryValues.append(aSelected);
522   myWorkshop->updateCommandStatus();
523
524 #ifdef DEBUG_UNDO_REDO
525   printHistoryInfo("appendSelectionInHistory", myCurrentHistoryIndex, mySelectedHistoryValues);
526 #endif
527 }
528
529 void ModuleBase_WidgetMultiSelector::clearSelectedHistory()
530 {
531   mySelectedHistoryValues.clear();
532   myCurrentHistoryIndex = -1;
533   myWorkshop->updateCommandStatus();
534
535 #ifdef DEBUG_UNDO_REDO
536   printHistoryInfo("clearSelectedHistory", myCurrentHistoryIndex, mySelectedHistoryValues);
537 #endif
538 }
539
540 void ModuleBase_WidgetMultiSelector::updateFocus()
541 {
542   // Set focus to List control in order to make possible
543   // to use Tab key for transfer the focus to next widgets
544   ModuleBase_Tools::setFocus(myListView->getControl(),
545                              "ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()");
546 }
547
548 //********************************************************************
549 void ModuleBase_WidgetMultiSelector::updateSelectionName()
550 {
551 }
552
553 //********************************************************************
554 void ModuleBase_WidgetMultiSelector::updateOnSelectionChanged(const bool theDone)
555 {
556   if (myIsSetSelectionBlocked)
557     return;
558   ModuleBase_WidgetSelector::updateOnSelectionChanged(theDone);
559
560   // according to #2154 we need to update OB selection when selection in the viewer happens
561   // it is important to flush sinchronize selection signal after flush of Update/Create/Delete.
562   // because we need that Object Browser has been already updated when synchronize happens.
563
564   // Restore selection in the viewer by the attribute selection list
565   // it is possible that diring selection attribute filling, selection in Object Browser
566   // is changed(some items were removed/added) and as result, selection in the viewer
567   // differs from the selection come to this method. By next rows, we restore selection
568   // in the viewer according to content of selection attribute. Case is Edge selection in Group
569   myIsSetSelectionBlocked = true;
570   static Events_ID anEvent = Events_Loop::eventByName(EVENT_UPDATE_BY_WIDGET_SELECTION);
571   ModelAPI_EventCreator::get()->sendUpdated(myFeature, anEvent);
572   Events_Loop::loop()->flush(anEvent);
573   myIsSetSelectionBlocked = false;
574 }
575
576 //********************************************************************
577 QIntList ModuleBase_WidgetMultiSelector::shapeTypes() const
578 {
579   QIntList aShapeTypes;
580
581   if (myTypeCombo->count() > 1 && myIsUseChoice) {
582     aShapeTypes.append(ModuleBase_Tools::shapeType(myTypeCombo->currentText()));
583   }
584   else {
585     for (int i = 0, aCount = myTypeCombo->count(); i < aCount; i++)
586       aShapeTypes.append(ModuleBase_Tools::shapeType(myTypeCombo->itemText(i)));
587   }
588   return aShapeTypes;
589 }
590
591 //********************************************************************
592 void ModuleBase_WidgetMultiSelector::setCurrentShapeType(const int theShapeType)
593 {
594   QString aShapeTypeName;
595
596   for (int idx = 0; idx < myTypeCombo->count(); ++idx) {
597     aShapeTypeName = myTypeCombo->itemText(idx);
598     int aRefType = ModuleBase_Tools::shapeType(aShapeTypeName);
599     if(aRefType == theShapeType && idx != myTypeCombo->currentIndex()) {
600       updateSelectionModesAndFilters(false);
601       bool isBlocked = myTypeCombo->blockSignals(true);
602       myTypeCombo->setCurrentIndex(idx);
603       myTypeCombo->blockSignals(isBlocked);
604       updateSelectionModesAndFilters(true);
605       break;
606     }
607   }
608 }
609
610 QList<ModuleBase_ViewerPrsPtr> ModuleBase_WidgetMultiSelector::getAttributeSelection() const
611 {
612   QList<ModuleBase_ViewerPrsPtr> aSelected;
613   convertIndicesToViewerSelection(std::set<int>(), aSelected);
614   return aSelected;
615 }
616
617 //********************************************************************
618 void ModuleBase_WidgetMultiSelector::updateSelectionList()
619 {
620   myListView->getControl()->clear();
621
622   DataPtr aData = myFeature->data();
623   AttributePtr anAttribute = aData->attribute(attributeID());
624   std::string aType = anAttribute->attributeType();
625   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
626     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
627     for (int i = 0; i < aSelectionListAttr->size(); i++) {
628       AttributeSelectionPtr aAttr = aSelectionListAttr->value(i);
629       myListView->addItem(aAttr->namingName().c_str(), i);
630     }
631   }
632   else if (aType == ModelAPI_AttributeRefList::typeId()) {
633     AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
634     for (int i = 0; i < aRefListAttr->size(); i++) {
635       ObjectPtr anObject = aRefListAttr->object(i);
636       if (anObject.get()) {
637         myListView->addItem(anObject->data()->name().c_str(), i);
638       }
639     }
640   }
641   else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
642     AttributeRefAttrListPtr aRefAttrListAttr = aData->refattrlist(attributeID());
643     for (int i = 0; i < aRefAttrListAttr->size(); i++) {
644       AttributePtr anAttribute = aRefAttrListAttr->attribute(i);
645       QString aName;
646       if (anAttribute.get()) {
647         std::string anAttrName = generateName(anAttribute, myWorkshop);
648         aName = QString::fromStdString(anAttrName);
649       }
650       else {
651         ObjectPtr anObject = aRefAttrListAttr->object(i);
652         if (anObject.get()) {
653           aName = anObject->data()->name().c_str();
654         }
655       }
656       myListView->addItem(aName, i);
657     }
658   }
659
660   // We have to call repaint because sometimes the List control is not updated
661   myListView->getControl()->repaint();
662 }
663
664 //********************************************************************
665 std::string ModuleBase_WidgetMultiSelector::validatorType(const QString& theType) const
666 {
667   std::string aType;
668
669   if (theType == "Vertices")
670     aType = "vertex";
671   else if (theType == "Edges")
672     aType = "edge";
673   else if (theType == "Faces")
674     aType = "face";
675   else if (theType == "Solids")
676     aType = "solid";
677
678   return aType;
679 }
680
681 //********************************************************************
682 void ModuleBase_WidgetMultiSelector::clearSelection()
683 {
684   bool isClearInNeutralPoint = myIsNeutralPointClear;
685   myIsNeutralPointClear = true;
686
687   QList<ModuleBase_ViewerPrsPtr> anEmptyList;
688   // This method will call Selection changed event which will call onSelectionChanged
689   // To clear mySelection, myListView and storeValue()
690   // So, we don't need to call it
691   myWorkshop->setSelected(anEmptyList);
692
693   myIsNeutralPointClear = isClearInNeutralPoint;
694 }
695
696 //********************************************************************
697 void ModuleBase_WidgetMultiSelector::onDeleteItem()
698 {
699   processDelete();
700 }
701
702 //********************************************************************
703 void ModuleBase_WidgetMultiSelector::onListSelection()
704 {
705   myWorkshop->module()->customizeObject(myFeature, ModuleBase_IModule::CustomizeHighlightedObjects,
706                                         true);
707 }
708
709 //********************************************************************
710 void ModuleBase_WidgetMultiSelector::getSelectedAttributeIndices(std::set<int>& theAttributeIds)
711 {
712   myListView->getSelectedIndices(theAttributeIds);
713 }
714
715 void ModuleBase_WidgetMultiSelector::convertIndicesToViewerSelection(std::set<int> theAttributeIds,
716                                                    QList<ModuleBase_ViewerPrsPtr>& theValues) const
717 {
718   if(myFeature.get() == NULL)
719     return;
720
721   DataPtr aData = myFeature->data();
722   AttributePtr anAttribute = aData->attribute(attributeID());
723   std::string aType = anAttribute->attributeType();
724   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
725     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
726     for (int i = 0; i < aSelectionListAttr->size(); i++) {
727       // filter by attribute indices only if the container is not empty otherwise return all items
728       if (!theAttributeIds.empty() && theAttributeIds.find(i) == theAttributeIds.end())
729         continue;
730       AttributeSelectionPtr anAttr = aSelectionListAttr->value(i);
731       ResultPtr anObject = anAttr->context();
732       if (anObject.get())
733         theValues.append(std::shared_ptr<ModuleBase_ViewerPrs>(
734                new ModuleBase_ViewerPrs(anObject, anAttr->value(), NULL)));
735     }
736   }
737   else if (aType == ModelAPI_AttributeRefList::typeId()) {
738     AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
739     for (int i = 0; i < aRefListAttr->size(); i++) {
740       // filter by attribute indices only if the container is not empty otherwise return all items
741       if (!theAttributeIds.empty() && theAttributeIds.find(i) == theAttributeIds.end())
742         continue;
743       ObjectPtr anObject = aRefListAttr->object(i);
744       if (anObject.get()) {
745         theValues.append(std::shared_ptr<ModuleBase_ViewerPrs>(
746                new ModuleBase_ViewerPrs(anObject, GeomShapePtr(), NULL)));
747       }
748     }
749   }
750   else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
751     AttributeRefAttrListPtr aRefAttrListAttr = aData->refattrlist(attributeID());
752     for (int i = 0; i < aRefAttrListAttr->size(); i++) {
753       // filter by attribute indices only if the container is not empty otherwise return all items
754       if (!theAttributeIds.empty() && theAttributeIds.find(i) == theAttributeIds.end())
755         continue;
756       ObjectPtr anObject = aRefAttrListAttr->object(i);
757       if (!anObject.get())
758         continue;
759       TopoDS_Shape aShape;
760       AttributePtr anAttribute = aRefAttrListAttr->attribute(i);
761       if (anAttribute.get()) {
762         GeomShapePtr aGeomShape = ModuleBase_Tools::getShape(anAttribute, myWorkshop);
763         theValues.append(std::shared_ptr<ModuleBase_ViewerPrs>(
764                new ModuleBase_ViewerPrs(anObject, aGeomShape, NULL)));
765       }
766     }
767   }
768 }
769
770 bool ModuleBase_WidgetMultiSelector::removeUnusedAttributeObjects
771                                                  (QList<ModuleBase_ViewerPrsPtr>& theValues)
772 {
773   bool isDone = false;
774
775   std::map<ObjectPtr, std::set<GeomShapePtr> > aGeomSelection = convertSelection(theValues);
776   DataPtr aData = myFeature->data();
777   AttributePtr anAttribute = aData->attribute(attributeID());
778   std::string aType = anAttribute->attributeType();
779   std::set<GeomShapePtr> aShapes;
780   std::set<int> anIndicesToBeRemoved;
781   if (aType == ModelAPI_AttributeSelectionList::typeId()) {
782     // iteration through data model to find not selected elements to remove them
783     AttributeSelectionListPtr aSelectionListAttr = aData->selectionList(attributeID());
784     for (int i = 0; i < aSelectionListAttr->size(); i++) {
785       AttributeSelectionPtr anAttr = aSelectionListAttr->value(i);
786       bool aFound = findInSelection(anAttr->context(), anAttr->value(), aGeomSelection,
787                                     myWorkshop);
788       if (!aFound)
789         anIndicesToBeRemoved.insert(i);
790     }
791     isDone = anIndicesToBeRemoved.size() > 0;
792     aSelectionListAttr->remove(anIndicesToBeRemoved);
793   }
794   else if (aType == ModelAPI_AttributeRefList::typeId()) {
795     AttributeRefListPtr aRefListAttr = aData->reflist(attributeID());
796     for (int i = 0; i < aRefListAttr->size(); i++) {
797       ObjectPtr anObject = aRefListAttr->object(i);
798       if (anObject.get()) {
799         bool aFound = findInSelection(anObject, GeomShapePtr(), aGeomSelection,
800                                       myWorkshop);
801         if (!aFound)
802           anIndicesToBeRemoved.insert(i);
803       }
804     }
805     isDone = anIndicesToBeRemoved.size() > 0;
806     aRefListAttr->remove(anIndicesToBeRemoved);
807   }
808   else if (aType == ModelAPI_AttributeRefAttrList::typeId()) {
809     std::set<AttributePtr> anAttributes;
810     QList<ModuleBase_ViewerPrsPtr>::const_iterator
811       anIt = theValues.begin(), aLast = theValues.end();
812     ObjectPtr anObject;
813     GeomShapePtr aShape;
814     for (; anIt != aLast; anIt++) {
815       ModuleBase_ViewerPrsPtr aPrs = *anIt;
816       getGeomSelection(aPrs, anObject, aShape);
817       AttributePtr anAttr = myWorkshop->module()->findAttribute(anObject, aShape);
818       if (anAttr.get() && anAttributes.find(anAttr) == anAttributes.end())
819         anAttributes.insert(anAttr);
820     }
821
822     AttributeRefAttrListPtr aRefAttrListAttr = aData->refattrlist(attributeID());
823     for (int i = 0; i < aRefAttrListAttr->size(); i++) {
824       bool aFound = false;
825       if (aRefAttrListAttr->isAttribute(i)) {
826         AttributePtr anAttribute = aRefAttrListAttr->attribute(i);
827         aFound = anAttributes.find(anAttribute) != anAttributes.end();
828       }
829       else {
830         aFound = findInSelection(aRefAttrListAttr->object(i), GeomShapePtr(), aGeomSelection,
831                                  myWorkshop);
832       }
833       if (!aFound)
834         anIndicesToBeRemoved.insert(i);
835     }
836     isDone = anIndicesToBeRemoved.size() > 0;
837     aRefAttrListAttr->remove(anIndicesToBeRemoved);
838   }
839
840   return isDone;
841 }
842
843 std::map<ObjectPtr, std::set<GeomShapePtr> > ModuleBase_WidgetMultiSelector::convertSelection
844                                                      (QList<ModuleBase_ViewerPrsPtr>& theValues)
845 {
846   // convert prs list to objects map
847   std::map<ObjectPtr, std::set<GeomShapePtr> > aGeomSelection;
848   std::set<GeomShapePtr> aShapes;
849   QList<ModuleBase_ViewerPrsPtr>::const_iterator anIt = theValues.begin(), aLast = theValues.end();
850   ObjectPtr anObject;
851   GeomShapePtr aShape;
852   GeomShapePtr anEmptyShape(new GeomAPI_Shape());
853   for (; anIt != aLast; anIt++) {
854     ModuleBase_ViewerPrsPtr aPrs = *anIt;
855     getGeomSelection(aPrs, anObject, aShape);
856     aShapes.clear();
857     if (aGeomSelection.find(anObject) != aGeomSelection.end()) // found
858       aShapes = aGeomSelection[anObject];
859     // we need to know if there was an empty shape in selection for the object
860     if (!aShape.get())
861       aShape = anEmptyShape;
862     if (aShape.get() && aShapes.find(aShape) == aShapes.end()) // not found
863       aShapes.insert(aShape);
864     aGeomSelection[anObject] = aShapes;
865   }
866   return aGeomSelection;
867 }
868
869 bool ModuleBase_WidgetMultiSelector::findInSelection(const ObjectPtr& theObject,
870                               GeomShapePtr theShape,
871                               const std::map<ObjectPtr, std::set<GeomShapePtr> >& theGeomSelection,
872                               ModuleBase_IWorkshop* theWorkshop)
873 {
874   // issue #2154: we should not remove from list objects hidden in the viewer if selection
875   // was done with SHIFT button
876   if (theWorkshop->hasSHIFTPressed() && !theObject->isDisplayed())
877     return true;
878
879   bool aFound = false;
880   GeomShapePtr anEmptyShape(new GeomAPI_Shape());
881   if (theShape.get()) { // treat shape equal to context as null: 2219, keep order of shapes in list
882     const ResultPtr aContext = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
883     if (aContext.get() && aContext->shape()->isEqual(theShape))
884       theShape.reset();
885   }
886   GeomShapePtr aShape = theShape.get() ? theShape : anEmptyShape;
887   if (theGeomSelection.find(theObject) != theGeomSelection.end()) {// found
888     const std::set<GeomShapePtr>& aShapes = theGeomSelection.at(theObject);
889     std::set<GeomShapePtr>::const_iterator anIt = aShapes.begin(), aLast = aShapes.end();
890     for (; anIt != aLast && !aFound; anIt++) {
891       GeomShapePtr aCShape = *anIt;
892       if (aCShape.get())
893         aFound = aCShape->isSame(aShape);
894     }
895   }
896   return aFound;
897 }
898
899 QList<ActionInfo>
900   ModuleBase_WidgetMultiSelector::actionsList(ModuleBase_ActionType theActionType) const
901 {
902   QList<ActionInfo> aList;
903   if (myCurrentHistoryIndex > -1) {
904     int i = 0;
905     QString aTitle("Selection %1 items");
906     QString aTit("Selection %1 item");
907     QIcon aIcon(":pictures/selection.png");
908     int aNb;
909     switch (theActionType) {
910     case ActionUndo:
911       i = 1;
912       while (i <= myCurrentHistoryIndex) {
913         aNb = mySelectedHistoryValues.at(i).count();
914         if (aNb == 1) {
915           ActionInfo aInfo(aIcon, aTit.arg(aNb));
916           aList.insert(0, aInfo);
917         } else {
918           ActionInfo aInfo(aIcon, aTitle.arg(aNb));
919           aList.insert(0, aInfo);
920         }
921         i++;
922       }
923       break;
924     case ActionRedo:
925       i = mySelectedHistoryValues.length() - 1;
926       while (i > myCurrentHistoryIndex) {
927         aNb = mySelectedHistoryValues.at(i).count();
928         if (aNb == 1) {
929           ActionInfo aInfo(aIcon, aTit.arg(mySelectedHistoryValues.at(i).count()));
930           aList.insert(0, aInfo);
931         } else {
932           ActionInfo aInfo(aIcon, aTitle.arg(mySelectedHistoryValues.at(i).count()));
933           aList.insert(0, aInfo);
934         }
935         i--;
936       }
937       break;
938     }
939   }
940   return aList;
941 }