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