]> SALOME platform Git repositories - modules/shaper.git/blob - src/ModuleBase/ModuleBase_WidgetMultiSelector.cpp
Salome HOME
Improve multi-selector control to provide items multi-selection and "Delete" context...
[modules/shaper.git] / src / ModuleBase / ModuleBase_WidgetMultiSelector.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 /*
4  * ModuleBase_WidgetMultiSelector.cpp
5  *
6  *  Created on: Aug 28, 2014
7  *      Author: sbh
8  */
9
10 #include <ModuleBase_WidgetMultiSelector.h>
11 #include <ModuleBase_WidgetShapeSelector.h>
12 #include <ModuleBase_ISelection.h>
13 #include <ModuleBase_IWorkshop.h>
14 #include <ModuleBase_IViewer.h>
15 #include <ModuleBase_Tools.h>
16 #include <ModuleBase_Definitions.h>
17
18 #include <ModelAPI_Data.h>
19 #include <ModelAPI_Object.h>
20 #include <ModelAPI_AttributeSelectionList.h>
21 #include <ModelAPI_AttributeRefList.h>
22
23 #include <Config_WidgetAPI.h>
24
25 #include <QGridLayout>
26 #include <QLabel>
27 #include <QListWidget>
28 #include <QObject>
29 #include <QString>
30 #include <QComboBox>
31 #include <QEvent>
32 #include <QAction>
33 #include <QApplication>
34 #include <QClipboard>
35 #include <QTimer>
36
37 #include <memory>
38 #include <string>
39
40 const int ATTRIBUTE_SELECTION_INDEX_ROLE = Qt::UserRole + 1;
41
42 /**
43 * Customization of a List Widget to make it to be placed on full width of container
44 */
45 class CustomListWidget : public QListWidget
46 {
47 public:
48   /// Constructor
49   /// \param theParent a parent widget
50   CustomListWidget( QWidget* theParent )
51     : QListWidget( theParent )
52   {
53   }
54
55   /// Redefinition of virtual method
56   virtual QSize sizeHint() const
57   {
58     int aHeight = 2*QFontMetrics( font() ).height();
59     QSize aSize = QListWidget::sizeHint();
60     return QSize( aSize.width(), aHeight );
61   }
62
63   /// Redefinition of virtual method
64   virtual QSize minimumSizeHint() const
65   {
66     int aHeight = 4/*2*/*QFontMetrics( font() ).height();
67     QSize aSize = QListWidget::minimumSizeHint();
68     return QSize( aSize.width(), aHeight );
69   }
70
71 #ifndef WIN32
72 // The code is necessary only for Linux because
73 //it can not update viewport on widget resize
74 protected:
75   void resizeEvent(QResizeEvent* theEvent)
76   {
77     QListWidget::resizeEvent(theEvent);
78     QTimer::singleShot(5, viewport(), SLOT(repaint()));
79   }
80 #endif
81 };
82
83 ModuleBase_WidgetMultiSelector::ModuleBase_WidgetMultiSelector(QWidget* theParent,
84                                                                ModuleBase_IWorkshop* theWorkshop,
85                                                                const Config_WidgetAPI* theData,
86                                                                const std::string& theParentId)
87  : ModuleBase_WidgetSelector(theParent, theWorkshop, theData, theParentId),
88    mySelectionCount(0)
89 {
90   QGridLayout* aMainLay = new QGridLayout(this);
91   ModuleBase_Tools::adjustMargins(aMainLay);
92
93   QLabel* aTypeLabel = new QLabel(tr("Type"), this);
94   aMainLay->addWidget(aTypeLabel, 0, 0);
95
96   myTypeCombo = new QComboBox(this);
97   // There is no sense to parameterize list of types while we can not parameterize selection mode
98
99   std::string aPropertyTypes = theData->getProperty("type_choice");
100   QString aTypesStr = aPropertyTypes.c_str();
101   QStringList aShapeTypes = aTypesStr.split(' ', QString::SkipEmptyParts);
102
103   myIsUseChoice = theData->getBooleanAttribute("use_choice", true);
104
105   if (!aShapeTypes.empty())
106     myTypeCombo->addItems(aShapeTypes);
107   aMainLay->addWidget(myTypeCombo, 0, 1);
108   // if the xml definition contains one type, the controls to select a type should not be shown
109   if (aShapeTypes.size() <= 1 || !myIsUseChoice) {
110     aTypeLabel->setVisible(false);
111     myTypeCombo->setVisible(false);
112   }
113
114   std::string aLabelText = theData->getProperty("label");
115   QLabel* aListLabel = new QLabel(!aLabelText.empty() ? aLabelText.c_str()
116                                                       : tr("Selected objects:"), this);
117   aMainLay->addWidget(aListLabel, 1, 0);
118   // if the xml definition contains one type, an information label should be shown near to the latest
119   if (aShapeTypes.size() <= 1) {
120     QString aLabelIcon = QString::fromStdString(theData->widgetIcon());
121     if (!aLabelIcon.isEmpty()) {
122       QLabel* aSelectedLabel = new QLabel("", this);
123       aSelectedLabel->setPixmap(QPixmap(aLabelIcon));
124       aMainLay->addWidget(aSelectedLabel, 1, 1);
125     }
126     aMainLay->setColumnStretch(2, 1);
127   }
128
129   QString aToolTip = QString::fromStdString(theData->widgetTooltip());
130   myListControl = new CustomListWidget(this);
131   QString anObjName = QString::fromStdString(attributeID());
132   myListControl->setObjectName(anObjName);
133   myListControl->setToolTip(aToolTip);
134   myListControl->setSelectionMode(QAbstractItemView::ExtendedSelection);
135
136   aMainLay->addWidget(myListControl, 2, 0, 1, -1);
137   aMainLay->setRowStretch(2, 1);
138   //aMainLay->addWidget(new QLabel(this)); //FIXME(sbh)???
139   //aMainLay->setRowMinimumHeight(3, 20);
140   //this->setLayout(aMainLay);
141   connect(myTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onSelectionTypeChanged()));
142
143   myCopyAction = new QAction(QIcon(":pictures/copy.png"), tr("Copy"), this);
144   myCopyAction->setShortcut(QKeySequence::Copy);
145   myCopyAction->setEnabled(false);
146   connect(myCopyAction, SIGNAL(triggered(bool)), SLOT(onCopyItem()));
147   myListControl->addAction(myCopyAction);
148
149   myDeleteAction = new QAction(QIcon(":pictures/delete.png"), tr("Delete"), this);
150   myDeleteAction->setShortcut(QKeySequence::Delete);
151   myDeleteAction->setEnabled(false);
152   connect(myDeleteAction, SIGNAL(triggered(bool)), SLOT(onDeleteItem()));
153   myListControl->addAction(myDeleteAction);
154
155   myListControl->setContextMenuPolicy(Qt::ActionsContextMenu);
156   connect(myListControl, SIGNAL(itemSelectionChanged()), SLOT(onListSelection()));
157 }
158
159 ModuleBase_WidgetMultiSelector::~ModuleBase_WidgetMultiSelector()
160 {
161 }
162
163 //********************************************************************
164 bool ModuleBase_WidgetMultiSelector::storeValueCustom() const
165 {
166   // the value is stored on the selection changed signal processing 
167   // A rare case when plugin was not loaded. 
168   if (!myFeature)
169     return false;
170
171   AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
172   if (aSelectionListAttr.get()) {
173      aSelectionListAttr->setSelectionType(myTypeCombo->currentText().toStdString());
174   }   
175    return true;
176 }
177
178 //********************************************************************
179 bool ModuleBase_WidgetMultiSelector::restoreValueCustom()
180 {
181   // A rare case when plugin was not loaded. 
182   if (!myFeature)
183     return false;
184
185   AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
186   if (aSelectionListAttr.get()) {
187     // Restore shape type
188     if (!aSelectionListAttr->selectionType().empty())
189       setCurrentShapeType(ModuleBase_Tools::shapeType(aSelectionListAttr->selectionType().c_str()));
190   }
191   updateSelectionList();
192   return true;
193 }
194
195 //********************************************************************
196 void ModuleBase_WidgetMultiSelector::storeAttributeValue()
197 {
198   ModuleBase_WidgetValidated::storeAttributeValue();
199
200   AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
201   if (aSelectionListAttr.get()) {
202     mySelectionType = aSelectionListAttr->selectionType();
203     mySelectionCount = aSelectionListAttr->size();
204   }
205   else {
206     AttributeRefListPtr aRefListAttr = myFeature->data()->reflist(attributeID());
207     mySelectionCount = aRefListAttr->size();
208   }
209 }
210
211 //********************************************************************
212 void ModuleBase_WidgetMultiSelector::restoreAttributeValue(bool theValid)
213 {
214   ModuleBase_WidgetValidated::restoreAttributeValue(theValid);
215
216   // Store shape type
217   AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
218   if (aSelectionListAttr.get()) {
219     aSelectionListAttr->setSelectionType(mySelectionType);
220
221     // restore selection in the attribute. Indeed there is only one stored object
222     int aCountAppened = aSelectionListAttr->size() - mySelectionCount;
223     for (int i = 0; i < aCountAppened; i++)
224       aSelectionListAttr->removeLast();
225   }
226   else {
227     AttributeRefListPtr aRefListAttr = myFeature->data()->reflist(attributeID());
228     // restore objects in the attribute. Indeed there is only one stored object
229     int aCountAppened = aRefListAttr->size() - mySelectionCount;
230     for (int i = 0; i < aCountAppened; i++)
231       aRefListAttr->removeLast();
232   }
233 }
234
235 //********************************************************************
236 void ModuleBase_WidgetMultiSelector::clearAttribute()
237 {
238   AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
239   if (aSelectionListAttr.get())
240     aSelectionListAttr->clear();
241   else {
242     AttributeRefListPtr aRefListAttr = myFeature->data()->reflist(attributeID());
243     aRefListAttr->clear();
244   }
245 }
246
247 //********************************************************************
248 void ModuleBase_WidgetMultiSelector::setObject(ObjectPtr theSelectedObject,
249                                                GeomShapePtr theShape)
250 {
251   AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
252   if (aSelectionListAttr.get()) {
253     ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theSelectedObject);
254     aSelectionListAttr->append(aResult, theShape, myIsInValidate);
255   }
256   else {
257     AttributeRefListPtr aRefListAttr = myFeature->data()->reflist(attributeID());
258     aRefListAttr->append(theSelectedObject);
259   }
260 }
261
262 //********************************************************************
263 bool ModuleBase_WidgetMultiSelector::setSelection(QList<ModuleBase_ViewerPrs>& theValues,
264                                                   const bool theToValidate)
265 {
266   QList<ModuleBase_ViewerPrs> aSkippedValues;
267
268   QList<ModuleBase_ViewerPrs>::const_iterator anIt = theValues.begin(), aLast = theValues.end();
269   bool isDone = false;
270   for (; anIt != aLast; anIt++) {
271     ModuleBase_ViewerPrs aValue = *anIt;
272     bool aProcessed = false;
273     if (!theToValidate || isValidInFilters(aValue)) {
274       aProcessed = setSelectionCustom(aValue);
275     }
276     else
277       aSkippedValues.append(aValue);
278     // if there is at least one set, the result is true
279     isDone = isDone || aProcessed;
280   }
281   // updateObject - to update/redisplay feature
282   // it is commented in order to perfom it outside the method
283   //if (isDone) {
284     //updateObject(myFeature);
285     // this emit is necessary to call store/restore method an restore type of selection
286     //emit valuesChanged();
287   //}
288   theValues.clear();
289   if (!aSkippedValues.empty())
290     theValues.append(aSkippedValues);
291
292   return isDone;
293 }
294
295 //********************************************************************
296 bool ModuleBase_WidgetMultiSelector::isValidSelectionCustom(const ModuleBase_ViewerPrs& thePrs)
297 {
298   bool aValid = ModuleBase_WidgetSelector::isValidSelectionCustom(thePrs);
299   if (aValid) {
300     ResultPtr aResult = myWorkshop->selection()->getResult(thePrs);
301     aValid = aResult.get() != NULL;
302     if (aValid) {
303       if (myFeature) {
304         // We can not select a result of our feature
305         const std::list<ResultPtr>& aResList = myFeature->results();
306         std::list<ResultPtr>::const_iterator aIt;
307         bool isSkipSelf = false;
308         for (aIt = aResList.cbegin(); aIt != aResList.cend(); ++aIt) {
309           if ((*aIt) == aResult) {
310             isSkipSelf = true;
311             break;
312           }
313         }
314         if (isSkipSelf)
315           aValid = false;
316       }
317     }
318   }
319   return aValid;
320 }
321
322 //********************************************************************
323 QList<QWidget*> ModuleBase_WidgetMultiSelector::getControls() const
324 {
325   QList<QWidget*> result;
326   //result << myTypeCombo;
327   result << myListControl;
328   return result;
329 }
330
331 //********************************************************************
332 void ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()
333 {
334   activateSelectionAndFilters(true);
335   QList<ModuleBase_ViewerPrs> anEmptyList;
336   // This method will call Selection changed event which will call onSelectionChanged
337   // To clear mySelection, myListControl and storeValue()
338   // So, we don't need to call it
339   myWorkshop->setSelected(anEmptyList);
340 }
341
342 void ModuleBase_WidgetMultiSelector::updateFocus()
343 {
344   // Set focus to List control in order to make possible 
345   // to use Tab key for transfer the focus to next widgets
346   myListControl->setCurrentRow(myListControl->model()->rowCount() - 1);
347   ModuleBase_Tools::setFocus(myListControl,
348                              "ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()");
349 }
350
351 //********************************************************************
352 void ModuleBase_WidgetMultiSelector::updateSelectionName()
353 {
354 }
355
356 //********************************************************************
357 QIntList ModuleBase_WidgetMultiSelector::getShapeTypes() const
358 {
359   QIntList aShapeTypes;
360
361   if (myTypeCombo->count() > 1 && myIsUseChoice) {
362     aShapeTypes.append(ModuleBase_Tools::shapeType(myTypeCombo->currentText()));
363   }
364   else {
365     for (int i = 0, aCount = myTypeCombo->count(); i < aCount; i++) {
366       TopAbs_ShapeEnum aType = ModuleBase_Tools::shapeType(myTypeCombo->itemText(i));
367       aShapeTypes.append(aType);
368       if (aType == TopAbs_SOLID)
369         aShapeTypes.append(TopAbs_COMPSOLID);
370     }
371   }
372   return aShapeTypes;
373 }
374
375 //********************************************************************
376 void ModuleBase_WidgetMultiSelector::setCurrentShapeType(const TopAbs_ShapeEnum theShapeType)
377 {
378   QString aShapeTypeName;
379   
380   for (int idx = 0; idx < myTypeCombo->count(); ++idx) {
381     aShapeTypeName = myTypeCombo->itemText(idx);
382     TopAbs_ShapeEnum aRefType = ModuleBase_Tools::shapeType(aShapeTypeName);
383     if(aRefType == theShapeType && idx != myTypeCombo->currentIndex()) {
384       activateSelectionAndFilters(false);
385       bool isBlocked = myTypeCombo->blockSignals(true);
386       myTypeCombo->setCurrentIndex(idx);
387       myTypeCombo->blockSignals(isBlocked);
388
389       activateSelectionAndFilters(true);
390       break;
391     }
392   }
393 }
394
395 QList<ModuleBase_ViewerPrs> ModuleBase_WidgetMultiSelector::getAttributeSelection() const
396 {
397   QList<ModuleBase_ViewerPrs> aSelected;
398   // Restore selection in the viewer by the attribute selection list
399   if(myFeature) {
400     AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
401     if (aSelectionListAttr.get()) {
402       for (int i = 0; i < aSelectionListAttr->size(); i++) {
403         AttributeSelectionPtr anAttr = aSelectionListAttr->value(i);
404         ResultPtr anObject = anAttr->context();
405         if (anObject.get()) {
406           TopoDS_Shape aShape;
407           std::shared_ptr<GeomAPI_Shape> aShapePtr = anAttr->value();
408           if (aShapePtr.get()) {
409             aShape = aShapePtr->impl<TopoDS_Shape>();
410           }
411           aSelected.append(ModuleBase_ViewerPrs(anObject, aShape, NULL));
412         }
413       }
414     }
415     else {
416       AttributeRefListPtr aRefListAttr = myFeature->data()->reflist(attributeID());
417       if (aRefListAttr.get()) {
418         for (int i = 0; i < aRefListAttr->size(); i++) {
419           ObjectPtr anObject = aRefListAttr->object(i);
420           if (anObject.get()) {
421             aSelected.append(ModuleBase_ViewerPrs(anObject, TopoDS_Shape(), NULL));
422           }
423         }
424       }
425     }
426   }
427   return aSelected;
428 }
429
430 //********************************************************************
431 void ModuleBase_WidgetMultiSelector::updateSelectionList()
432 {
433   myListControl->clear();
434
435   AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
436   if (aSelectionListAttr.get()) {
437     for (int i = 0; i < aSelectionListAttr->size(); i++) {
438       AttributeSelectionPtr aAttr = aSelectionListAttr->value(i);
439       QListWidgetItem* anItem = new QListWidgetItem(aAttr->namingName().c_str(), myListControl);
440       anItem->setData(ATTRIBUTE_SELECTION_INDEX_ROLE, i);
441       myListControl->addItem(anItem);
442     }
443   }
444   else {
445     AttributeRefListPtr aRefListAttr = myFeature->data()->reflist(attributeID());
446     for (int i = 0; i < aRefListAttr->size(); i++) {
447       ObjectPtr anObject = aRefListAttr->object(i);
448       if (anObject.get()) {
449         QListWidgetItem* anItem = new QListWidgetItem(anObject->data()->name().c_str(),
450                                                       myListControl);
451         anItem->setData(ATTRIBUTE_SELECTION_INDEX_ROLE, i);
452         myListControl->addItem(anItem);
453       }
454     }
455   }
456   // We have to call repaint because sometimes the List control is not updated
457   myListControl->repaint();
458 }
459
460 //********************************************************************
461 std::string ModuleBase_WidgetMultiSelector::validatorType(const QString& theType) const
462 {
463   std::string aType;
464
465   if (theType == "Vertices")
466     aType = "vertex";
467   else if (theType == "Edges")
468     aType = "edge";
469   else if (theType == "Faces")
470     aType = "face";
471   else if (theType == "Solids")
472     aType = "solid";
473
474   return aType;
475 }
476
477 //********************************************************************
478 void ModuleBase_WidgetMultiSelector::onCopyItem()
479 {
480   QList<QListWidgetItem*> aItems = myListControl->selectedItems();
481   QString aRes;
482   foreach(QListWidgetItem* aItem, aItems) {
483     if (!aRes.isEmpty())
484       aRes += "\n";
485     aRes += aItem->text();
486   }
487   if (!aRes.isEmpty()) {
488     QClipboard *clipboard = QApplication::clipboard();
489     clipboard->setText(aRes);
490   }
491 }
492
493 //********************************************************************
494 void ModuleBase_WidgetMultiSelector::onDeleteItem()
495 {
496   // find attribute indices to delete
497   QList<QListWidgetItem*> aItems = myListControl->selectedItems();
498   std::set<int> anAttributeIds;
499   foreach(QListWidgetItem* anItem, aItems) {
500     int anIndex = anItem->data(ATTRIBUTE_SELECTION_INDEX_ROLE).toInt();
501     if (anAttributeIds.find(anIndex) == anAttributeIds.end())
502       anAttributeIds.insert(anIndex);
503   }
504   // refill attribute by the items which indices are not in the list of ids
505   bool aDone = false;
506   AttributeSelectionListPtr aSelectionListAttr = myFeature->data()->selectionList(attributeID());
507   if (aSelectionListAttr.get()) {
508     aDone = !anAttributeIds.empty();
509     aSelectionListAttr->remove(anAttributeIds);
510   }
511   else {
512     AttributeRefListPtr aRefListAttr = myFeature->data()->reflist(attributeID());
513     if (aRefListAttr.get()) {
514       aDone = !anAttributeIds.empty();
515       aRefListAttr->remove(anAttributeIds);
516     }
517   }
518   if (aDone) {
519     restoreValue();
520     myWorkshop->setSelected(getAttributeSelection());
521   }
522 }
523
524 //********************************************************************
525 void ModuleBase_WidgetMultiSelector::onListSelection()
526 {
527   QList<QListWidgetItem*> aItems = myListControl->selectedItems();
528   myCopyAction->setEnabled(!aItems.isEmpty());
529   myDeleteAction->setEnabled(!aItems.isEmpty());
530
531   //myWorkshop->setSelected(>setSelected(getAttributeSelection());
532   QList<ModuleBase_ViewerPrs> aSelectedItems;
533   
534   emit itemsSelected(aSelectedItems);
535 }