Salome HOME
Fix for missed values in DoubleSpinBox on edit
[modules/shaper.git] / src / ModuleBase / ModuleBase_ParamSpinBox.cpp
1 #include "ModuleBase_ParamSpinBox.h"
2
3 #include <ModelAPI_Session.h>
4 #include <ModelAPI_Document.h>
5 #include <ModelAPI_ResultParameter.h>
6 #include <ModelAPI_AttributeDouble.h>
7
8 #include <QKeyEvent>
9 #include <QLineEdit>
10 #include <QToolTip>
11 #include <QRegExp>
12
13 #include <string>
14
15 /*!
16  \class ModuleBase_ParamSpinBox
17  */
18
19 /*!
20  \brief Constructor.
21
22  Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value,
23  a step value of 1.0 and a precision of 2 decimal places.
24  The value is initially set to 0.00.
25
26  \param parent parent object
27  */
28 ModuleBase_ParamSpinBox::ModuleBase_ParamSpinBox(QWidget* theParent, int thePrecision)
29     : ModuleBase_DoubleSpinBox(theParent, thePrecision),
30       myAcceptVariables(true)
31 {
32   connectSignalsAndSlots();
33 }
34
35 /*!
36  \brief Destructor.
37  */
38 ModuleBase_ParamSpinBox::~ModuleBase_ParamSpinBox()
39 {
40 }
41
42 /*!
43  \brief Perform \a steps increment/decrement steps.
44
45  Re-implemented to handle cases when Notebook variable
46  name is specified by the user as the widget text.
47  Otherwise, simply calls the base implementation.
48
49  \param steps number of increment/decrement steps
50  */
51 void ModuleBase_ParamSpinBox::stepBy(int steps)
52 {
53   if (hasVariable())
54     return;
55
56   ModuleBase_DoubleSpinBox::stepBy(steps);
57 }
58
59 /*!
60  \brief Connect signals and slots.
61  */
62 void ModuleBase_ParamSpinBox::connectSignalsAndSlots()
63 {
64   connect(this, SIGNAL(editingFinished()),
65           this, SLOT(onEditingFinished()));
66
67   connect(this, SIGNAL(valueChanged(const QString&)),
68           this, SLOT(onTextChanged(const QString&)));
69
70   //connect(lineEdit(), SIGNAL(textChanged(const QString&)),
71   //        this,       SLOT(onTextChanged(const QString&)));
72
73   //connect(lineEdit(), SIGNAL(textChanged(const QString&)),
74   //        this,       SIGNAL(textChanged(const QString&)));
75 }
76
77 /*!
78  \brief This function is called when editing is finished.
79  */
80 void ModuleBase_ParamSpinBox::onEditingFinished()
81 {
82   if (myTextValue.isNull())
83     myTextValue = text();
84
85   setText(myTextValue);
86 }
87
88 /*!
89  \brief This function is called when value is changed.
90  */
91 void ModuleBase_ParamSpinBox::onTextChanged(const QString& text)
92 {
93   myTextValue = text;
94
95   double value = 0;
96   if (isValid(text, value) == Acceptable) {
97     myCorrectValue = text;
98   }
99 }
100
101 /*!
102  \brief Interpret text entered by the user as a value.
103  \param text text entered by the user
104  \return mapped value
105  \sa textFromValue()
106  */
107 double ModuleBase_ParamSpinBox::valueFromText(const QString& theText) const
108 {
109   if (!hasVariable(theText))
110     return ModuleBase_DoubleSpinBox::valueFromText(theText);
111
112   double aValue = 0;
113   findVariable(theText, aValue);
114   return aValue;
115 }
116
117 /*!
118  \brief This function is used to determine whether input is valid.
119  \param str currently entered value
120  \param pos cursor position in the string
121  \return validating operation result
122  */
123 QValidator::State ModuleBase_ParamSpinBox::validate(QString& str, int& pos) const
124 {
125   // Trying to interpret the current input text as a numeric value
126   if (!hasVariable(str))
127     return ModuleBase_DoubleSpinBox::validate(str, pos);
128
129   QValidator::State res = QValidator::Invalid;
130
131   // Considering the input text as a variable name
132   // Applying Python identifier syntax:
133   // either a string starting with a letter, or a string starting with
134   // an underscore followed by at least one alphanumeric character
135   if (isAcceptVariables()) {
136     QRegExp varNameMask("[_a-zA-Z][a-zA-Z0-9_]*");
137     if (varNameMask.exactMatch(str))
138       res = QValidator::Acceptable;
139
140     if (res == QValidator::Invalid) {
141       varNameMask.setPattern("_");
142       if (varNameMask.exactMatch(str))
143         res = QValidator::Intermediate;
144     }
145   }
146   return res;
147 }
148
149 /*!
150  \brief This function is used to set a current value for this spinbox.
151  \param value current value
152  */
153 void ModuleBase_ParamSpinBox::setValue(const double value)
154 {
155   ModuleBase_DoubleSpinBox::setValue(value);
156
157   myCorrectValue = ModuleBase_DoubleSpinBox::textFromValue(value);
158   myTextValue = myCorrectValue;
159 }
160
161 /*!
162  \brief This function is used to set a text for this spinbox.
163  \param value current value
164  */
165 void ModuleBase_ParamSpinBox::setText(const QString& value)
166 {
167   lineEdit()->setText(value);
168 }
169
170 /*!
171  \brief Enables or disables variable names in the spin box.
172  By default, variable names are enabled.
173  \param flag If true, variable names are enabled.
174  */
175 void ModuleBase_ParamSpinBox::setAcceptVariables(const bool flag)
176 {
177   myAcceptVariables = flag;
178 }
179
180 /*!
181  \brief Returns true if the spin box accepts variable names.
182  */
183 bool ModuleBase_ParamSpinBox::isAcceptVariables() const
184 {
185   return myAcceptVariables;
186 }
187
188 bool ModuleBase_ParamSpinBox::hasVariable() const
189 {
190   return hasVariable(text());
191 }
192
193 bool ModuleBase_ParamSpinBox::hasVariable(const QString& theText) const
194 {
195   QRegExp varNameMask("([a-z]|[A-Z]|_).*");
196   return varNameMask.exactMatch(theText);
197 }
198
199 /*!
200  \brief This function is used to determine whether input is valid.
201  \return validating operation result
202  */
203 ModuleBase_ParamSpinBox::State ModuleBase_ParamSpinBox::isValid(const QString& theText,
204                                                                 double& theValue) const
205 {
206   if (hasVariable() && !findVariable(theText, theValue)) {
207     bool ok = false;
208     theValue = locale().toDouble(theText, &ok);
209     if (!ok) {
210       return NoVariable;
211     }
212   }
213   if (!checkRange(theValue)) {
214     return Invalid;
215   }
216
217   return Acceptable;
218 }
219
220 /*!
221  \brief This function is used to check that string value lies within predefined range.
222  \return check status
223  */
224 bool ModuleBase_ParamSpinBox::checkRange(const double theValue) const
225 {
226   return theValue >= minimum() && theValue <= maximum();
227 }
228
229 /*!
230  \brief This function is used to determine whether input is a variable name and to get its value.
231  \return status of search operation
232  */
233 bool ModuleBase_ParamSpinBox::findVariable(const QString& theName,
234                                            double& outValue) const
235 {
236
237   SessionPtr aSession = ModelAPI_Session::get();
238   DocumentPtr aDocument = aSession->activeDocument();
239   ObjectPtr aParamObj = aDocument->objectByName(ModelAPI_ResultParameter::group(),
240                                                 theName.toStdString());
241   ResultParameterPtr aParam = std::dynamic_pointer_cast<ModelAPI_ResultParameter>(aParamObj);
242   if(!aParam.get())
243     return false;
244   AttributeDoublePtr aValueAttribute = aParam->data()->real(ModelAPI_ResultParameter::VALUE());
245   outValue = aValueAttribute->value();
246   return true;
247 }
248
249 /*!
250  \brief This function is called when the spinbox recieves key press event.
251  */
252 void ModuleBase_ParamSpinBox::keyPressEvent(QKeyEvent* e)
253 {
254   if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
255     QWidget::keyPressEvent(e);
256   } else {
257     ModuleBase_DoubleSpinBox::keyPressEvent(e);
258   }
259 }
260
261 /*!
262  \brief This function is called when the spinbox recieves show event.
263  */
264 void ModuleBase_ParamSpinBox::showEvent(QShowEvent* theEvent)
265 {
266   ModuleBase_DoubleSpinBox::showEvent(theEvent);
267   //setText(myTextValue);
268 }