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 // MPV 15/09/2014: this must be set before setDecimals; otherwise in release mode setDecimals may crash
66 myPrecision = thePrecision;
68 // Use precision equal to default Qt decimals
69 // it's necessary to set decimals before the range setting,
70 // by default Qt rounds boundaries to 2 decimals at setRange
71 setDecimals(thePrecision);
73 connect(lineEdit(), SIGNAL(textChanged( const QString& )), this,
74 SLOT(onTextChanged( const QString& )));
80 ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox()
85 \brief Check if spin box is in the "cleared" state.
86 \return \c true if spin box is cleared
89 bool ModuleBase_DoubleSpinBox::isCleared() const
95 \brief Change "cleared" status of the spin box.
96 \param on new "cleared" status
99 void ModuleBase_DoubleSpinBox::setCleared(const bool on)
105 setSpecialValueText(specialValueText());
109 \brief Set precision of the spin box
111 If precision value is less than 0, the 'g' format is used for value output,
112 otherwise 'f' format is used.
114 \param prec new precision value.
117 void ModuleBase_DoubleSpinBox::setPrecision(const int prec)
119 int newPrec = qMax(prec, 0);
120 int oldPrec = qMax(myPrecision, 0);
122 if (newPrec != oldPrec)
127 \brief Get precision value of the spin box
128 \return current prevision value
131 int ModuleBase_DoubleSpinBox::getPrecision() const
137 \brief Interpret text entered by the user as a value.
138 \param text text entered by the user
142 double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const
145 return text.toDouble();
147 return QDoubleSpinBox::valueFromText(text);
151 \brief This function is used by the spin box whenever it needs to display
154 \param val spin box value
155 \return text representation of the value
158 QString ModuleBase_DoubleSpinBox::textFromValue(double val) const
160 QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision));
161 return removeTrailingZeroes(s);
165 \brief Return source string with removed leading and trailing zeros.
166 \param str source string
167 \return resulting string
169 QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const
171 QString delim(locale().decimalPoint());
173 int idx = src.lastIndexOf(delim);
177 QString iPart = src.left(idx);
178 QString fPart = src.mid(idx + 1);
180 int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+"));
182 ePart = fPart.mid(idx1);
183 fPart = fPart.left(idx1);
186 fPart.remove(QRegExp("0+$"));
189 if (!fPart.isEmpty())
190 res += delim + fPart;
197 \brief Perform \a steps increment/decrement steps.
199 The \a steps value can be any integer number. If it is > 0,
200 the value incrementing is done, otherwise value is decremented
203 \param steps number of increment/decrement steps
205 void ModuleBase_DoubleSpinBox::stepBy(int steps)
209 QDoubleSpinBox::stepBy(steps);
210 double tmpval = value();
211 if (qAbs(tmpval) < PSEUDO_ZERO)
213 if (tmpval < minimum())
215 else if (tmpval > maximum())
221 \brief This function is used to determine whether input is valid.
222 \param str currently entered value
223 \param pos cursor position in the string
224 \return validating operation result
226 QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const
228 QString pref = this->prefix();
229 QString suff = this->suffix();
230 uint overhead = pref.length() + suff.length();
231 QValidator::State state = QValidator::Invalid;
233 QDoubleValidator v(NULL);
235 // If 'g' format is used (myPrecision < 0), then
236 // myPrecision - 1 digits are allowed after the decimal point.
237 // Otherwise, expect myPrecision digits after the decimal point.
238 int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision;
241 v.setBottom(minimum());
244 myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation);
247 state = v.validate(str, pos);
249 if ((uint)(str.length()) >= overhead && str.startsWith(pref) && str.right(suff.length()) == suff) {
250 QString core = str.mid(pref.length(), str.length() - overhead);
251 int corePos = pos - pref.length();
252 state = v.validate(core, corePos);
253 pos = corePos + pref.length();
254 str.replace(pref.length(), str.length() - overhead, core);
256 state = v.validate(str, pos);
257 if (state == QValidator::Invalid) {
258 QString special = this->specialValueText().trimmed();
259 QString candidate = str.trimmed();
260 if (special.startsWith(candidate)) {
261 if (candidate.length() == special.length())
262 state = QValidator::Acceptable;
264 state = QValidator::Intermediate;
270 // Treat values ouside (min; max) range as Invalid
271 // This check is enabled by assigning "strict_validity_check" dynamic property
272 // with value "true" to the spin box instance.
273 if (state == QValidator::Intermediate) {
275 double val = str.toDouble(&isOk);
277 QVariant propVal = property("strict_validity_check");
278 if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) {
279 if (val < minimum() || val > maximum())
280 state = QValidator::Invalid;
282 } else if (myPrecision < 0) {
283 // Consider too large negative exponent as Invalid
284 QChar e(locale().exponential());
285 int epos = str.indexOf(e, 0, Qt::CaseInsensitive);
287 epos++; // Skip exponential symbol itself
288 QString exponent = str.right(str.length() - epos);
289 int expValue = exponent.toInt(&isOk);
290 if (isOk && expValue < std::numeric_limits<double>::min_exponent10)
291 state = QValidator::Invalid;
300 \brief Called when user enters the text in the spin box.
301 \param txt current spin box text (not used)
303 void ModuleBase_DoubleSpinBox::onTextChanged(const QString& /*txt*/)