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>
15 const double PSEUDO_ZERO = 1.e-20;
18 \class ModuleBase_DoubleSpinBox
19 \brief Enhanced version of the Qt's double spin box.
21 The ModuleBase_DoubleSpinBox class represents the widget for entering the
22 floating point values. In addition to the functionality provided by
23 QDoubleSpinBox, this class supports "cleared" state - this is the
24 state corresponding to "None" (or empty) entered value.
26 To set "cleared" state use setCleared() method. To check if the spin
27 box stores "cleared" state, use isCleared() method.
30 if (myDblSpinBox->isCleared()) {
31 ... // process "None" state
34 double value = myDblSpinBox->value();
35 ... // process entered value
39 Another useful feature is possibility to use scientific notation (e.g. 1.234e+18)
40 for the widget text. To enable this, negative precision should be specified either
41 through a constructor or using setPrecision() method.
43 Note that "decimals" property of QDoubleSpinBox is almost completely substituted
44 by "myPrecision" field of ModuleBase_DoubleSpinBox class. "decimals" is still used
45 for proper size hint calculation and for rounding minimum and maximum bounds of
52 Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value,
53 a step value of 1.0 and a precision of 2 decimal places.
54 The value is initially set to 0.00.
56 \param theParent parent object
57 \param thePrecision precision of values input
59 ModuleBase_DoubleSpinBox::ModuleBase_DoubleSpinBox(QWidget* theParent, int thePrecision)
60 : QDoubleSpinBox(theParent),
62 myIsEmitKeyPressEvent(false)
64 // VSR 01/07/2010: Disable thousands separator for spin box
65 // (to avoid inconsistency of double-2-string and string-2-double conversion)
67 loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
70 // MPV 15/09/2014: this must be set before setDecimals; otherwise in release mode setDecimals may crash
71 myPrecision = thePrecision;
73 // Use precision equal to default Qt decimals
74 // it's necessary to set decimals before the range setting,
75 // by default Qt rounds boundaries to 2 decimals at setRange
76 setDecimals(qAbs(myPrecision));
78 connect(lineEdit(), SIGNAL(textChanged( const QString& )), this,
79 SLOT(onTextChanged( const QString& )));
85 ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox()
90 \brief Check if spin box is in the "cleared" state.
91 \return \c true if spin box is cleared
94 bool ModuleBase_DoubleSpinBox::isCleared() const
100 \brief Change "cleared" status of the spin box.
101 \param on new "cleared" status
104 void ModuleBase_DoubleSpinBox::setCleared(const bool on)
110 setSpecialValueText(specialValueText());
114 \brief Set precision of the spin box
116 If precision value is less than 0, the 'g' format is used for value output,
117 otherwise 'f' format is used.
119 \param prec new precision value.
122 void ModuleBase_DoubleSpinBox::setPrecision(const int prec)
124 int newPrec = qAbs(prec);
125 int oldPrec = qAbs(myPrecision);
127 if (newPrec != oldPrec)
132 \brief Get precision value of the spin box
133 \return current precision value
136 int ModuleBase_DoubleSpinBox::getPrecision() const
142 \brief Interpret text entered by the user as a value.
143 \param text text entered by the user
147 double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const
150 return text.toDouble();
152 return QDoubleSpinBox::valueFromText(text);
156 \brief This function is used by the spin box whenever it needs to display
159 \param val spin box value
160 \return text representation of the value
163 QString ModuleBase_DoubleSpinBox::textFromValue(double val) const
165 QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision));
166 return removeTrailingZeroes(s);
170 \brief Return source string with removed leading and trailing zeros.
171 \param src source string
172 \return resulting string
174 QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const
176 QString delim(locale().decimalPoint());
178 int idx = src.lastIndexOf(delim);
182 QString iPart = src.left(idx);
183 QString fPart = src.mid(idx + 1);
185 int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+"));
187 ePart = fPart.mid(idx1);
188 fPart = fPart.left(idx1);
191 fPart.remove(QRegExp("0+$"));
194 if (!fPart.isEmpty())
195 res += delim + fPart;
201 void ModuleBase_DoubleSpinBox::keyPressEvent(QKeyEvent* theEvent)
203 bool isEmitKeyRelease = false;
204 switch (theEvent->key()) {
206 case Qt::Key_Return: {
207 // do not react to the Enter key, the property panel processes it
208 if (!myIsEmitKeyPressEvent)
211 isEmitKeyRelease = true;
217 QDoubleSpinBox::keyPressEvent(theEvent);
219 if (isEmitKeyRelease)
223 void ModuleBase_DoubleSpinBox::keyReleaseEvent(QKeyEvent* theEvent)
225 switch (theEvent->key()) {
227 case Qt::Key_Return: {
228 // the enter has already been processed when key is pressed,
229 // key release should not be processed in operation manager
230 if (myIsEmitKeyPressEvent) {
232 emit enterReleased();
240 QDoubleSpinBox::keyReleaseEvent(theEvent);
244 \brief Perform \a steps increment/decrement steps.
246 The \a steps value can be any integer number. If it is > 0,
247 the value incrementing is done, otherwise value is decremented
250 \param steps number of increment/decrement steps
252 void ModuleBase_DoubleSpinBox::stepBy(int steps)
256 QDoubleSpinBox::stepBy(steps);
257 double tmpval = value();
258 if (qAbs(tmpval) < PSEUDO_ZERO)
260 if (tmpval < minimum())
262 else if (tmpval > maximum())
268 \brief This function is used to determine whether input is valid.
269 \param str currently entered value
270 \param pos cursor position in the string
271 \return validating operation result
273 QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const
275 QString pref = this->prefix();
276 QString suff = this->suffix();
277 uint overhead = pref.length() + suff.length();
278 QValidator::State state = QValidator::Invalid;
280 QDoubleValidator v(NULL);
282 // If 'g' format is used (myPrecision < 0), then
283 // myPrecision - 1 digits are allowed after the decimal point.
284 // Otherwise, expect myPrecision digits after the decimal point.
285 int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision;
287 v.setLocale(this->locale());
289 v.setBottom(minimum());
292 myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation);
295 state = v.validate(str, pos);
297 if ((uint)(str.length()) >= overhead && str.startsWith(pref) && str.right(suff.length()) == suff) {
298 QString core = str.mid(pref.length(), str.length() - overhead);
299 int corePos = pos - pref.length();
300 state = v.validate(core, corePos);
301 pos = corePos + pref.length();
302 str.replace(pref.length(), str.length() - overhead, core);
304 state = v.validate(str, pos);
305 if (state == QValidator::Invalid) {
306 QString special = this->specialValueText().trimmed();
307 QString candidate = str.trimmed();
308 if (special.startsWith(candidate)) {
309 if (candidate.length() == special.length())
310 state = QValidator::Acceptable;
312 state = QValidator::Intermediate;
318 // Treat values outside (min; max) range as Invalid
319 // This check is enabled by assigning "strict_validity_check" dynamic property
320 // with value "true" to the spin box instance.
321 if (state == QValidator::Intermediate) {
323 double val = str.toDouble(&isOk);
325 QVariant propVal = property("strict_validity_check");
326 if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) {
327 if (val < minimum() || val > maximum())
328 state = QValidator::Invalid;
330 } else if (myPrecision < 0) {
331 // Consider too large negative exponent as Invalid
332 QChar e(locale().exponential());
333 int epos = str.indexOf(e, 0, Qt::CaseInsensitive);
335 epos++; // Skip exponential symbol itself
336 QString exponent = str.right(str.length() - epos);
337 int expValue = exponent.toInt(&isOk);
338 if (isOk && expValue < std::numeric_limits<double>::min_exponent10)
339 state = QValidator::Invalid;
348 \brief Called when user enters the text in the spin box.
350 void ModuleBase_DoubleSpinBox::onTextChanged(const QString& )
355 bool ModuleBase_DoubleSpinBox::enableKeyPressEvent(const bool& theEnable)
357 bool aPreviousValue = myIsEmitKeyPressEvent;
358 myIsEmitKeyPressEvent = theEnable;
360 return aPreviousValue;