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),
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&)));
83 //connect(this, SIGNAL(editingFinished()), this, SLOT(onEditingFinished()));
89 ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox()
94 \brief Check if spin box is in the "cleared" state.
95 \return \c true if spin box is cleared
98 bool ModuleBase_DoubleSpinBox::isCleared() const
104 \brief Change "cleared" status of the spin box.
105 \param on new "cleared" status
108 void ModuleBase_DoubleSpinBox::setCleared(const bool on)
114 setSpecialValueText(specialValueText());
118 \brief Set precision of the spin box
120 If precision value is less than 0, the 'g' format is used for value output,
121 otherwise 'f' format is used.
123 \param prec new precision value.
126 void ModuleBase_DoubleSpinBox::setPrecision(const int prec)
128 int newPrec = qAbs(prec);
129 int oldPrec = qAbs(myPrecision);
131 if (newPrec != oldPrec)
136 \brief Get precision value of the spin box
137 \return current precision value
140 int ModuleBase_DoubleSpinBox::getPrecision() const
146 \brief Interpret text entered by the user as a value.
147 \param text text entered by the user
151 double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const
154 return text.toDouble();
156 return QDoubleSpinBox::valueFromText(text);
160 \brief This function is used by the spin box whenever it needs to display
163 \param val spin box value
164 \return text representation of the value
167 QString ModuleBase_DoubleSpinBox::textFromValue(double val) const
169 QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision));
170 return removeTrailingZeroes(s);
174 \brief Return source string with removed leading and trailing zeros.
175 \param src source string
176 \return resulting string
178 QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const
180 QString delim(locale().decimalPoint());
182 int idx = src.lastIndexOf(delim);
186 QString iPart = src.left(idx);
187 QString fPart = src.mid(idx + 1);
189 int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+"));
191 ePart = fPart.mid(idx1);
192 fPart = fPart.left(idx1);
195 fPart.remove(QRegExp("0+$"));
198 if (!fPart.isEmpty())
199 res += delim + fPart;
205 void ModuleBase_DoubleSpinBox::keyPressEvent(QKeyEvent *theEvent)
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)
218 QDoubleSpinBox::keyPressEvent(theEvent);
221 bool ModuleBase_DoubleSpinBox::focusNextPrevChild(bool theIsNext)
223 myIsModified = false;
226 emit focusNextPrev();
227 return QDoubleSpinBox::focusNextPrevChild(theIsNext);
231 \brief Perform \a steps increment/decrement steps.
233 The \a steps value can be any integer number. If it is > 0,
234 the value incrementing is done, otherwise value is decremented
237 \param steps number of increment/decrement steps
239 void ModuleBase_DoubleSpinBox::stepBy(int steps)
243 QDoubleSpinBox::stepBy(steps);
244 double tmpval = value();
245 if (qAbs(tmpval) < PSEUDO_ZERO)
247 if (tmpval < minimum())
249 else if (tmpval > maximum())
255 \brief This function is used to determine whether input is valid.
256 \param str currently entered value
257 \param pos cursor position in the string
258 \return validating operation result
260 QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const
262 QString pref = this->prefix();
263 QString suff = this->suffix();
264 uint overhead = pref.length() + suff.length();
265 QValidator::State state = QValidator::Invalid;
267 QDoubleValidator v(NULL);
269 // If 'g' format is used (myPrecision < 0), then
270 // myPrecision - 1 digits are allowed after the decimal point.
271 // Otherwise, expect myPrecision digits after the decimal point.
272 int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision;
274 v.setLocale(this->locale());
276 v.setBottom(minimum());
279 myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation);
282 state = v.validate(str, pos);
284 if ((uint)(str.length()) >= overhead && str.startsWith(pref) && str.right(suff.length()) == suff) {
285 QString core = str.mid(pref.length(), str.length() - overhead);
286 int corePos = pos - pref.length();
287 state = v.validate(core, corePos);
288 pos = corePos + pref.length();
289 str.replace(pref.length(), str.length() - overhead, core);
291 state = v.validate(str, pos);
292 if (state == QValidator::Invalid) {
293 QString special = this->specialValueText().trimmed();
294 QString candidate = str.trimmed();
295 if (special.startsWith(candidate)) {
296 if (candidate.length() == special.length())
297 state = QValidator::Acceptable;
299 state = QValidator::Intermediate;
305 // Treat values outside (min; max) range as Invalid
306 // This check is enabled by assigning "strict_validity_check" dynamic property
307 // with value "true" to the spin box instance.
308 if (state == QValidator::Intermediate) {
310 double val = str.toDouble(&isOk);
312 QVariant propVal = property("strict_validity_check");
313 if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) {
314 if (val < minimum() || val > maximum())
315 state = QValidator::Invalid;
317 } else if (myPrecision < 0) {
318 // Consider too large negative exponent as Invalid
319 QChar e(locale().exponential());
320 int epos = str.indexOf(e, 0, Qt::CaseInsensitive);
322 epos++; // Skip exponential symbol itself
323 QString exponent = str.right(str.length() - epos);
324 int expValue = exponent.toInt(&isOk);
325 if (isOk && expValue < std::numeric_limits<double>::min_exponent10)
326 state = QValidator::Invalid;
335 \brief Called when user enters the text in the spin box.
337 void ModuleBase_DoubleSpinBox::onTextChanged(const QString& )
343 void ModuleBase_DoubleSpinBox::onValueChanged(const QString& theValue)
348 void ModuleBase_DoubleSpinBox::onEditingFinished()
350 //myIsModified = false;
353 bool ModuleBase_DoubleSpinBox::isModified() const
358 void ModuleBase_DoubleSpinBox::clearModified()
360 myIsModified = false;
363 bool ModuleBase_DoubleSpinBox::enableKeyPressEvent(const bool& theEnable)
365 bool aPreviousValue = myIsEmitKeyPressEvent;
366 myIsEmitKeyPressEvent = theEnable;
368 return aPreviousValue;