Salome HOME
It removes commented not used code.
[modules/shaper.git] / src / ModuleBase / ModuleBase_DoubleSpinBox.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:      ModuleBase_DoubleSpinBox.cxx
4 // Author:    Sergey TELKOV
5 //
6 #include "ModuleBase_DoubleSpinBox.h"
7
8 #include <QLineEdit>
9 #include <QDoubleValidator>
10 #include <QVariant>
11 #include <QKeyEvent>
12
13 #include <limits>
14
15 const double PSEUDO_ZERO = 1.e-20;
16
17 /*!
18  \class ModuleBase_DoubleSpinBox
19  \brief Enhanced version of the Qt's double spin box.
20
21  The ModuleBase_DoubleSpinBox class represents the widget for entering the
22  floating point values. In addition to the functionality provided by
23  QDoubleSpinBox, this class supports "cleared" state - this is the
24  state corresponding to "None" (or empty) entered value.
25
26  To set "cleared" state use setCleared() method. To check if the spin
27  box stores "cleared" state, use isCleared() method.
28  For example:
29  \code
30  if (myDblSpinBox->isCleared()) {
31  ... // process "None" state
32  }
33  else {
34  double value = myDblSpinBox->value();
35  ... // process entered value
36  }
37  \endcode
38
39  Another useful feature is possibility to use scientific notation (e.g. 1.234e+18)
40  for the widget text. To enable this, negative precision should be specified either
41  through a constructor or using setPrecision() method.
42
43  Note that "decimals" property of QDoubleSpinBox is almost completely substituted
44  by "myPrecision" field of ModuleBase_DoubleSpinBox class. "decimals" is still used
45  for proper size hint calculation and for rounding minimum and maximum bounds of
46  the spin box range.
47  */
48
49 /*!
50  \brief Constructor.
51
52  Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value,
53  a step value of 1.0 and a precision of 2 decimal places.
54  The value is initially set to 0.00.
55
56  \param theParent parent object
57  \param thePrecision precision of values input
58  */
59 ModuleBase_DoubleSpinBox::ModuleBase_DoubleSpinBox(QWidget* theParent, int thePrecision)
60     : QDoubleSpinBox(theParent),
61       myCleared(false),
62       myIsEmitKeyPressEvent(false)
63 {
64   // VSR 01/07/2010: Disable thousands separator for spin box
65   // (to avoid inconsistency of double-2-string and string-2-double conversion)
66   QLocale loc;
67   loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
68   setLocale(loc);
69
70   // MPV 15/09/2014: this must be set before setDecimals; otherwise in release mode setDecimals may crash
71   myPrecision = thePrecision;
72
73   // Use precision equal to default Qt decimals
74   // it's necessary to set decimals before the range setting,
75   // by default Qt rounds boundaries to 2 decimals at setRange
76   setDecimals(qAbs(myPrecision));
77
78   connect(lineEdit(), SIGNAL(textChanged( const QString& )), this,
79           SLOT(onTextChanged( const QString& )));
80 }
81
82 /*!
83  \brief Destructor.
84  */
85 ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox()
86 {
87 }
88
89 /*!
90  \brief Check if spin box is in the "cleared" state.
91  \return \c true if spin box is cleared
92  \sa setCleared()
93  */
94 bool ModuleBase_DoubleSpinBox::isCleared() const
95 {
96   return myCleared;
97 }
98
99 /*!
100  \brief Change "cleared" status of the spin box.
101  \param on new "cleared" status
102  \sa isCleared()
103  */
104 void ModuleBase_DoubleSpinBox::setCleared(const bool on)
105 {
106   if (myCleared == on)
107     return;
108
109   myCleared = on;
110   setSpecialValueText(specialValueText());
111 }
112
113 /*!
114  \brief Set precision of the spin box
115
116  If precision value is less than 0, the 'g' format is used for value output,
117  otherwise 'f' format is used.
118
119  \param prec new precision value.
120  \sa precision()
121  */
122 void ModuleBase_DoubleSpinBox::setPrecision(const int prec)
123 {
124   int newPrec = qAbs(prec);
125   int oldPrec = qAbs(myPrecision);
126   myPrecision = prec;
127   if (newPrec != oldPrec)
128     update();
129 }
130
131 /*!
132  \brief Get precision value of the spin box
133  \return current precision value
134  \sa setPrecision()
135  */
136 int ModuleBase_DoubleSpinBox::getPrecision() const
137 {
138   return myPrecision;
139 }
140
141 /*!
142  \brief Interpret text entered by the user as a value.
143  \param text text entered by the user
144  \return mapped value
145  \sa textFromValue()
146  */
147 double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const
148 {
149   if (myPrecision < 0)
150     return text.toDouble();
151
152   return QDoubleSpinBox::valueFromText(text);
153 }
154
155 /*!
156  \brief This function is used by the spin box whenever it needs to display
157  the given value.
158
159  \param val spin box value
160  \return text representation of the value
161  \sa valueFromText()
162  */
163 QString ModuleBase_DoubleSpinBox::textFromValue(double val) const
164 {
165   QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision));
166   return removeTrailingZeroes(s);
167 }
168
169 /*!
170  \brief Return source string with removed leading and trailing zeros.
171  \param src source string
172  \return resulting string
173  */
174 QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const
175 {
176   QString delim(locale().decimalPoint());
177
178   int idx = src.lastIndexOf(delim);
179   if (idx == -1)
180     return src;
181
182   QString iPart = src.left(idx);
183   QString fPart = src.mid(idx + 1);
184   QString ePart = "";
185   int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+"));
186   if (idx1 >= 0) {
187     ePart = fPart.mid(idx1);
188     fPart = fPart.left(idx1);
189   }
190
191   fPart.remove(QRegExp("0+$"));
192
193   QString res = iPart;
194   if (!fPart.isEmpty())
195     res += delim + fPart;
196   res += ePart;
197
198   return res;
199 }
200
201 void ModuleBase_DoubleSpinBox::keyPressEvent(QKeyEvent* theEvent)
202 {
203   bool isEmitKeyRelease = false;
204   switch (theEvent->key()) {
205     case Qt::Key_Enter:
206     case Qt::Key_Return: {
207       // do not react to the Enter key, the property panel processes it
208       if (!myIsEmitKeyPressEvent)
209         return;
210       else
211         isEmitKeyRelease = true;
212     }
213     break;
214     default:
215       break;
216   }
217   QDoubleSpinBox::keyPressEvent(theEvent);
218
219   if (isEmitKeyRelease)
220     emit enterPressed();
221 }
222
223 void ModuleBase_DoubleSpinBox::keyReleaseEvent(QKeyEvent* theEvent)
224 {
225   switch (theEvent->key()) {
226     case Qt::Key_Enter:
227     case Qt::Key_Return: {
228       // the enter has already been processed when key is pressed,
229       // key release should not be processed in operation manager
230       if (myIsEmitKeyPressEvent) {
231         theEvent->accept();
232         emit enterReleased();
233         return;
234       }
235     }
236     break;
237     default:
238       break;
239   }
240   QDoubleSpinBox::keyReleaseEvent(theEvent);
241 }
242
243 /*!
244  \brief Perform \a steps increment/decrement steps.
245
246  The \a steps value can be any integer number. If it is > 0,
247  the value incrementing is done, otherwise value is decremented
248  \a steps times.
249
250  \param steps number of increment/decrement steps
251  */
252 void ModuleBase_DoubleSpinBox::stepBy(int steps)
253 {
254   myCleared = false;
255
256   QDoubleSpinBox::stepBy(steps);
257   double tmpval = value();
258   if (qAbs(tmpval) < PSEUDO_ZERO)
259     tmpval = 0.;
260   if (tmpval < minimum())
261     tmpval = minimum();
262   else if (tmpval > maximum())
263     tmpval = maximum();
264   setValue(tmpval);
265 }
266
267 /*!
268  \brief This function is used to determine whether input is valid.
269  \param str currently entered value
270  \param pos cursor position in the string
271  \return validating operation result
272  */
273 QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const
274 {
275   QString pref = this->prefix();
276   QString suff = this->suffix();
277   uint overhead = pref.length() + suff.length();
278   QValidator::State state = QValidator::Invalid;
279
280   QDoubleValidator v(NULL);
281
282   // If 'g' format is used (myPrecision < 0), then
283   // myPrecision - 1 digits are allowed after the decimal point.
284   // Otherwise, expect myPrecision digits after the decimal point.
285   int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision;
286
287   v.setLocale(this->locale());
288   v.setDecimals(decs);
289   v.setBottom(minimum());
290   v.setTop(maximum());
291   v.setNotation(
292       myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation);
293
294   if (overhead == 0)
295     state = v.validate(str, pos);
296   else {
297     if ((uint)(str.length()) >= overhead && str.startsWith(pref) && str.right(suff.length()) == suff) {
298       QString core = str.mid(pref.length(), str.length() - overhead);
299       int corePos = pos - pref.length();
300       state = v.validate(core, corePos);
301       pos = corePos + pref.length();
302       str.replace(pref.length(), str.length() - overhead, core);
303     } else {
304       state = v.validate(str, pos);
305       if (state == QValidator::Invalid) {
306         QString special = this->specialValueText().trimmed();
307         QString candidate = str.trimmed();
308         if (special.startsWith(candidate)) {
309           if (candidate.length() == special.length())
310             state = QValidator::Acceptable;
311           else
312             state = QValidator::Intermediate;
313         }
314       }
315     }
316   }
317
318   // Treat values outside (min; max) range as Invalid
319   // This check is enabled by assigning "strict_validity_check" dynamic property
320   // with value "true" to the spin box instance.
321   if (state == QValidator::Intermediate) {
322     bool isOk;
323     double val = str.toDouble(&isOk);
324     if (isOk) {
325       QVariant propVal = property("strict_validity_check");
326       if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) {
327         if (val < minimum() || val > maximum())
328           state = QValidator::Invalid;
329       }
330     } else if (myPrecision < 0) {
331       // Consider too large negative exponent as Invalid
332       QChar e(locale().exponential());
333       int epos = str.indexOf(e, 0, Qt::CaseInsensitive);
334       if (epos != -1) {
335         epos++;  // Skip exponential symbol itself
336         QString exponent = str.right(str.length() - epos);
337         int expValue = exponent.toInt(&isOk);
338         if (isOk && expValue < std::numeric_limits<double>::min_exponent10)
339           state = QValidator::Invalid;
340       }
341     }
342   }
343
344   return state;
345 }
346
347 /*!
348  \brief Called when user enters the text in the spin box.
349  */
350 void ModuleBase_DoubleSpinBox::onTextChanged(const QString& )
351 {
352   myCleared = false;
353 }
354
355 bool ModuleBase_DoubleSpinBox::enableKeyPressEvent(const bool& theEnable)
356 {
357   bool aPreviousValue = myIsEmitKeyPressEvent;
358   myIsEmitKeyPressEvent = theEnable;
359
360   return aPreviousValue;
361 }