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