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 widegt 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 parent parent object
57 ModuleBase_DoubleSpinBox::ModuleBase_DoubleSpinBox(QWidget* parent, int thePrecision)
58 : QDoubleSpinBox(parent),
61 // VSR 01/07/2010: Disable thousands separator for spin box
62 // (to avoid incosistency of double-2-string and string-2-double conversion)
64 loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
67 // MPV 15/09/2014: this must be set before setDecimals; otherwise in release mode setDecimals may crash
68 myPrecision = thePrecision;
70 // Use precision equal to default Qt decimals
71 // it's necessary to set decimals before the range setting,
72 // by default Qt rounds boundaries to 2 decimals at setRange
73 setDecimals(thePrecision);
75 connect(lineEdit(), SIGNAL(textChanged( const QString& )), this,
76 SLOT(onTextChanged( const QString& )));
82 ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox()
87 \brief Check if spin box is in the "cleared" state.
88 \return \c true if spin box is cleared
91 bool ModuleBase_DoubleSpinBox::isCleared() const
97 \brief Change "cleared" status of the spin box.
98 \param on new "cleared" status
101 void ModuleBase_DoubleSpinBox::setCleared(const bool on)
107 setSpecialValueText(specialValueText());
111 \brief Set precision of the spin box
113 If precision value is less than 0, the 'g' format is used for value output,
114 otherwise 'f' format is used.
116 \param prec new precision value.
119 void ModuleBase_DoubleSpinBox::setPrecision(const int prec)
121 int newPrec = qMax(prec, 0);
122 int oldPrec = qMax(myPrecision, 0);
124 if (newPrec != oldPrec)
129 \brief Get precision value of the spin box
130 \return current prevision value
133 int ModuleBase_DoubleSpinBox::getPrecision() const
139 \brief Interpret text entered by the user as a value.
140 \param text text entered by the user
144 double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const
147 return text.toDouble();
149 return QDoubleSpinBox::valueFromText(text);
153 \brief This function is used by the spin box whenever it needs to display
156 \param val spin box value
157 \return text representation of the value
160 QString ModuleBase_DoubleSpinBox::textFromValue(double val) const
162 QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision));
163 return removeTrailingZeroes(s);
167 \brief Return source string with removed leading and trailing zeros.
168 \param str source string
169 \return resulting string
171 QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const
173 QString delim(locale().decimalPoint());
175 int idx = src.lastIndexOf(delim);
179 QString iPart = src.left(idx);
180 QString fPart = src.mid(idx + 1);
182 int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+"));
184 ePart = fPart.mid(idx1);
185 fPart = fPart.left(idx1);
188 fPart.remove(QRegExp("0+$"));
191 if (!fPart.isEmpty())
192 res += delim + fPart;
199 \brief Perform \a steps increment/decrement steps.
201 The \a steps value can be any integer number. If it is > 0,
202 the value incrementing is done, otherwise value is decremented
205 \param steps number of increment/decrement steps
207 void ModuleBase_DoubleSpinBox::stepBy(int steps)
211 QDoubleSpinBox::stepBy(steps);
212 double tmpval = value();
213 if (qAbs(tmpval) < PSEUDO_ZERO)
215 if (tmpval < minimum())
217 else if (tmpval > maximum())
223 \brief This function is used to determine whether input is valid.
224 \param str currently entered value
225 \param pos cursor position in the string
226 \return validating operation result
228 QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const
230 QString pref = this->prefix();
231 QString suff = this->suffix();
232 uint overhead = pref.length() + suff.length();
233 QValidator::State state = QValidator::Invalid;
235 QDoubleValidator v(NULL);
237 // If 'g' format is used (myPrecision < 0), then
238 // myPrecision - 1 digits are allowed after the decimal point.
239 // Otherwise, expect myPrecision digits after the decimal point.
240 int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision;
243 v.setBottom(minimum());
246 myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation);
249 state = v.validate(str, pos);
251 if ((uint)(str.length()) >= overhead && str.startsWith(pref) && str.right(suff.length()) == suff) {
252 QString core = str.mid(pref.length(), str.length() - overhead);
253 int corePos = pos - pref.length();
254 state = v.validate(core, corePos);
255 pos = corePos + pref.length();
256 str.replace(pref.length(), str.length() - overhead, core);
258 state = v.validate(str, pos);
259 if (state == QValidator::Invalid) {
260 QString special = this->specialValueText().trimmed();
261 QString candidate = str.trimmed();
262 if (special.startsWith(candidate)) {
263 if (candidate.length() == special.length())
264 state = QValidator::Acceptable;
266 state = QValidator::Intermediate;
272 // Treat values ouside (min; max) range as Invalid
273 // This check is enabled by assigning "strict_validity_check" dynamic property
274 // with value "true" to the spin box instance.
275 if (state == QValidator::Intermediate) {
277 double val = str.toDouble(&isOk);
279 QVariant propVal = property("strict_validity_check");
280 if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) {
281 if (val < minimum() || val > maximum())
282 state = QValidator::Invalid;
284 } else if (myPrecision < 0) {
285 // Consider too large negative exponent as Invalid
286 QChar e(locale().exponential());
287 int epos = str.indexOf(e, 0, Qt::CaseInsensitive);
289 epos++; // Skip exponential symbol itself
290 QString exponent = str.right(str.length() - epos);
291 int expValue = exponent.toInt(&isOk);
292 if (isOk && expValue < std::numeric_limits<double>::min_exponent10)
293 state = QValidator::Invalid;
302 \brief Called when user enters the text in the spin box.
303 \param txt current spin box text (not used)
305 void ModuleBase_DoubleSpinBox::onTextChanged(const QString& /*txt*/)