1 // Copyright (C) 2014-2019 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 email : webmaster.salome@opencascade.com
20 #include "ModuleBase_DoubleSpinBox.h"
21 #include "ModuleBase_Tools.h"
24 #include <QDoubleValidator>
30 const double PSEUDO_ZERO = 1.e-20;
33 \class ModuleBase_DoubleSpinBox
34 \brief Enhanced version of the Qt's double spin box.
36 The ModuleBase_DoubleSpinBox class represents the widget for entering the
37 floating point values. In addition to the functionality provided by
38 QDoubleSpinBox, this class supports "cleared" state - this is the
39 state corresponding to "None" (or empty) entered value.
41 To set "cleared" state use setCleared() method. To check if the spin
42 box stores "cleared" state, use isCleared() method.
45 if (myDblSpinBox->isCleared()) {
46 ... // process "None" state
49 double value = myDblSpinBox->value();
50 ... // process entered value
54 Another useful feature is possibility to use scientific notation (e.g. 1.234e+18)
55 for the widget text. To enable this, negative precision should be specified either
56 through a constructor or using setPrecision() method.
58 Note that "decimals" property of QDoubleSpinBox is almost completely substituted
59 by "myPrecision" field of ModuleBase_DoubleSpinBox class. "decimals" is still used
60 for proper size hint calculation and for rounding minimum and maximum bounds of
67 Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value,
68 a step value of 1.0 and a precision of 2 decimal places.
69 The value is initially set to 0.00.
71 \param theParent parent object
72 \param thePrecision precision of values input
74 ModuleBase_DoubleSpinBox::ModuleBase_DoubleSpinBox(QWidget* theParent, int thePrecision)
75 : QDoubleSpinBox(theParent),
78 setLocale(ModuleBase_Tools::doubleLocale());
80 // MPV 15/09/2014: this must be set before setDecimals;
81 // otherwise in release mode setDecimals may crash
82 myPrecision = thePrecision;
84 // Use precision equal to default Qt decimals
85 // it's necessary to set decimals before the range setting,
86 // by default Qt rounds boundaries to 2 decimals at setRange
87 setDecimals(qAbs(myPrecision));
89 connect(lineEdit(), SIGNAL(textChanged( const QString& )), this,
90 SLOT(onTextChanged( const QString& )));
92 myEnabledBaseColor = palette().color(QPalette::Active, QPalette::Base);
98 ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox()
103 \brief Check if spin box is in the "cleared" state.
104 \return \c true if spin box is cleared
107 bool ModuleBase_DoubleSpinBox::isCleared() const
113 \brief Change "cleared" status of the spin box.
114 \param on new "cleared" status
117 void ModuleBase_DoubleSpinBox::setCleared(const bool on)
123 setSpecialValueText(specialValueText());
127 \brief Set precision of the spin box
129 If precision value is less than 0, the 'g' format is used for value output,
130 otherwise 'f' format is used.
132 \param prec new precision value.
135 void ModuleBase_DoubleSpinBox::setPrecision(const int prec)
137 int newPrec = qAbs(prec);
138 int oldPrec = qAbs(myPrecision);
140 if (newPrec != oldPrec)
145 \brief Get precision value of the spin box
146 \return current precision value
149 int ModuleBase_DoubleSpinBox::getPrecision() const
155 \brief Interpret text entered by the user as a value.
156 \param text text entered by the user
160 double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const
163 return text.toDouble();
165 return QDoubleSpinBox::valueFromText(text);
169 \brief This function is used by the spin box whenever it needs to display
172 \param val spin box value
173 \return text representation of the value
176 QString ModuleBase_DoubleSpinBox::textFromValue(double val) const
178 QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision));
179 return removeTrailingZeroes(s);
183 \brief Return source string with removed leading and trailing zeros.
184 \param src source string
185 \return resulting string
187 QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const
189 QString delim(locale().decimalPoint());
191 int idx = src.lastIndexOf(delim);
195 QString iPart = src.left(idx);
196 QString fPart = src.mid(idx + 1);
198 int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+"));
200 ePart = fPart.mid(idx1);
201 fPart = fPart.left(idx1);
204 fPart.remove(QRegExp("0+$"));
207 if (!fPart.isEmpty())
208 res += delim + fPart;
215 \brief Perform \a steps increment/decrement steps.
217 The \a steps value can be any integer number. If it is > 0,
218 the value incrementing is done, otherwise value is decremented
221 \param steps number of increment/decrement steps
223 void ModuleBase_DoubleSpinBox::stepBy(int steps)
227 QDoubleSpinBox::stepBy(steps);
228 double tmpval = value();
229 if (qAbs(tmpval) < PSEUDO_ZERO)
231 if (tmpval < minimum())
233 else if (tmpval > maximum())
239 \brief This function is used to determine whether input is valid.
240 \param str currently entered value
241 \param pos cursor position in the string
242 \return validating operation result
244 QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const
246 QString pref = this->prefix();
247 QString suff = this->suffix();
248 uint overhead = pref.length() + suff.length();
249 QValidator::State state = QValidator::Invalid;
251 QDoubleValidator v(NULL);
253 // If 'g' format is used (myPrecision < 0), then
254 // myPrecision - 1 digits are allowed after the decimal point.
255 // Otherwise, expect myPrecision digits after the decimal point.
256 int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision;
258 v.setLocale(this->locale());
260 v.setBottom(minimum());
263 myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation);
266 state = v.validate(str, pos);
268 if ((uint)(str.length()) >= overhead &&
269 str.startsWith(pref) &&
270 str.right(suff.length()) == suff) {
271 QString core = str.mid(pref.length(), str.length() - overhead);
272 int corePos = pos - pref.length();
273 state = v.validate(core, corePos);
274 pos = corePos + pref.length();
275 str.replace(pref.length(), str.length() - overhead, core);
277 state = v.validate(str, pos);
278 if (state == QValidator::Invalid) {
279 QString special = this->specialValueText().trimmed();
280 QString candidate = str.trimmed();
281 if (special.startsWith(candidate)) {
282 if (candidate.length() == special.length())
283 state = QValidator::Acceptable;
285 state = QValidator::Intermediate;
291 // Treat values outside (min; max) range as Invalid
292 // This check is enabled by assigning "strict_validity_check" dynamic property
293 // with value "true" to the spin box instance.
294 if (state == QValidator::Intermediate) {
296 double val = str.toDouble(&isOk);
298 QVariant propVal = property("strict_validity_check");
299 if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) {
300 if (val < minimum() || val > maximum())
301 state = QValidator::Invalid;
303 } else if (myPrecision < 0) {
304 // Consider too large negative exponent as Invalid
305 QChar e(locale().exponential());
306 int epos = str.indexOf(e, 0, Qt::CaseInsensitive);
308 epos++; // Skip exponential symbol itself
309 QString exponent = str.right(str.length() - epos);
310 int expValue = exponent.toInt(&isOk);
311 if (isOk && expValue < std::numeric_limits<double>::min_exponent10)
312 state = QValidator::Invalid;
321 \brief Called when user enters the text in the spin box.
323 void ModuleBase_DoubleSpinBox::onTextChanged(const QString& )
328 void ModuleBase_DoubleSpinBox::setValueEnabled(const bool& theEnable)
330 setReadOnly(!theEnable);
332 QPalette aPal = palette();
333 aPal.setColor(QPalette::All, QPalette::Base,
334 theEnable? myEnabledBaseColor : aPal.color(QPalette::Disabled, QPalette::Base));