1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
3 // File: ModuleBase_DoubleSpinBox.cxx
4 // Author: Sergey TELKOV
6 #include "ModuleBase_DoubleSpinBox.h"
9 #include <QDoubleValidator>
15 const double PSEUDO_ZERO = 1.e-20;
18 \class ModuleBase_DoubleSpinBox
19 \brief Enhanced version of the Qt's double spin box.
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.
26 To set "cleared" state use setCleared() method. To check if the spin
27 box stores "cleared" state, use isCleared() method.
30 if (myDblSpinBox->isCleared()) {
31 ... // process "None" state
34 double value = myDblSpinBox->value();
35 ... // process entered value
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.
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
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.
56 \param theParent parent object
57 \param thePrecision precision of values input
59 ModuleBase_DoubleSpinBox::ModuleBase_DoubleSpinBox(QWidget* theParent, int thePrecision)
60 : QDoubleSpinBox(theParent),
62 //myIsModified(false),
63 myIsEmitKeyPressEvent(false)
65 // VSR 01/07/2010: Disable thousands separator for spin box
66 // (to avoid inconsistency of double-2-string and string-2-double conversion)
68 loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
71 // MPV 15/09/2014: this must be set before setDecimals; otherwise in release mode setDecimals may crash
72 myPrecision = thePrecision;
74 // Use precision equal to default Qt decimals
75 // it's necessary to set decimals before the range setting,
76 // by default Qt rounds boundaries to 2 decimals at setRange
77 setDecimals(qAbs(myPrecision));
79 connect(lineEdit(), SIGNAL(textChanged( const QString& )), this,
80 SLOT(onTextChanged( const QString& )));
82 //connect(this, SIGNAL(valueChanged(const QString&)), this, SLOT(onValueChanged(const QString&)));
88 ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox()
93 \brief Check if spin box is in the "cleared" state.
94 \return \c true if spin box is cleared
97 bool ModuleBase_DoubleSpinBox::isCleared() const
103 \brief Change "cleared" status of the spin box.
104 \param on new "cleared" status
107 void ModuleBase_DoubleSpinBox::setCleared(const bool on)
113 setSpecialValueText(specialValueText());
117 \brief Set precision of the spin box
119 If precision value is less than 0, the 'g' format is used for value output,
120 otherwise 'f' format is used.
122 \param prec new precision value.
125 void ModuleBase_DoubleSpinBox::setPrecision(const int prec)
127 int newPrec = qAbs(prec);
128 int oldPrec = qAbs(myPrecision);
130 if (newPrec != oldPrec)
135 \brief Get precision value of the spin box
136 \return current precision value
139 int ModuleBase_DoubleSpinBox::getPrecision() const
145 \brief Interpret text entered by the user as a value.
146 \param text text entered by the user
150 double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const
153 return text.toDouble();
155 return QDoubleSpinBox::valueFromText(text);
159 \brief This function is used by the spin box whenever it needs to display
162 \param val spin box value
163 \return text representation of the value
166 QString ModuleBase_DoubleSpinBox::textFromValue(double val) const
168 QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision));
169 return removeTrailingZeroes(s);
173 \brief Return source string with removed leading and trailing zeros.
174 \param src source string
175 \return resulting string
177 QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const
179 QString delim(locale().decimalPoint());
181 int idx = src.lastIndexOf(delim);
185 QString iPart = src.left(idx);
186 QString fPart = src.mid(idx + 1);
188 int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+"));
190 ePart = fPart.mid(idx1);
191 fPart = fPart.left(idx1);
194 fPart.remove(QRegExp("0+$"));
197 if (!fPart.isEmpty())
198 res += delim + fPart;
204 void ModuleBase_DoubleSpinBox::keyPressEvent(QKeyEvent* theEvent)
206 bool isEmitKeyRelease = false;
207 switch (theEvent->key()) {
209 case Qt::Key_Return: {
210 // do not react to the Enter key, the property panel processes it
211 if (!myIsEmitKeyPressEvent)
214 isEmitKeyRelease = true;
220 QDoubleSpinBox::keyPressEvent(theEvent);
222 if (isEmitKeyRelease)
226 void ModuleBase_DoubleSpinBox::keyReleaseEvent(QKeyEvent* theEvent)
228 switch (theEvent->key()) {
230 case Qt::Key_Return: {
231 // the enter has already been processed when key is pressed,
232 // key release should not be processed in operation manager
233 if (myIsEmitKeyPressEvent) {
235 emit enterReleased();
243 QDoubleSpinBox::keyReleaseEvent(theEvent);
247 \brief Perform \a steps increment/decrement steps.
249 The \a steps value can be any integer number. If it is > 0,
250 the value incrementing is done, otherwise value is decremented
253 \param steps number of increment/decrement steps
255 void ModuleBase_DoubleSpinBox::stepBy(int steps)
259 QDoubleSpinBox::stepBy(steps);
260 double tmpval = value();
261 if (qAbs(tmpval) < PSEUDO_ZERO)
263 if (tmpval < minimum())
265 else if (tmpval > maximum())
271 \brief This function is used to determine whether input is valid.
272 \param str currently entered value
273 \param pos cursor position in the string
274 \return validating operation result
276 QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const
278 QString pref = this->prefix();
279 QString suff = this->suffix();
280 uint overhead = pref.length() + suff.length();
281 QValidator::State state = QValidator::Invalid;
283 QDoubleValidator v(NULL);
285 // If 'g' format is used (myPrecision < 0), then
286 // myPrecision - 1 digits are allowed after the decimal point.
287 // Otherwise, expect myPrecision digits after the decimal point.
288 int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision;
290 v.setLocale(this->locale());
292 v.setBottom(minimum());
295 myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation);
298 state = v.validate(str, pos);
300 if ((uint)(str.length()) >= overhead && str.startsWith(pref) && str.right(suff.length()) == suff) {
301 QString core = str.mid(pref.length(), str.length() - overhead);
302 int corePos = pos - pref.length();
303 state = v.validate(core, corePos);
304 pos = corePos + pref.length();
305 str.replace(pref.length(), str.length() - overhead, core);
307 state = v.validate(str, pos);
308 if (state == QValidator::Invalid) {
309 QString special = this->specialValueText().trimmed();
310 QString candidate = str.trimmed();
311 if (special.startsWith(candidate)) {
312 if (candidate.length() == special.length())
313 state = QValidator::Acceptable;
315 state = QValidator::Intermediate;
321 // Treat values outside (min; max) range as Invalid
322 // This check is enabled by assigning "strict_validity_check" dynamic property
323 // with value "true" to the spin box instance.
324 if (state == QValidator::Intermediate) {
326 double val = str.toDouble(&isOk);
328 QVariant propVal = property("strict_validity_check");
329 if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) {
330 if (val < minimum() || val > maximum())
331 state = QValidator::Invalid;
333 } else if (myPrecision < 0) {
334 // Consider too large negative exponent as Invalid
335 QChar e(locale().exponential());
336 int epos = str.indexOf(e, 0, Qt::CaseInsensitive);
338 epos++; // Skip exponential symbol itself
339 QString exponent = str.right(str.length() - epos);
340 int expValue = exponent.toInt(&isOk);
341 if (isOk && expValue < std::numeric_limits<double>::min_exponent10)
342 state = QValidator::Invalid;
351 \brief Called when user enters the text in the spin box.
353 void ModuleBase_DoubleSpinBox::onTextChanged(const QString& )
356 //myIsModified = true;
359 /*void ModuleBase_DoubleSpinBox::onValueChanged(const QString& theValue)
364 bool ModuleBase_DoubleSpinBox::isModified() const
369 void ModuleBase_DoubleSpinBox::clearModified()
371 myIsModified = false;
374 bool ModuleBase_DoubleSpinBox::enableKeyPressEvent(const bool& theEnable)
376 bool aPreviousValue = myIsEmitKeyPressEvent;
377 myIsEmitKeyPressEvent = theEnable;
379 return aPreviousValue;