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>
14 const double PSEUDO_ZERO = 1.e-20;
17 \class ModuleBase_DoubleSpinBox
18 \brief Enhanced version of the Qt's double spin box.
20 The ModuleBase_DoubleSpinBox class represents the widget for entering the
21 floating point values. In addition to the functionality provided by
22 QDoubleSpinBox, this class supports "cleared" state - this is the
23 state corresponding to "None" (or empty) entered value.
25 To set "cleared" state use setCleared() method. To check if the spin
26 box stores "cleared" state, use isCleared() method.
29 if (myDblSpinBox->isCleared()) {
30 ... // process "None" state
33 double value = myDblSpinBox->value();
34 ... // process entered value
38 Another useful feature is possibility to use scientific notation (e.g. 1.234e+18)
39 for the widget text. To enable this, negative precision should be specified either
40 through a constructor or using setPrecision() method.
42 Note that "decimals" property of QDoubleSpinBox is almost completely substituted
43 by "myPrecision" field of ModuleBase_DoubleSpinBox class. "decimals" is still used
44 for proper size hint calculation and for rounding minimum and maximum bounds of
51 Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value,
52 a step value of 1.0 and a precision of 2 decimal places.
53 The value is initially set to 0.00.
55 \param theParent parent object
56 \param thePrecision precision of values input
58 ModuleBase_DoubleSpinBox::ModuleBase_DoubleSpinBox(QWidget* theParent, int thePrecision)
59 : QDoubleSpinBox(theParent),
62 // VSR 01/07/2010: Disable thousands separator for spin box
63 // (to avoid incosistency of double-2-string and string-2-double conversion)
65 loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
68 // MPV 15/09/2014: this must be set before setDecimals; otherwise in release mode setDecimals may crash
69 myPrecision = thePrecision;
71 // Use precision equal to default Qt decimals
72 // it's necessary to set decimals before the range setting,
73 // by default Qt rounds boundaries to 2 decimals at setRange
74 setDecimals(qAbs(myPrecision));
76 connect(lineEdit(), SIGNAL(textChanged( const QString& )), this,
77 SLOT(onTextChanged( const QString& )));
83 ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox()
88 \brief Check if spin box is in the "cleared" state.
89 \return \c true if spin box is cleared
92 bool ModuleBase_DoubleSpinBox::isCleared() const
98 \brief Change "cleared" status of the spin box.
99 \param on new "cleared" status
102 void ModuleBase_DoubleSpinBox::setCleared(const bool on)
108 setSpecialValueText(specialValueText());
112 \brief Set precision of the spin box
114 If precision value is less than 0, the 'g' format is used for value output,
115 otherwise 'f' format is used.
117 \param prec new precision value.
120 void ModuleBase_DoubleSpinBox::setPrecision(const int prec)
122 int newPrec = qAbs(prec);
123 int oldPrec = qAbs(myPrecision);
125 if (newPrec != oldPrec)
130 \brief Get precision value of the spin box
131 \return current precision value
134 int ModuleBase_DoubleSpinBox::getPrecision() const
140 \brief Interpret text entered by the user as a value.
141 \param text text entered by the user
145 double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const
148 return text.toDouble();
150 return QDoubleSpinBox::valueFromText(text);
154 \brief This function is used by the spin box whenever it needs to display
157 \param val spin box value
158 \return text representation of the value
161 QString ModuleBase_DoubleSpinBox::textFromValue(double val) const
163 QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision));
164 return removeTrailingZeroes(s);
168 \brief Return source string with removed leading and trailing zeros.
169 \param src source string
170 \return resulting string
172 QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const
174 QString delim(locale().decimalPoint());
176 int idx = src.lastIndexOf(delim);
180 QString iPart = src.left(idx);
181 QString fPart = src.mid(idx + 1);
183 int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+"));
185 ePart = fPart.mid(idx1);
186 fPart = fPart.left(idx1);
189 fPart.remove(QRegExp("0+$"));
192 if (!fPart.isEmpty())
193 res += delim + fPart;
200 \brief Perform \a steps increment/decrement steps.
202 The \a steps value can be any integer number. If it is > 0,
203 the value incrementing is done, otherwise value is decremented
206 \param steps number of increment/decrement steps
208 void ModuleBase_DoubleSpinBox::stepBy(int steps)
212 QDoubleSpinBox::stepBy(steps);
213 double tmpval = value();
214 if (qAbs(tmpval) < PSEUDO_ZERO)
216 if (tmpval < minimum())
218 else if (tmpval > maximum())
224 \brief This function is used to determine whether input is valid.
225 \param str currently entered value
226 \param pos cursor position in the string
227 \return validating operation result
229 QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const
231 QString pref = this->prefix();
232 QString suff = this->suffix();
233 uint overhead = pref.length() + suff.length();
234 QValidator::State state = QValidator::Invalid;
236 QDoubleValidator v(NULL);
238 // If 'g' format is used (myPrecision < 0), then
239 // myPrecision - 1 digits are allowed after the decimal point.
240 // Otherwise, expect myPrecision digits after the decimal point.
241 int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision;
243 v.setLocale(this->locale());
245 v.setBottom(minimum());
248 myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation);
251 state = v.validate(str, pos);
253 if ((uint)(str.length()) >= overhead && str.startsWith(pref) && str.right(suff.length()) == suff) {
254 QString core = str.mid(pref.length(), str.length() - overhead);
255 int corePos = pos - pref.length();
256 state = v.validate(core, corePos);
257 pos = corePos + pref.length();
258 str.replace(pref.length(), str.length() - overhead, core);
260 state = v.validate(str, pos);
261 if (state == QValidator::Invalid) {
262 QString special = this->specialValueText().trimmed();
263 QString candidate = str.trimmed();
264 if (special.startsWith(candidate)) {
265 if (candidate.length() == special.length())
266 state = QValidator::Acceptable;
268 state = QValidator::Intermediate;
274 // Treat values ouside (min; max) range as Invalid
275 // This check is enabled by assigning "strict_validity_check" dynamic property
276 // with value "true" to the spin box instance.
277 if (state == QValidator::Intermediate) {
279 double val = str.toDouble(&isOk);
281 QVariant propVal = property("strict_validity_check");
282 if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) {
283 if (val < minimum() || val > maximum())
284 state = QValidator::Invalid;
286 } else if (myPrecision < 0) {
287 // Consider too large negative exponent as Invalid
288 QChar e(locale().exponential());
289 int epos = str.indexOf(e, 0, Qt::CaseInsensitive);
291 epos++; // Skip exponential symbol itself
292 QString exponent = str.right(str.length() - epos);
293 int expValue = exponent.toInt(&isOk);
294 if (isOk && expValue < std::numeric_limits<double>::min_exponent10)
295 state = QValidator::Invalid;
304 \brief Called when user enters the text in the spin box.
306 void ModuleBase_DoubleSpinBox::onTextChanged(const QString& )