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