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