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