Salome HOME
Specification controls
[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 ModuleBase_WidgetMultiSelector::ModuleBase_WidgetMultiSelector(QWidget* theParent,
40                                                                ModuleBase_IWorkshop* theWorkshop,
41                                                                const Config_WidgetAPI* theData,
42                                                                const std::string& theParentId)
43     : ModuleBase_WidgetValidated(theParent, theData, theParentId),
44       myWorkshop(theWorkshop)
45 {
46   QGridLayout* aMainLay = new QGridLayout(this);
47   ModuleBase_Tools::adjustMargins(aMainLay);
48
49   QLabel* aTypeLabel = new QLabel(tr("Type"), this);
50   aMainLay->addWidget(aTypeLabel, 0, 0);
51
52   myTypeCombo = new QComboBox(this);
53   // There is no sence to paramerize list of types while we can not parametrize selection mode
54
55   myShapeValidator = new GeomValidators_ShapeType();
56
57   std::string aPropertyTypes = theData->getProperty("type_choice");
58   QString aTypesStr = aPropertyTypes.c_str();
59   QStringList aShapeTypes = aTypesStr.split(' ');
60
61   //myIsUseChoice = theData->getBooleanAttribute("use_choice", true);
62
63   myTypeCombo->addItems(aShapeTypes);
64   aMainLay->addWidget(myTypeCombo, 0, 1);
65   // if the xml definition contains one type, the controls to select a type should not be shown
66   if (aShapeTypes.size() == 1/* || !myIsUseChoice*/) {
67     aTypeLabel->setVisible(false);
68     myTypeCombo->setVisible(false);
69   }
70
71 // Modification for specification of 1.3.0
72   std::string aLabelText = "";//theData->getProperty("label");
73   QLabel* aListLabel = new QLabel(!aLabelText.empty() ? aLabelText.c_str()
74                                                       : tr("Selected objects:"), this);
75   aMainLay->addWidget(aListLabel, 1, 0);
76   // if the xml definition contains one type, an information label should be shown near to the latest
77   if (aShapeTypes.size() == 1) {
78     QString aLabelText = QString::fromStdString(theData->widgetLabel());
79     QString aLabelIcon = QString::fromStdString(theData->widgetIcon());
80     QLabel* aSelectedLabel = new QLabel(aLabelText, this);
81     if (!aLabelIcon.isEmpty())
82       aSelectedLabel->setPixmap(QPixmap(aLabelIcon));
83     aMainLay->addWidget(aSelectedLabel, 1, 1);
84     aMainLay->setColumnStretch(2, 1);
85   }
86
87   myListControl = new QListWidget(this);
88   aMainLay->addWidget(myListControl, 2, 0, 2, -1);
89   aMainLay->setRowStretch(2, 1);
90   aMainLay->addWidget(new QLabel(this)); //FIXME(sbh)???
91   aMainLay->setRowMinimumHeight(3, 20);
92   this->setLayout(aMainLay);
93   connect(myTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onSelectionTypeChanged()));
94
95   myCopyAction = new QAction(QIcon(":pictures/copy.png"), tr("Copy"), this);
96   myCopyAction->setShortcut(QKeySequence::Copy);
97   myCopyAction->setEnabled(false);
98   connect(myCopyAction, SIGNAL(triggered(bool)), SLOT(onCopyItem()));
99   myListControl->addAction(myCopyAction);
100   myListControl->setContextMenuPolicy(Qt::ActionsContextMenu);
101   connect(myListControl, SIGNAL(itemSelectionChanged()), SLOT(onListSelection()));
102 }
103
104 ModuleBase_WidgetMultiSelector::~ModuleBase_WidgetMultiSelector()
105 {
106   delete myShapeValidator;
107 }
108
109 //TODO: nds stabilization hotfix
110 void ModuleBase_WidgetMultiSelector::disconnectSignals()
111 {
112   disconnect(myWorkshop, SIGNAL(selectionChanged()), this, SLOT(onSelectionChanged()));
113 }
114
115 //********************************************************************
116 void ModuleBase_WidgetMultiSelector::activateCustom()
117 {
118   ModuleBase_IViewer* aViewer = myWorkshop->viewer();
119   connect(myWorkshop, SIGNAL(selectionChanged()), 
120           this,       SLOT(onSelectionChanged()), 
121           Qt::UniqueConnection);
122
123   activateShapeSelection(true);
124
125   // Restore selection in the viewer by the attribute selection list
126   myWorkshop->setSelected(getAttributeSelection());
127
128   activateFilters(myWorkshop, true);
129 }
130
131 //********************************************************************
132 void ModuleBase_WidgetMultiSelector::deactivate()
133 {
134   disconnect(myWorkshop, SIGNAL(selectionChanged()), this, SLOT(onSelectionChanged()));
135   activateShapeSelection(false);
136   activateFilters(myWorkshop, false);
137 }
138
139 //********************************************************************
140 bool ModuleBase_WidgetMultiSelector::storeValueCustom() const
141 {
142   // the value is stored on the selection changed signal processing 
143   // A rare case when plugin was not loaded. 
144   if(!myFeature)
145     return false;
146   DataPtr aData = myFeature->data();
147   AttributeSelectionListPtr aSelectionListAttr = 
148     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(aData->attribute(attributeID()));
149
150   if (aSelectionListAttr) {
151     // Store shapes type
152      TopAbs_ShapeEnum aCurrentType =
153            ModuleBase_Tools::shapeType(myTypeCombo->currentText());
154      aSelectionListAttr->setSelectionType(myTypeCombo->currentText().toStdString());
155   }   
156    return true;
157 }
158
159 //********************************************************************
160 bool ModuleBase_WidgetMultiSelector::restoreValue()
161 {
162   // A rare case when plugin was not loaded. 
163   if(!myFeature)
164     return false;
165   DataPtr aData = myFeature->data();
166   AttributeSelectionListPtr aSelectionListAttr = 
167     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(aData->attribute(attributeID()));
168
169   if (aSelectionListAttr) {
170     // Restore shape type
171     if (!aSelectionListAttr->selectionType().empty())
172       setCurrentShapeType(ModuleBase_Tools::shapeType(aSelectionListAttr->selectionType().c_str()));
173     updateSelectionList(aSelectionListAttr);
174     return true;
175   }
176   return false;
177 }
178
179 //********************************************************************
180 void ModuleBase_WidgetMultiSelector::storeAttributeValue()
181 {
182   DataPtr aData = myFeature->data();
183   AttributeSelectionListPtr aSelectionListAttr = 
184     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(aData->attribute(attributeID()));
185   if (aSelectionListAttr.get() == NULL)
186     return;
187
188   mySelectionType = aSelectionListAttr->selectionType();
189   mySelection.clear();
190   int aSize = aSelectionListAttr->size();
191   for (int i = 0; i < aSelectionListAttr->size(); i++) {
192     AttributeSelectionPtr aSelectAttr = aSelectionListAttr->value(i);
193     mySelection.append(GeomSelection(aSelectAttr->context(), aSelectAttr->value()));
194   }
195 }
196
197 //********************************************************************
198 void ModuleBase_WidgetMultiSelector::restoreAttributeValue(bool/* theValid*/)
199 {
200   DataPtr aData = myFeature->data();
201   AttributeSelectionListPtr aSelectionListAttr = 
202     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(aData->attribute(attributeID()));
203   if (aSelectionListAttr.get() == NULL)
204     return;
205   aSelectionListAttr->clear();
206
207   // Store shapes type
208   aSelectionListAttr->setSelectionType(mySelectionType);
209
210   // Store selection in the attribute
211   int aSize = mySelection.size();
212   foreach (GeomSelection aSelec, mySelection) {
213     aSelectionListAttr->append(aSelec.first, aSelec.second);
214   }
215 }
216
217 //********************************************************************
218 void ModuleBase_WidgetMultiSelector::customValidators(
219                                         std::list<ModelAPI_Validator*>& theValidators,
220                                         std::list<std::list<std::string> >& theArguments) const
221 {
222   return;
223   std::list<std::string> anArguments;
224
225   theValidators.push_back(myShapeValidator);
226   if (true/*myIsUseChoice*/) {
227     QString aType = myTypeCombo->currentText();
228     anArguments.push_back(validatorType(aType));
229   }
230   else {
231     for(int i = 0, aCount = myTypeCombo->count(); i < aCount; i++) {
232       anArguments.push_back(validatorType(myTypeCombo->itemText(i)));
233     }
234   }
235   theArguments.push_back(anArguments);
236 }
237
238 //********************************************************************
239 bool ModuleBase_WidgetMultiSelector::acceptSubShape(const TopoDS_Shape& theShape) const
240 {
241   bool aValid = true;
242   if (theShape.IsNull()) {
243     aValid = true; // do not check the shape type if the shape is empty
244     // extrusion uses a sketch object selectected in Object browser
245   }
246   else {
247     aValid = false;
248     TopAbs_ShapeEnum aShapeType = theShape.ShapeType();
249     if (myTypeCombo->count() > 1) {
250       TopAbs_ShapeEnum aType = ModuleBase_Tools::shapeType(myTypeCombo->currentText());
251       aValid = aShapeType == aType;
252     }
253     else {
254       for(int i = 0, aCount = myTypeCombo->count(); i < aCount && !aValid; i++) {
255         TopAbs_ShapeEnum aType = ModuleBase_Tools::shapeType(myTypeCombo->itemText(i));
256         aValid = aShapeType == aType;
257       }
258     }
259   }
260   return aValid;
261 }
262
263 //********************************************************************
264 bool ModuleBase_WidgetMultiSelector::setSelection(const QList<ModuleBase_ViewerPrs>& theValues,
265                                                   int& thePosition)
266 {
267   if (thePosition < 0)
268     return false;
269
270   QList<ModuleBase_ViewerPrs>::const_iterator anIt = theValues.begin(), aLast = theValues.end();
271   bool isDone = false;
272   for (int i = thePosition; i < theValues.size(); i++) {
273     ModuleBase_ViewerPrs aValue = theValues[i];
274     bool aProcessed = false;
275     if (isValidSelection(aValue)) {
276       aProcessed = setSelectionCustom(aValue);
277     }
278     // if there is at least one set, the result is true
279     isDone = isDone || aProcessed;
280     // when an object, which do not satisfy the validating process, stop set selection
281     if (!aProcessed)
282       break;
283     else
284       thePosition++;
285   }
286   if (isDone) {
287     updateObject(myFeature);
288     // this emit is necessary to call store/restore method an restore type of selection
289     emit valuesChanged();
290   }
291   return isDone;
292 }
293
294 //********************************************************************
295 bool ModuleBase_WidgetMultiSelector::setSelectionCustom(const ModuleBase_ViewerPrs& thePrs)
296 {
297   TopoDS_Shape aShape = thePrs.shape();
298   if (!acceptSubShape(aShape))
299     return false;
300
301   ResultPtr aResult;
302   if (!thePrs.owner().IsNull()) {
303     ObjectPtr anObject = myWorkshop->selection()->getSelectableObject(thePrs.owner());
304     aResult = std::dynamic_pointer_cast<ModelAPI_Result>(anObject);
305   }
306   else {
307     aResult = std::dynamic_pointer_cast<ModelAPI_Result>(thePrs.object());
308   }
309
310
311   if (myFeature) {
312     // We can not select a result of our feature
313     const std::list<ResultPtr>& aResList = myFeature->results();
314     std::list<ResultPtr>::const_iterator aIt;
315     bool isSkipSelf = false;
316     for (aIt = aResList.cbegin(); aIt != aResList.cend(); ++aIt) {
317       if ((*aIt) == aResult) {
318         isSkipSelf = true;
319         break;
320       }
321     }
322     if(isSkipSelf)
323       return false;
324   }
325
326   // if the result has the similar shap as the parameter shape, just the context is set to the
327   // selection list attribute.
328   DataPtr aData = myFeature->data();
329   AttributeSelectionListPtr aSelectionListAttr = 
330     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(aData->attribute(attributeID()));
331
332   const TopoDS_Shape& aTDSShape = thePrs.shape();
333   // if only result is selected, an empty shape is set to the model
334   if (aTDSShape.IsNull()) {
335     aSelectionListAttr->append(aResult, GeomShapePtr());
336   }
337   else {
338     GeomShapePtr aShape(new GeomAPI_Shape());
339     aShape->setImpl(new TopoDS_Shape(aTDSShape));
340     // We can not select a result of our feature
341     if (aShape->isEqual(aResult->shape()))
342       aSelectionListAttr->append(aResult, GeomShapePtr());
343     else
344       aSelectionListAttr->append(aResult, aShape);
345   }
346   return true;
347 }
348
349 //********************************************************************
350 QList<QWidget*> ModuleBase_WidgetMultiSelector::getControls() const
351 {
352   QList<QWidget*> result;
353   //result << myTypeCombo;
354   result << myListControl;
355   return result;
356 }
357
358 //********************************************************************
359 bool ModuleBase_WidgetMultiSelector::eventFilter(QObject* theObj, QEvent* theEvent)
360 {
361   //TODO: Remove maybe?
362   return ModuleBase_ModelWidget::eventFilter(theObj, theEvent);
363 }
364
365 //********************************************************************
366 void ModuleBase_WidgetMultiSelector::onSelectionTypeChanged()
367 {
368   activateShapeSelection(true);
369   activateFilters(myWorkshop, true);
370   QList<ModuleBase_ViewerPrs> anEmptyList;
371   // This method will call Selection changed event which will call onSelectionChanged
372   // To clear mySelection, myListControl and storeValue()
373   // So, we don't need to call it
374   myWorkshop->setSelected(anEmptyList);
375 }
376
377 //********************************************************************
378 void ModuleBase_WidgetMultiSelector::onSelectionChanged()
379 {
380   QList<ModuleBase_ViewerPrs> aSelected = getSelectedEntitiesOrObjects(myWorkshop->selection());
381
382   DataPtr aData = myFeature->data();
383   AttributeSelectionListPtr aSelectionListAttr = 
384     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(aData->attribute(attributeID()));
385
386   aSelectionListAttr->clear();
387   if (aSelected.size() > 0) {
388     foreach (ModuleBase_ViewerPrs aPrs, aSelected) {
389       if (isValidSelection(aPrs)) {
390         setSelectionCustom(aPrs);
391       }
392     }
393   }
394   emit valuesChanged();
395   // the updateObject method should be called to flush the updated sigal. The workshop listens it,
396   // calls validators for the feature and, as a result, updates the Apply button state.
397   updateObject(myFeature);
398
399   // Set focus to List control in order to make possible 
400   // to use Tab key for transfer the focus to next widgets
401   myListControl->setCurrentRow(myListControl->model()->rowCount() - 1);
402   myListControl->setFocus();
403 }
404
405 //********************************************************************
406 void ModuleBase_WidgetMultiSelector::setCurrentShapeType(const TopAbs_ShapeEnum theShapeType)
407 {
408   QString aShapeTypeName;
409   
410   for (int idx = 0; idx < myTypeCombo->count(); ++idx) {
411     aShapeTypeName = myTypeCombo->itemText(idx);
412     TopAbs_ShapeEnum aRefType = ModuleBase_Tools::shapeType(aShapeTypeName);
413     if(aRefType == theShapeType && idx != myTypeCombo->currentIndex()) {
414       activateShapeSelection(false);
415       activateFilters(myWorkshop, false);
416       bool isBlocked = myTypeCombo->blockSignals(true);
417       myTypeCombo->setCurrentIndex(idx);
418       myTypeCombo->blockSignals(isBlocked);
419
420       activateShapeSelection(true);
421       activateFilters(myWorkshop, true);
422       break;
423     }
424   }
425 }
426
427 void ModuleBase_WidgetMultiSelector::activateShapeSelection(const bool isActivated)
428 {
429   ModuleBase_IViewer* aViewer = myWorkshop->viewer();
430
431   if (isActivated) {
432     QString aNewType = myTypeCombo->currentText();
433     QIntList aList;
434     if (true /*myIsUseChoice*/) {
435       aList.append(ModuleBase_Tools::shapeType(aNewType));
436     }
437     else {
438       for(int i = 0, aCount = myTypeCombo->count(); i < aCount; i++)
439         aList.append(ModuleBase_Tools::shapeType(myTypeCombo->itemText(i)));
440     }
441     myWorkshop->activateSubShapesSelection(aList);
442   } else {
443     myWorkshop->deactivateSubShapesSelection();
444   }
445 }
446
447 QList<ModuleBase_ViewerPrs> ModuleBase_WidgetMultiSelector::getAttributeSelection() const
448 {
449   QList<ModuleBase_ViewerPrs> aSelected;
450   // Restore selection in the viewer by the attribute selection list
451   if(myFeature) {
452     DataPtr aData = myFeature->data();
453     AttributeSelectionListPtr aListAttr = 
454       std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(aData->attribute(attributeID()));
455     if (aListAttr) {
456       for (int i = 0; i < aListAttr->size(); i++) {
457         AttributeSelectionPtr anAttr = aListAttr->value(i);
458         ResultPtr anObject = anAttr->context();
459         if (anObject.get()) {
460           TopoDS_Shape aShape;
461           std::shared_ptr<GeomAPI_Shape> aShapePtr = anAttr->value();
462           if (aShapePtr.get()) {
463             aShape = aShapePtr->impl<TopoDS_Shape>();
464           }
465           aSelected.append(ModuleBase_ViewerPrs(anObject, aShape, NULL));
466         }
467       }
468     }
469   }
470   return aSelected;
471 }
472
473 //********************************************************************
474 void ModuleBase_WidgetMultiSelector::updateSelectionList(AttributeSelectionListPtr theList)
475 {
476   myListControl->clear();
477   for (int i = 0; i < theList->size(); i++) {
478     AttributeSelectionPtr aAttr = theList->value(i);
479     myListControl->addItem(aAttr->namingName().c_str());
480   }
481   // We have to call repaint because sometimes the List control is not updated
482   myListControl->repaint();
483 }
484
485 //********************************************************************
486 std::string ModuleBase_WidgetMultiSelector::validatorType(const QString& theType) const
487 {
488   std::string aType;
489
490   if (theType == "Vertices")
491     aType = "vertex";
492   else if (theType == "Edges")
493     aType = "edge";
494   else if (theType == "Faces")
495     aType = "face";
496   else if (theType == "Solids")
497     aType = "solid";
498
499   return aType;
500 }
501
502 //********************************************************************
503 void ModuleBase_WidgetMultiSelector::onCopyItem()
504 {
505   QList<QListWidgetItem*> aItems = myListControl->selectedItems();
506   QString aRes;
507   foreach(QListWidgetItem* aItem, aItems) {
508     if (!aRes.isEmpty())
509       aRes += "\n";
510     aRes += aItem->text();
511   }
512   if (!aRes.isEmpty()) {
513     QClipboard *clipboard = QApplication::clipboard();
514     clipboard->setText(aRes);
515   }
516 }
517
518 //********************************************************************
519 void ModuleBase_WidgetMultiSelector::onListSelection()
520 {
521   QList<QListWidgetItem*> aItems = myListControl->selectedItems();
522   myCopyAction->setEnabled(!aItems.isEmpty());
523 }
524