1 // Copyright (C) 2014-2017 CEA/DEN, EDF R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
21 #include "ModuleBase_DoubleSpinBox.h"
22 #include "ModuleBase_Tools.h"
25 #include <QDoubleValidator>
31 const double PSEUDO_ZERO = 1.e-20;
34 \class ModuleBase_DoubleSpinBox
35 \brief Enhanced version of the Qt's double spin box.
37 The ModuleBase_DoubleSpinBox class represents the widget for entering the
38 floating point values. In addition to the functionality provided by
39 QDoubleSpinBox, this class supports "cleared" state - this is the
40 state corresponding to "None" (or empty) entered value.
42 To set "cleared" state use setCleared() method. To check if the spin
43 box stores "cleared" state, use isCleared() method.
46 if (myDblSpinBox->isCleared()) {
47 ... // process "None" state
50 double value = myDblSpinBox->value();
51 ... // process entered value
55 Another useful feature is possibility to use scientific notation (e.g. 1.234e+18)
56 for the widget text. To enable this, negative precision should be specified either
57 through a constructor or using setPrecision() method.
59 Note that "decimals" property of QDoubleSpinBox is almost completely substituted
60 by "myPrecision" field of ModuleBase_DoubleSpinBox class. "decimals" is still used
61 for proper size hint calculation and for rounding minimum and maximum bounds of
68 Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value,
69 a step value of 1.0 and a precision of 2 decimal places.
70 The value is initially set to 0.00.
72 \param theParent parent object
73 \param thePrecision precision of values input
75 ModuleBase_DoubleSpinBox::ModuleBase_DoubleSpinBox(QWidget* theParent, int thePrecision)
76 : QDoubleSpinBox(theParent),
79 setLocale(ModuleBase_Tools::doubleLocale());
81 // MPV 15/09/2014: this must be set before setDecimals;
82 // otherwise in release mode setDecimals may crash
83 myPrecision = thePrecision;
85 // Use precision equal to default Qt decimals
86 // it's necessary to set decimals before the range setting,
87 // by default Qt rounds boundaries to 2 decimals at setRange
88 setDecimals(qAbs(myPrecision));
90 connect(lineEdit(), SIGNAL(textChanged( const QString& )), this,
91 SLOT(onTextChanged( const QString& )));
93 myEnabledBaseColor = palette().color(QPalette::Active, QPalette::Base);
99 ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox()
104 \brief Check if spin box is in the "cleared" state.
105 \return \c true if spin box is cleared
108 bool ModuleBase_DoubleSpinBox::isCleared() const
114 \brief Change "cleared" status of the spin box.
115 \param on new "cleared" status
118 void ModuleBase_DoubleSpinBox::setCleared(const bool on)
124 setSpecialValueText(specialValueText());
128 \brief Set precision of the spin box
130 If precision value is less than 0, the 'g' format is used for value output,
131 otherwise 'f' format is used.
133 \param prec new precision value.
136 void ModuleBase_DoubleSpinBox::setPrecision(const int prec)
138 int newPrec = qAbs(prec);
139 int oldPrec = qAbs(myPrecision);
141 if (newPrec != oldPrec)
146 \brief Get precision value of the spin box
147 \return current precision value
150 int ModuleBase_DoubleSpinBox::getPrecision() const
156 \brief Interpret text entered by the user as a value.
157 \param text text entered by the user
161 double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const
164 return text.toDouble();
166 return QDoubleSpinBox::valueFromText(text);
170 \brief This function is used by the spin box whenever it needs to display
173 \param val spin box value
174 \return text representation of the value
177 QString ModuleBase_DoubleSpinBox::textFromValue(double val) const
179 QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision));
180 return removeTrailingZeroes(s);
184 \brief Return source string with removed leading and trailing zeros.
185 \param src source string
186 \return resulting string
188 QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const
190 QString delim(locale().decimalPoint());
192 int idx = src.lastIndexOf(delim);
196 QString iPart = src.left(idx);
197 QString fPart = src.mid(idx + 1);
199 int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+"));
201 ePart = fPart.mid(idx1);
202 fPart = fPart.left(idx1);
205 fPart.remove(QRegExp("0+$"));
208 if (!fPart.isEmpty())
209 res += delim + fPart;
216 \brief Perform \a steps increment/decrement steps.
218 The \a steps value can be any integer number. If it is > 0,
219 the value incrementing is done, otherwise value is decremented
222 \param steps number of increment/decrement steps
224 void ModuleBase_DoubleSpinBox::stepBy(int steps)
228 QDoubleSpinBox::stepBy(steps);
229 double tmpval = value();
230 if (qAbs(tmpval) < PSEUDO_ZERO)
232 if (tmpval < minimum())
234 else if (tmpval > maximum())
240 \brief This function is used to determine whether input is valid.
241 \param str currently entered value
242 \param pos cursor position in the string
243 \return validating operation result
245 QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const
247 QString pref = this->prefix();
248 QString suff = this->suffix();
249 uint overhead = pref.length() + suff.length();
250 QValidator::State state = QValidator::Invalid;
252 QDoubleValidator v(NULL);
254 // If 'g' format is used (myPrecision < 0), then
255 // myPrecision - 1 digits are allowed after the decimal point.
256 // Otherwise, expect myPrecision digits after the decimal point.
257 int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision;
259 v.setLocale(this->locale());
261 v.setBottom(minimum());
264 myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation);
267 state = v.validate(str, pos);
269 if ((uint)(str.length()) >= overhead &&
270 str.startsWith(pref) &&
271 str.right(suff.length()) == suff) {
272 QString core = str.mid(pref.length(), str.length() - overhead);
273 int corePos = pos - pref.length();
274 state = v.validate(core, corePos);
275 pos = corePos + pref.length();
276 str.replace(pref.length(), str.length() - overhead, core);
278 state = v.validate(str, pos);
279 if (state == QValidator::Invalid) {
280 QString special = this->specialValueText().trimmed();
281 QString candidate = str.trimmed();
282 if (special.startsWith(candidate)) {
283 if (candidate.length() == special.length())
284 state = QValidator::Acceptable;
286 state = QValidator::Intermediate;
292 // Treat values outside (min; max) range as Invalid
293 // This check is enabled by assigning "strict_validity_check" dynamic property
294 // with value "true" to the spin box instance.
295 if (state == QValidator::Intermediate) {
297 double val = str.toDouble(&isOk);
299 QVariant propVal = property("strict_validity_check");
300 if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) {
301 if (val < minimum() || val > maximum())
302 state = QValidator::Invalid;
304 } else if (myPrecision < 0) {
305 // Consider too large negative exponent as Invalid
306 QChar e(locale().exponential());
307 int epos = str.indexOf(e, 0, Qt::CaseInsensitive);
309 epos++; // Skip exponential symbol itself
310 QString exponent = str.right(str.length() - epos);
311 int expValue = exponent.toInt(&isOk);
312 if (isOk && expValue < std::numeric_limits<double>::min_exponent10)
313 state = QValidator::Invalid;
322 \brief Called when user enters the text in the spin box.
324 void ModuleBase_DoubleSpinBox::onTextChanged(const QString& )
329 void ModuleBase_DoubleSpinBox::setValueEnabled(const bool& theEnable)
331 setReadOnly(!theEnable);
333 QPalette aPal = palette();
334 aPal.setColor(QPalette::All, QPalette::Base,
335 theEnable? myEnabledBaseColor : aPal.color(QPalette::Disabled, QPalette::Base));