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