]> SALOME platform Git repositories - modules/shaper.git/blob - src/ModuleBase/ModuleBase_ModelWidget.cpp
Salome HOME
Issue #3086: Avoid crash when FeatureInfo is null.
[modules/shaper.git] / src / ModuleBase / ModuleBase_ModelWidget.cpp
1 // Copyright (C) 2014-2019  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "ModuleBase_ModelWidget.h"
21 #include "ModuleBase_IPropertyPanel.h"
22 #include "ModuleBase_ViewerPrs.h"
23 #include "ModuleBase_Tools.h"
24 #include "ModuleBase_WidgetValidator.h"
25
26 #include <Events_InfoMessage.h>
27
28 #include <ModelAPI_Data.h>
29 #include <ModelAPI_Attribute.h>
30 #include <ModelAPI_Events.h>
31 #include <ModelAPI_Session.h>
32 #include <ModelAPI_Validator.h>
33
34 #include <Config_Keywords.h>
35 #include <Config_WidgetAPI.h>
36 #include <Config_Translator.h>
37 #include <Config_PropManager.h>
38
39 #include <Events_Loop.h>
40
41 #include <QEvent>
42 #include <QLabel>
43 #include <QFocusEvent>
44 #include <QTextCodec>
45
46 //#define DEBUG_VALUE_STATE
47
48 //#define DEBUG_WIDGET_INSTANCE
49 //#define DEBUG_ENABLE_SKETCH_INPUT_FIELDS
50
51 //**************************************************************
52 ModuleBase_ModelWidget::ModuleBase_ModelWidget(QWidget* theParent,
53   const Config_WidgetAPI* theData)
54   : QWidget(theParent),
55   myIsEditing(false),
56   myState(Stored),
57   myIsValueStateBlocked(false),
58   myFlushUpdateBlocked(false),
59   myWidgetValidator(0)
60 {
61 #ifdef DEBUG_WIDGET_INSTANCE
62   qDebug("ModuleBase_ModelWidget::ModuleBase_ModelWidget");
63 #endif
64
65   myFeatureId = theData->featureId();
66
67   myIsInternal = theData->getBooleanAttribute(ATTR_INTERNAL, false);
68
69   myIsModifiedInEdit = theData->getProperty(ATTR_MODIFIED_IN_EDIT);
70
71   myUpdateVisualAttributes = theData->getBooleanAttribute(ATTR_VISUAL_CHANGED, false);
72
73   myDefaultValue = theData->getProperty(ATTR_DEFAULT);
74   myUseReset = theData->getBooleanAttribute(ATTR_USE_RESET, true);
75   myIsComputedDefault = theData->getProperty(ATTR_DEFAULT) == DOUBLE_WDG_DEFAULT_COMPUTED;
76   myAttributeID = theData ? theData->widgetId() : "";
77   myIsObligatory = theData->getBooleanAttribute(ATTR_OBLIGATORY, true);
78
79   myIsValueEnabled = On; // not defined or "true"
80   std::string anEnableValue = theData->getProperty(DOUBLE_WDG_ENABLE_VALUE);
81   if (anEnableValue == "false")
82     myIsValueEnabled = Off;
83   if (anEnableValue == DOUBLE_WDG_ENABLE_VALUE_BY_PREFERENCES)
84     myIsValueEnabled = DefinedInPreferences;
85
86   connect(this, SIGNAL(valuesChanged()), this, SLOT(onWidgetValuesChanged()));
87   connect(this, SIGNAL(valuesModified()), this, SLOT(onWidgetValuesModified()));
88 }
89
90 //**************************************************************
91 ModuleBase_ModelWidget::~ModuleBase_ModelWidget()
92 {
93 #ifdef DEBUG_WIDGET_INSTANCE
94   qDebug("ModuleBase_ModelWidget::~ModuleBase_ModelWidget");
95 #endif
96 }
97
98 //**************************************************************
99 bool ModuleBase_ModelWidget::reset()
100 {
101   bool aResult = resetCustom();
102   if (aResult)
103     setValueState(Reset);
104
105   return aResult;
106 }
107
108 //**************************************************************
109 bool ModuleBase_ModelWidget::isInitialized(ObjectPtr theObject) const
110 {
111   return theObject->data()->attribute(attributeID())->isInitialized();
112 }
113
114 //**************************************************************
115 void ModuleBase_ModelWidget::selectionModes(int& theModuleSelectionModes, QIntList& theModes)
116 {
117   theModuleSelectionModes = -1;
118   if (myWidgetValidator)
119     myWidgetValidator->selectionModes(theModuleSelectionModes, theModes);
120 }
121
122 //**************************************************************
123 void ModuleBase_ModelWidget::selectionFilters(QIntList& theModuleSelectionFilters,
124                                               SelectMgr_ListOfFilter& theSelectionFilters)
125 {
126   if (myWidgetValidator)
127     myWidgetValidator->selectionFilters(theModuleSelectionFilters, theSelectionFilters);
128 }
129
130 //**************************************************************
131 bool ModuleBase_ModelWidget::isValueEnabled() const
132 {
133   bool anEnabled = true;
134   if (myIsValueEnabled == DefinedInPreferences) {
135 #ifdef DEBUG_ENABLE_SKETCH_INPUT_FIELDS
136     bool aCanDisable = false;
137 #else
138     //Config_PropManager::boolean(SKETCH_TAB_NAME, "disable_input_fields", "true");
139     bool aCanDisable = true;
140 #endif
141     if (aCanDisable)
142       anEnabled = false;
143   }
144   else if (myIsValueEnabled == Off)
145     anEnabled = false;
146   return anEnabled;
147 }
148
149 //**************************************************************
150 void ModuleBase_ModelWidget::processValueState()
151 {
152   if (myState == ModifiedInPP || myState == ModifiedInViewer)
153     storeValue();
154 }
155
156 //**************************************************************
157 Events_InfoMessage ModuleBase_ModelWidget::getValueStateError() const
158 {
159   Events_InfoMessage aMessage;
160   aMessage.setContext(context());
161
162   ModuleBase_ModelWidget::ValueState aState = getValueState();
163   if (aState != ModuleBase_ModelWidget::Stored) {
164     AttributePtr anAttr = feature()->attribute(attributeID());
165     if (anAttr.get()) {
166       const std::string& anAttributeName = anAttr->id();
167       switch (aState) {
168         case ModuleBase_ModelWidget::ModifiedInViewer:
169           aMessage = "Attribute \"%1\" is locked by modification value in the viewer.";
170           aMessage.addParameter(anAttributeName);
171           break;
172         case ModuleBase_ModelWidget::Reset:
173           aMessage = "Attribute \"%1\" is not initialized.";
174           aMessage.addParameter(anAttributeName);
175           break;
176         case ModuleBase_ModelWidget::ModifiedInPP: // Apply should be enabled in this mode
177         default:
178           break;
179       }
180     }
181   }
182   return aMessage;
183 }
184
185 //**************************************************************
186 QString ModuleBase_ModelWidget::getError(const bool theValueStateChecked) const
187 {
188   QString anError;
189
190   if (!feature().get())
191     return anError;
192
193   std::string aFeatureID = feature()->getKind();
194   std::string anAttributeID = attributeID();
195   AttributePtr anAttribute = feature()->attribute(anAttributeID);
196   if (!anAttribute.get())
197     return anError;
198
199   std::string aValidatorID;
200   Events_InfoMessage anErrorMsg;
201
202   static ModelAPI_ValidatorsFactory* aValidators = ModelAPI_Session::get()->validators();
203   if (!aValidators->validate(anAttribute, aValidatorID, anErrorMsg)) {
204     if (anErrorMsg.empty())
205       anErrorMsg = "Unknown error.";
206
207     if (anErrorMsg.context().empty()) {
208       anErrorMsg.setContext(aFeatureID + ":" + anAttributeID + ":" + aValidatorID);
209     }
210   }
211
212   if (anErrorMsg.empty() && theValueStateChecked) {
213     anErrorMsg = getValueStateError();
214   }
215
216   if (!anErrorMsg.empty()) {
217     anError = ModuleBase_Tools::translate(anErrorMsg);
218   }
219
220   return anError;
221 }
222
223 //**************************************************************
224 void ModuleBase_ModelWidget::enableFocusProcessing()
225 {
226   QList<QWidget*> aMyControls = getControls();
227   foreach(QWidget*  eachControl, aMyControls) {
228       eachControl->setFocusPolicy(Qt::StrongFocus);
229       eachControl->installEventFilter(this);
230   }
231 }
232
233 //**************************************************************
234 void ModuleBase_ModelWidget::setHighlighted(bool isHighlighted)
235 {
236   QList<QWidget*> aWidgetList = getControls();
237   foreach(QWidget* aWidget, aWidgetList) {
238     QLabel* aLabel = qobject_cast<QLabel*>(aWidget);
239     // We won't set the effect to QLabels - it looks ugly
240     if(aLabel) continue;
241     // If effect is the installed on a different widget, setGraphicsEffect() will
242     // remove the effect from the widget and install it on this widget.
243     // That's why we create a new effect for each widget
244     ModuleBase_Tools::setShadowEffect(aWidget, isHighlighted);
245   }
246 }
247
248 //**************************************************************
249 void ModuleBase_ModelWidget::setFeature(const FeaturePtr& theFeature, const bool theToStoreValue,
250                                         const bool isUpdateFlushed)
251 {
252   /// it is possible to give this flag as parameter in storeValue/storeCustomValue
253   /// after debug, it may be corrected
254   myFlushUpdateBlocked = !isUpdateFlushed;
255   myFeature = theFeature;
256   if (theToStoreValue) {
257     /// it is possible that the attribute is filled before the operation is started,
258     /// e.g. by reentrant operation case some attributes are filled by values of
259     /// feature of previous operation, we should not lost them here
260     if (!theFeature->data()->attribute(attributeID())->isInitialized())
261       storeValue();
262   }
263   myFlushUpdateBlocked = false;
264 }
265
266 //**************************************************************
267 bool ModuleBase_ModelWidget::focusTo()
268 {
269 #ifdef DEBUG_WIDGET_INSTANCE
270   qDebug("ModuleBase_ModelWidget::focusTo");
271 #endif
272   QList<QWidget*> aControls = getControls();
273   QList<QWidget*>::const_iterator anIt = aControls.begin(), aLast = aControls.end();
274   bool isFocusAccepted = false;
275   for (; anIt != aLast && !isFocusAccepted; anIt++) {
276     QWidget* aWidget = *anIt;
277     if (aWidget && aWidget->focusPolicy() != Qt::NoFocus) {
278       ModuleBase_Tools::setFocus(aWidget, "ModuleBase_ModelWidget::focusTo()");
279       isFocusAccepted = true;
280     }
281   }
282   return isFocusAccepted;
283 }
284
285 //**************************************************************
286 void ModuleBase_ModelWidget::activate()
287 {
288 #ifdef DEBUG_WIDGET_INSTANCE
289   qDebug("ModuleBase_ModelWidget::activate");
290 #endif
291   // the control value is stored to the mode by the focus in on the widget
292   // we need the value is initialized in order to enable the apply button in the property panel.
293   // It should happens in the creation mode only because all fields are filled in the edition mode
294   if (!isEditingMode()) {
295     AttributePtr anAttribute = myFeature->data()->attribute(myAttributeID);
296     if (anAttribute.get() != NULL && !anAttribute->isInitialized())
297       initializeValueByActivate();
298   }
299   activateCustom();
300 }
301
302 //**************************************************************
303 void ModuleBase_ModelWidget::deactivate()
304 {
305 #ifdef DEBUG_WIDGET_INSTANCE
306   qDebug("ModuleBase_ModelWidget::deactivate");
307 #endif
308   myIsValueStateBlocked = false;
309   myState = Stored;
310   if (myWidgetValidator)
311     myWidgetValidator->clearValidatedCash();
312 }
313
314 //**************************************************************
315 void ModuleBase_ModelWidget::initializeValueByActivate()
316 {
317   if (isComputedDefault()) {
318     if (myFeature->compute(myAttributeID)) {
319       restoreValue();
320     }
321   }
322   else {
323     storeValue();
324   }
325 }
326
327 //**************************************************************
328 QWidget* ModuleBase_ModelWidget::getControlAcceptingFocus(const bool isFirst)
329 {
330   QWidget* aControl = 0;
331
332   QList<QWidget*> aControls = getControls();
333   int aSize = aControls.size();
334
335   if (isFirst) {
336     for (int i = 0; i < aSize && !aControl; i++)  {
337       if (aControls[i]->focusPolicy() != Qt::NoFocus)
338         aControl = aControls[i];
339     }
340   }
341   else {
342     for (int i = aSize - 1; i >= 0 && !aControl; i--)  {
343       if (aControls[i]->focusPolicy() != Qt::NoFocus)
344         aControl = aControls[i];
345     }
346   }
347   return aControl;
348 }
349
350 //**************************************************************
351 void ModuleBase_ModelWidget::setDefaultValue(const std::string& theValue)
352 {
353   myDefaultValue = theValue;
354 }
355
356 //**************************************************************
357 bool ModuleBase_ModelWidget::storeValue()
358 {
359   setValueState(Stored);
360
361   emit beforeValuesChanged();
362   bool isDone = false;
363   // value is stored only in creation mode and in edition if there is not
364   // XML flag prohibited modification in edit mode(macro feature circle/arc)
365   if (!isEditingMode() || isModifiedInEdit().empty())
366     isDone = storeValueCustom();
367   else {
368     /// store value in an alternative attribute if possible(attribute has the same type)
369     std::string aWidgetAttribute = attributeID();
370     myAttributeID = isModifiedInEdit();
371     storeValueCustom();
372     myAttributeID = aWidgetAttribute;
373     // operation will be restarted but if isDone == true, PagedContainer will try to set focus
374     // to the current widget, but will be already deleted
375     isDone = false;
376   }
377
378   emit afterValuesChanged();
379
380   return isDone;
381 }
382 #ifdef DEBUG_VALUE_STATE
383
384 //**************************************************************
385 std::string getDebugInfo(const ModuleBase_ModelWidget::ValueState& theState)
386 {
387   std::string anInfo;
388   switch (theState) {
389     case ModuleBase_ModelWidget::Stored:           anInfo = "Stored          "; break;
390     case ModuleBase_ModelWidget::ModifiedInPP:     anInfo = "ModifiedInPP    "; break;
391     case ModuleBase_ModelWidget::ModifiedInViewer: anInfo = "ModifiedInViewer"; break;
392     case ModuleBase_ModelWidget::Reset:            anInfo = "Reset           "; break;
393     default: break;
394   }
395   return anInfo;
396 }
397 #endif
398
399 //**************************************************************
400 ModuleBase_ModelWidget::ValueState ModuleBase_ModelWidget::setValueState
401                                          (const ModuleBase_ModelWidget::ValueState& theState)
402 {
403   ValueState aState = myState;
404
405   if (myState != theState && !myIsValueStateBlocked) {
406 #ifdef DEBUG_VALUE_STATE
407     qDebug(QString("setValueState: previous state = %1,\t new state = %2")
408            .arg(getDebugInfo(myState).c_str())
409            .arg(getDebugInfo(theState).c_str()).toStdString().c_str());
410 #endif
411     myState = theState;
412     emit valueStateChanged(aState);
413   }
414   return aState;
415 }
416
417 //**************************************************************
418 bool ModuleBase_ModelWidget::blockValueState(const bool theBlocked)
419 {
420   bool isBlocked = myIsValueStateBlocked;
421   myIsValueStateBlocked = theBlocked;
422   return isBlocked;
423 }
424
425 //**************************************************************
426 bool ModuleBase_ModelWidget::restoreValue()
427 {
428   if (!isEnabled()) {
429     // This code works in inspection panel
430     ModelAPI_ValidatorsFactory* aValidators = ModelAPI_Session::get()->validators();
431     if (!aValidators->isCase(myFeature, attributeID()))
432       return false; // if it is not an active case for the widget
433   }
434   emit beforeValuesRestored();
435   bool isDone = restoreValueCustom();
436   emit afterValuesRestored();
437
438   return isDone;
439 }
440
441 //**************************************************************
442 void ModuleBase_ModelWidget::updateObject(ObjectPtr theObject)
443 {
444   if (!myFlushUpdateBlocked) {
445 #ifdef DEBUG_WIDGET_INSTANCE
446     qDebug("ModuleBase_ModelWidget::updateObject");
447 #endif
448     if (myFeature.get() && myUpdateVisualAttributes) {
449       static const Events_ID anEvent = Events_Loop::eventByName(EVENT_VISUAL_ATTRIBUTES);
450       ModelAPI_EventCreator::get()->sendUpdated(myFeature, anEvent);
451     }
452     ModuleBase_Tools::flushUpdated(theObject);
453     emit objectUpdated();
454   }
455 }
456
457 //**************************************************************
458 bool ModuleBase_ModelWidget::canProcessAction(ModuleBase_ActionType theActionType,
459                                               bool& isActionEnabled)
460 {
461   isActionEnabled = false;
462   switch (theActionType) {
463     case ActionEnter: return false;
464     case ActionEscape: return false;
465     case ActionDelete: return true;
466     case ActionSelection: return true;
467     case ActionUndo:
468     case ActionRedo:
469     default:
470       return false;
471   }
472 }
473
474 //**************************************************************
475 bool ModuleBase_ModelWidget::processAction(ModuleBase_ActionType theActionType,
476                                            const ActionParamPtr& theParam)
477 {
478   switch (theActionType) {
479     case ActionEnter:
480       return processEnter();
481     case ActionEscape:
482       return processEscape();
483     case ActionDelete:
484       return processDelete();
485     case ActionSelection:
486       processSelection();
487     case ActionUndo:
488     case ActionRedo:
489     default:
490       return false;
491   }
492 }
493
494 //**************************************************************
495 bool ModuleBase_ModelWidget::processEnter()
496 {
497   return false;
498 }
499
500 //**************************************************************
501 bool ModuleBase_ModelWidget::processEscape()
502 {
503   return false;
504 }
505
506 //**************************************************************
507 bool ModuleBase_ModelWidget::processDelete()
508 {
509   // we consider that model objects eats delete key in order to
510   // do nothing by for example symbol delete in line edit or spin box
511   return true;
512 }
513
514 //**************************************************************
515 bool ModuleBase_ModelWidget::processSelection()
516 {
517   return false;
518 }
519
520 //**************************************************************
521 bool ModuleBase_ModelWidget::eventFilter(QObject* theObject, QEvent *theEvent)
522 {
523   QWidget* aWidget = qobject_cast<QWidget*>(theObject);
524   if (theEvent->type() == QEvent::FocusIn) {
525     QFocusEvent* aFocusEvent = dynamic_cast<QFocusEvent*>(theEvent);
526     Qt::FocusReason aReason = aFocusEvent->reason();
527     bool aMouseOrKey = aReason == Qt::MouseFocusReason ||
528                         /*aReason == Qt::TabFocusReason ||
529                         //aReason == Qt::BacktabFocusReason ||*/
530                         aReason == Qt::OtherFocusReason; // to process widget->setFocus()
531     if (aMouseOrKey && getControls().contains(aWidget)) {
532     //if (getControls().contains(aWidget)) {
533       emitFocusInWidget();
534     }
535   }
536   else if (theEvent->type() == QEvent::FocusOut) {
537     QFocusEvent* aFocusEvent = dynamic_cast<QFocusEvent*>(theEvent);
538
539     Qt::FocusReason aReason = aFocusEvent->reason();
540     bool aMouseOrKey = aReason == Qt::MouseFocusReason ||
541                         aReason == Qt::TabFocusReason ||
542                         aReason == Qt::BacktabFocusReason ||
543                         aReason == Qt::OtherFocusReason; // to process widget->setFocus()
544     if (aMouseOrKey && getControls().contains(aWidget)) {
545       if (getValueState() == ModifiedInPP) {
546         storeValue();
547       }
548     }
549   }
550   // pass the event on to the parent class
551
552   return QObject::eventFilter(theObject, theEvent);
553 }
554
555 //**************************************************************
556 void ModuleBase_ModelWidget::onWidgetValuesChanged()
557 {
558   storeValue();
559 }
560
561 //**************************************************************
562 void ModuleBase_ModelWidget::onWidgetValuesModified()
563 {
564   setValueState(ModifiedInPP);
565 }
566
567 //**************************************************************
568 QString ModuleBase_ModelWidget::translate(const std::string& theStr) const
569 {
570   return ModuleBase_Tools::translate(context(), theStr);
571 }
572
573 //**************************************************************
574 ModuleBase_ModelWidget* ModuleBase_ModelWidget::findModelWidget(ModuleBase_IPropertyPanel* theProp,
575                                                                 QWidget* theWidget)
576 {
577   ModuleBase_ModelWidget* aModelWidget = 0;
578   if (!theWidget)
579     return aModelWidget;
580
581   QObject* aParent = theWidget->parent();
582   while (aParent) {
583     aModelWidget = qobject_cast<ModuleBase_ModelWidget*>(aParent);
584     if (aModelWidget)
585       break;
586     aParent = aParent->parent();
587   }
588   return aModelWidget;
589 }