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