1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
3 // File: ModuleBase_DoubleSpinBox.cxx
4 // Author: Sergey TELKOV
6 #include "ModuleBase_DoubleSpinBox.h"
7 #include "ModuleBase_Tools.h"
10 #include <QDoubleValidator>
16 const double PSEUDO_ZERO = 1.e-20;
19 \class ModuleBase_DoubleSpinBox
20 \brief Enhanced version of the Qt's double spin box.
22 The ModuleBase_DoubleSpinBox class represents the widget for entering the
23 floating point values. In addition to the functionality provided by
24 QDoubleSpinBox, this class supports "cleared" state - this is the
25 state corresponding to "None" (or empty) entered value.
27 To set "cleared" state use setCleared() method. To check if the spin
28 box stores "cleared" state, use isCleared() method.
31 if (myDblSpinBox->isCleared()) {
32 ... // process "None" state
35 double value = myDblSpinBox->value();
36 ... // process entered value
40 Another useful feature is possibility to use scientific notation (e.g. 1.234e+18)
41 for the widget text. To enable this, negative precision should be specified either
42 through a constructor or using setPrecision() method.
44 Note that "decimals" property of QDoubleSpinBox is almost completely substituted
45 by "myPrecision" field of ModuleBase_DoubleSpinBox class. "decimals" is still used
46 for proper size hint calculation and for rounding minimum and maximum bounds of
53 Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value,
54 a step value of 1.0 and a precision of 2 decimal places.
55 The value is initially set to 0.00.
57 \param theParent parent object
58 \param thePrecision precision of values input
60 ModuleBase_DoubleSpinBox::ModuleBase_DoubleSpinBox(QWidget* theParent, int thePrecision)
61 : QDoubleSpinBox(theParent),
64 setLocale(ModuleBase_Tools::doubleLocale());
66 // MPV 15/09/2014: this must be set before setDecimals;
67 // 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(qAbs(myPrecision));
75 connect(lineEdit(), SIGNAL(textChanged( const QString& )), this,
76 SLOT(onTextChanged( const QString& )));
78 myEnabledBaseColor = palette().color(QPalette::Active, QPalette::Base);
84 ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox()
89 \brief Check if spin box is in the "cleared" state.
90 \return \c true if spin box is cleared
93 bool ModuleBase_DoubleSpinBox::isCleared() const
99 \brief Change "cleared" status of the spin box.
100 \param on new "cleared" status
103 void ModuleBase_DoubleSpinBox::setCleared(const bool on)
109 setSpecialValueText(specialValueText());
113 \brief Set precision of the spin box
115 If precision value is less than 0, the 'g' format is used for value output,
116 otherwise 'f' format is used.
118 \param prec new precision value.
121 void ModuleBase_DoubleSpinBox::setPrecision(const int prec)
123 int newPrec = qAbs(prec);
124 int oldPrec = qAbs(myPrecision);
126 if (newPrec != oldPrec)
131 \brief Get precision value of the spin box
132 \return current precision value
135 int ModuleBase_DoubleSpinBox::getPrecision() const
141 \brief Interpret text entered by the user as a value.
142 \param text text entered by the user
146 double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const
149 return text.toDouble();
151 return QDoubleSpinBox::valueFromText(text);
155 \brief This function is used by the spin box whenever it needs to display
158 \param val spin box value
159 \return text representation of the value
162 QString ModuleBase_DoubleSpinBox::textFromValue(double val) const
164 QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision));
165 return removeTrailingZeroes(s);
169 \brief Return source string with removed leading and trailing zeros.
170 \param src source string
171 \return resulting string
173 QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const
175 QString delim(locale().decimalPoint());
177 int idx = src.lastIndexOf(delim);
181 QString iPart = src.left(idx);
182 QString fPart = src.mid(idx + 1);
184 int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+"));
186 ePart = fPart.mid(idx1);
187 fPart = fPart.left(idx1);
190 fPart.remove(QRegExp("0+$"));
193 if (!fPart.isEmpty())
194 res += delim + fPart;
201 \brief Perform \a steps increment/decrement steps.
203 The \a steps value can be any integer number. If it is > 0,
204 the value incrementing is done, otherwise value is decremented
207 \param steps number of increment/decrement steps
209 void ModuleBase_DoubleSpinBox::stepBy(int steps)
213 QDoubleSpinBox::stepBy(steps);
214 double tmpval = value();
215 if (qAbs(tmpval) < PSEUDO_ZERO)
217 if (tmpval < minimum())
219 else if (tmpval > maximum())
225 \brief This function is used to determine whether input is valid.
226 \param str currently entered value
227 \param pos cursor position in the string
228 \return validating operation result
230 QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const
232 QString pref = this->prefix();
233 QString suff = this->suffix();
234 uint overhead = pref.length() + suff.length();
235 QValidator::State state = QValidator::Invalid;
237 QDoubleValidator v(NULL);
239 // If 'g' format is used (myPrecision < 0), then
240 // myPrecision - 1 digits are allowed after the decimal point.
241 // Otherwise, expect myPrecision digits after the decimal point.
242 int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision;
244 v.setLocale(this->locale());
246 v.setBottom(minimum());
249 myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation);
252 state = v.validate(str, pos);
254 if ((uint)(str.length()) >= overhead &&
255 str.startsWith(pref) &&
256 str.right(suff.length()) == suff) {
257 QString core = str.mid(pref.length(), str.length() - overhead);
258 int corePos = pos - pref.length();
259 state = v.validate(core, corePos);
260 pos = corePos + pref.length();
261 str.replace(pref.length(), str.length() - overhead, core);
263 state = v.validate(str, pos);
264 if (state == QValidator::Invalid) {
265 QString special = this->specialValueText().trimmed();
266 QString candidate = str.trimmed();
267 if (special.startsWith(candidate)) {
268 if (candidate.length() == special.length())
269 state = QValidator::Acceptable;
271 state = QValidator::Intermediate;
277 // Treat values outside (min; max) range as Invalid
278 // This check is enabled by assigning "strict_validity_check" dynamic property
279 // with value "true" to the spin box instance.
280 if (state == QValidator::Intermediate) {
282 double val = str.toDouble(&isOk);
284 QVariant propVal = property("strict_validity_check");
285 if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) {
286 if (val < minimum() || val > maximum())
287 state = QValidator::Invalid;
289 } else if (myPrecision < 0) {
290 // Consider too large negative exponent as Invalid
291 QChar e(locale().exponential());
292 int epos = str.indexOf(e, 0, Qt::CaseInsensitive);
294 epos++; // Skip exponential symbol itself
295 QString exponent = str.right(str.length() - epos);
296 int expValue = exponent.toInt(&isOk);
297 if (isOk && expValue < std::numeric_limits<double>::min_exponent10)
298 state = QValidator::Invalid;
307 \brief Called when user enters the text in the spin box.
309 void ModuleBase_DoubleSpinBox::onTextChanged(const QString& )
314 void ModuleBase_DoubleSpinBox::setValueEnabled(const bool& theEnable)
316 setReadOnly(!theEnable);
318 QPalette aPal = palette();
319 aPal.setColor(QPalette::All, QPalette::Base,
320 theEnable? myEnabledBaseColor : aPal.color(QPalette::Disabled, QPalette::Base));