Salome HOME
242b05c8c6be8b711988556a9ea64e470021a0cc
[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 <GeomValidators_ShapeType.h>
19
20 #include <ModelAPI_Data.h>
21 #include <ModelAPI_Object.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), myIsInValidate(false)
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   DataPtr aData = myFeature->data();
136   AttributeSelectionListPtr aSelectionListAttr = 
137     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(aData->attribute(attributeID()));
138
139   if (aSelectionListAttr) {
140     // Store shapes type
141      TopAbs_ShapeEnum aCurrentType =
142            ModuleBase_Tools::shapeType(myTypeCombo->currentText());
143      aSelectionListAttr->setSelectionType(myTypeCombo->currentText().toStdString());
144   }   
145    return true;
146 }
147
148 //********************************************************************
149 bool ModuleBase_WidgetMultiSelector::restoreValueCustom()
150 {
151   // A rare case when plugin was not loaded. 
152   if(!myFeature)
153     return false;
154   DataPtr aData = myFeature->data();
155   AttributeSelectionListPtr aSelectionListAttr = 
156     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(aData->attribute(attributeID()));
157
158   if (aSelectionListAttr) {
159     // Restore shape type
160     if (!aSelectionListAttr->selectionType().empty())
161       setCurrentShapeType(ModuleBase_Tools::shapeType(aSelectionListAttr->selectionType().c_str()));
162     updateSelectionList(aSelectionListAttr);
163     return true;
164   }
165   return false;
166 }
167
168 //********************************************************************
169 void ModuleBase_WidgetMultiSelector::storeAttributeValue()
170 {
171   myIsInValidate = true;
172   DataPtr aData = myFeature->data();
173   AttributeSelectionListPtr aSelectionListAttr = 
174     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(aData->attribute(attributeID()));
175   if (aSelectionListAttr.get() == NULL)
176     return;
177
178   mySelectionType = aSelectionListAttr->selectionType();
179   mySelectionCount = aSelectionListAttr->size();
180 }
181
182 //********************************************************************
183 void ModuleBase_WidgetMultiSelector::clearAttribute()
184 {
185   DataPtr aData = myFeature->data();
186   AttributeSelectionListPtr aSelectionListAttr =
187     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(aData->attribute(attributeID()));
188   aSelectionListAttr->clear();
189 }
190
191 //********************************************************************
192 void ModuleBase_WidgetMultiSelector::setObject(ObjectPtr theSelectedObject,
193                                                GeomShapePtr theShape)
194 {
195   DataPtr aData = myFeature->data();
196   AttributeSelectionListPtr aSelectionListAttr = 
197     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(aData->attribute(attributeID()));
198
199   int aSelCount = aSelectionListAttr->size();
200   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theSelectedObject);
201   aSelectionListAttr->append(aResult, theShape, myIsInValidate);
202 }
203
204 //********************************************************************
205 void ModuleBase_WidgetMultiSelector::restoreAttributeValue(bool/* theValid*/)
206 {
207   myIsInValidate = false;
208
209   // Store shape type
210   DataPtr aData = myFeature->data();
211   AttributeSelectionListPtr aSelectionListAttr = 
212     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(aData->attribute(attributeID()));
213   aSelectionListAttr->setSelectionType(mySelectionType);
214
215   // restore selection in the attribute
216   int aCountAppened = aSelectionListAttr->size() - mySelectionCount;
217   for ( int i = 0; i < aCountAppened; i++)
218     aSelectionListAttr->removeLast();
219 }
220
221 //********************************************************************
222 bool ModuleBase_WidgetMultiSelector::setSelection(QList<ModuleBase_ViewerPrs>& theValues,
223                                                   const bool theToValidate)
224 {
225   QList<ModuleBase_ViewerPrs> aSkippedValues;
226
227   QList<ModuleBase_ViewerPrs>::const_iterator anIt = theValues.begin(), aLast = theValues.end();
228   bool isDone = false;
229   for (; anIt != aLast; anIt++) {
230     ModuleBase_ViewerPrs aValue = *anIt;
231     bool aProcessed = false;
232     if (!theToValidate || isValidInFilters(aValue)) {
233       aProcessed = setSelectionCustom(aValue);
234     }
235     else
236       aSkippedValues.append(aValue);
237     // if there is at least one set, the result is true
238     isDone = isDone || aProcessed;
239   }
240   // updateObject - to update/redisplay feature
241   // it is commented in order to perfom it outside the method
242   //if (isDone) {
243     //updateObject(myFeature);
244     // this emit is necessary to call store/restore method an restore type of selection
245     //emit valuesChanged();
246   //}
247   theValues.clear();
248   if (!aSkippedValues.empty())
249     theValues.append(aSkippedValues);
250
251   return isDone;
252 }
253
254 //********************************************************************
255 bool ModuleBase_WidgetMultiSelector::isValidSelectionCustom(const ModuleBase_ViewerPrs& thePrs)
256 {
257   bool aValid = ModuleBase_WidgetSelector::isValidSelectionCustom(thePrs);
258   if (aValid) {
259     ResultPtr aResult = myWorkshop->selection()->getResult(thePrs);
260     aValid = aResult.get() != NULL;
261     if (aValid) {
262       if (myFeature) {
263         // We can not select a result of our feature
264         const std::list<ResultPtr>& aResList = myFeature->results();
265         std::list<ResultPtr>::const_iterator aIt;
266         bool isSkipSelf = false;
267         for (aIt = aResList.cbegin(); aIt != aResList.cend(); ++aIt) {
268           if ((*aIt) == aResult) {
269             isSkipSelf = true;
270             break;
271           }
272         }
273         if (isSkipSelf)
274           aValid = false;
275       }
276     }
277   }
278   return aValid;
279 }
280
281 //********************************************************************
282 QList<QWidget*> ModuleBase_WidgetMultiSelector::getControls() const
283 {
284   QList<QWidget*> result;
285   //result << myTypeCombo;
286   result << myListControl;
287   return result;
288 }
289
290 //********************************************************************
291 void ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()
292 {
293   activateSelection(true);
294   activateFilters(true);
295   QList<ModuleBase_ViewerPrs> anEmptyList;
296   // This method will call Selection changed event which will call onSelectionChanged
297   // To clear mySelection, myListControl and storeValue()
298   // So, we don't need to call it
299   myWorkshop->setSelected(anEmptyList);
300 }
301
302 void ModuleBase_WidgetMultiSelector::updateFocus()
303 {
304   // Set focus to List control in order to make possible 
305   // to use Tab key for transfer the focus to next widgets
306   myListControl->setCurrentRow(myListControl->model()->rowCount() - 1);
307   myListControl->setFocus();
308 }
309
310 //********************************************************************
311 void ModuleBase_WidgetMultiSelector::updateSelectionName()
312 {
313 }
314
315 //********************************************************************
316 QIntList ModuleBase_WidgetMultiSelector::getShapeTypes() const
317 {
318   QIntList aShapeTypes;
319
320   if (myTypeCombo->count() > 1 && myIsUseChoice) {
321     aShapeTypes.append(ModuleBase_Tools::shapeType(myTypeCombo->currentText()));
322   }
323   else {
324     for (int i = 0, aCount = myTypeCombo->count(); i < aCount; i++) {
325       aShapeTypes.append(ModuleBase_Tools::shapeType(myTypeCombo->itemText(i)));
326     }
327   }
328   return aShapeTypes;
329 }
330
331 //********************************************************************
332 void ModuleBase_WidgetMultiSelector::setCurrentShapeType(const TopAbs_ShapeEnum theShapeType)
333 {
334   QString aShapeTypeName;
335   
336   for (int idx = 0; idx < myTypeCombo->count(); ++idx) {
337     aShapeTypeName = myTypeCombo->itemText(idx);
338     TopAbs_ShapeEnum aRefType = ModuleBase_Tools::shapeType(aShapeTypeName);
339     if(aRefType == theShapeType && idx != myTypeCombo->currentIndex()) {
340       activateSelection(false);
341       activateFilters(false);
342       bool isBlocked = myTypeCombo->blockSignals(true);
343       myTypeCombo->setCurrentIndex(idx);
344       myTypeCombo->blockSignals(isBlocked);
345
346       activateSelection(true);
347       activateFilters(true);
348       break;
349     }
350   }
351 }
352
353 QList<ModuleBase_ViewerPrs> ModuleBase_WidgetMultiSelector::getAttributeSelection() const
354 {
355   QList<ModuleBase_ViewerPrs> aSelected;
356   // Restore selection in the viewer by the attribute selection list
357   if(myFeature) {
358     DataPtr aData = myFeature->data();
359     AttributeSelectionListPtr aListAttr = 
360       std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(aData->attribute(attributeID()));
361     if (aListAttr) {
362       for (int i = 0; i < aListAttr->size(); i++) {
363         AttributeSelectionPtr anAttr = aListAttr->value(i);
364         ResultPtr anObject = anAttr->context();
365         if (anObject.get()) {
366           TopoDS_Shape aShape;
367           std::shared_ptr<GeomAPI_Shape> aShapePtr = anAttr->value();
368           if (aShapePtr.get()) {
369             aShape = aShapePtr->impl<TopoDS_Shape>();
370           }
371           aSelected.append(ModuleBase_ViewerPrs(anObject, aShape, NULL));
372         }
373       }
374     }
375   }
376   return aSelected;
377 }
378
379 //********************************************************************
380 void ModuleBase_WidgetMultiSelector::updateSelectionList(AttributeSelectionListPtr theList)
381 {
382   myListControl->clear();
383   for (int i = 0; i < theList->size(); i++) {
384     AttributeSelectionPtr aAttr = theList->value(i);
385     myListControl->addItem(aAttr->namingName().c_str());
386   }
387   // We have to call repaint because sometimes the List control is not updated
388   myListControl->repaint();
389 }
390
391 //********************************************************************
392 std::string ModuleBase_WidgetMultiSelector::validatorType(const QString& theType) const
393 {
394   std::string aType;
395
396   if (theType == "Vertices")
397     aType = "vertex";
398   else if (theType == "Edges")
399     aType = "edge";
400   else if (theType == "Faces")
401     aType = "face";
402   else if (theType == "Solids")
403     aType = "solid";
404
405   return aType;
406 }
407
408 //********************************************************************
409 void ModuleBase_WidgetMultiSelector::onCopyItem()
410 {
411   QList<QListWidgetItem*> aItems = myListControl->selectedItems();
412   QString aRes;
413   foreach(QListWidgetItem* aItem, aItems) {
414     if (!aRes.isEmpty())
415       aRes += "\n";
416     aRes += aItem->text();
417   }
418   if (!aRes.isEmpty()) {
419     QClipboard *clipboard = QApplication::clipboard();
420     clipboard->setText(aRes);
421   }
422 }
423
424 //********************************************************************
425 void ModuleBase_WidgetMultiSelector::onListSelection()
426 {
427   QList<QListWidgetItem*> aItems = myListControl->selectedItems();
428   myCopyAction->setEnabled(!aItems.isEmpty());
429 }