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