1 // File: ModuleBase_DoubleSpinBox.cxx
2 // Author: Sergey TELKOV
4 #include "ModuleBase_DoubleSpinBox.h"
7 #include <QDoubleValidator>
12 const double PSEUDO_ZERO = 1.e-20;
15 \class ModuleBase_DoubleSpinBox
16 \brief Enhanced version of the Qt's double spin box.
18 The ModuleBase_DoubleSpinBox class represents the widget for entering the
19 floating point values. In addition to the functionality provided by
20 QDoubleSpinBox, this class supports "cleared" state - this is the
21 state corresponding to "None" (or empty) entered value.
23 To set "cleared" state use setCleared() method. To check if the spin
24 box stores "cleared" state, use isCleared() method.
27 if (myDblSpinBox->isCleared()) {
28 ... // process "None" state
31 double value = myDblSpinBox->value();
32 ... // process entered value
36 Another useful feature is possibility to use scientific notation (e.g. 1.234e+18)
37 for the widegt text. To enable this, negative precision should be specified either
38 through a constructor or using setPrecision() method.
40 Note that "decimals" property of QDoubleSpinBox is almost completely substituted
41 by "myPrecision" field of ModuleBase_DoubleSpinBox class. "decimals" is still used
42 for proper size hint calculation and for rounding minimum and maximum bounds of
49 Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value,
50 a step value of 1.0 and a precision of 2 decimal places.
51 The value is initially set to 0.00.
53 \param parent parent object
55 ModuleBase_DoubleSpinBox::ModuleBase_DoubleSpinBox(QWidget* parent, int thePrecision)
56 : QDoubleSpinBox(parent),
59 // VSR 01/07/2010: Disable thousands separator for spin box
60 // (to avoid incosistency of double-2-string and string-2-double conversion)
62 loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
65 // Use precision equal to default Qt decimals
66 // it's necessary to set decimals before the range setting,
67 // by default Qt rounds boundaries to 2 decimals at setRange
68 setDecimals(thePrecision);
69 myPrecision = thePrecision;
71 connect(lineEdit(), SIGNAL(textChanged( const QString& )), this,
72 SLOT(onTextChanged( const QString& )));
78 ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox()
83 \brief Check if spin box is in the "cleared" state.
84 \return \c true if spin box is cleared
87 bool ModuleBase_DoubleSpinBox::isCleared() const
93 \brief Change "cleared" status of the spin box.
94 \param on new "cleared" status
97 void ModuleBase_DoubleSpinBox::setCleared(const bool on)
103 setSpecialValueText(specialValueText());
107 \brief Set precision of the spin box
109 If precision value is less than 0, the 'g' format is used for value output,
110 otherwise 'f' format is used.
112 \param prec new precision value.
115 void ModuleBase_DoubleSpinBox::setPrecision(const int prec)
117 int newPrec = qMax(prec, 0);
118 int oldPrec = qMax(myPrecision, 0);
120 if (newPrec != oldPrec)
125 \brief Get precision value of the spin box
126 \return current prevision value
129 int ModuleBase_DoubleSpinBox::getPrecision() const
135 \brief Interpret text entered by the user as a value.
136 \param text text entered by the user
140 double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const
143 return text.toDouble();
145 return QDoubleSpinBox::valueFromText(text);
149 \brief This function is used by the spin box whenever it needs to display
152 \param val spin box value
153 \return text representation of the value
156 QString ModuleBase_DoubleSpinBox::textFromValue(double val) const
158 QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision));
159 return removeTrailingZeroes(s);
163 \brief Return source string with removed leading and trailing zeros.
164 \param str source string
165 \return resulting string
167 QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const
169 QString delim(locale().decimalPoint());
171 int idx = src.lastIndexOf(delim);
175 QString iPart = src.left(idx);
176 QString fPart = src.mid(idx + 1);
178 int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+"));
180 ePart = fPart.mid(idx1);
181 fPart = fPart.left(idx1);
184 fPart.remove(QRegExp("0+$"));
187 if (!fPart.isEmpty())
188 res += delim + fPart;
195 \brief Perform \a steps increment/decrement steps.
197 The \a steps value can be any integer number. If it is > 0,
198 the value incrementing is done, otherwise value is decremented
201 \param steps number of increment/decrement steps
203 void ModuleBase_DoubleSpinBox::stepBy(int steps)
207 QDoubleSpinBox::stepBy(steps);
208 double tmpval = value();
209 if (qAbs(tmpval) < PSEUDO_ZERO)
211 if (tmpval < minimum())
213 else if (tmpval > maximum())
219 \brief This function is used to determine whether input is valid.
220 \param str currently entered value
221 \param pos cursor position in the string
222 \return validating operation result
224 QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const
226 QString pref = this->prefix();
227 QString suff = this->suffix();
228 uint overhead = pref.length() + suff.length();
229 QValidator::State state = QValidator::Invalid;
231 QDoubleValidator v(NULL);
233 // If 'g' format is used (myPrecision < 0), then
234 // myPrecision - 1 digits are allowed after the decimal point.
235 // Otherwise, expect myPrecision digits after the decimal point.
236 int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision;
239 v.setBottom(minimum());
242 myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation);
245 state = v.validate(str, pos);
247 if ((uint)(str.length()) >= overhead && str.startsWith(pref) && str.right(suff.length()) == suff) {
248 QString core = str.mid(pref.length(), str.length() - overhead);
249 int corePos = pos - pref.length();
250 state = v.validate(core, corePos);
251 pos = corePos + pref.length();
252 str.replace(pref.length(), str.length() - overhead, core);
254 state = v.validate(str, pos);
255 if (state == QValidator::Invalid) {
256 QString special = this->specialValueText().trimmed();
257 QString candidate = str.trimmed();
258 if (special.startsWith(candidate)) {
259 if (candidate.length() == special.length())
260 state = QValidator::Acceptable;
262 state = QValidator::Intermediate;
268 // Treat values ouside (min; max) range as Invalid
269 // This check is enabled by assigning "strict_validity_check" dynamic property
270 // with value "true" to the spin box instance.
271 if (state == QValidator::Intermediate) {
273 double val = str.toDouble(&isOk);
275 QVariant propVal = property("strict_validity_check");
276 if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) {
277 if (val < minimum() || val > maximum())
278 state = QValidator::Invalid;
280 } else if (myPrecision < 0) {
281 // Consider too large negative exponent as Invalid
282 QChar e(locale().exponential());
283 int epos = str.indexOf(e, 0, Qt::CaseInsensitive);
285 epos++; // Skip exponential symbol itself
286 QString exponent = str.right(str.length() - epos);
287 int expValue = exponent.toInt(&isOk);
288 if (isOk && expValue < std::numeric_limits<double>::min_exponent10)
289 state = QValidator::Invalid;
298 \brief Called when user enters the text in the spin box.
299 \param txt current spin box text (not used)
301 void ModuleBase_DoubleSpinBox::onTextChanged(const QString& /*txt*/)