1 // Copyright (C) 2007-2021 CEA/DEN, EDF R&D, OPEN CASCADE
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 // File: QtxDoubleSpinBox.cxx
21 // Author: Sergey TELKOV
23 #include "QtxDoubleSpinBox.h"
26 #include <QDoubleValidator>
31 const double PSEUDO_ZERO = 1.e-20;
34 \class QtxDoubleSpinBox
35 \brief Enhanced version of the Qt's double spin box.
37 The QtxDoubleSpinBox 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 widegt 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 QtxDoubleSpinBox 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 parent parent object
74 QtxDoubleSpinBox::QtxDoubleSpinBox( QWidget* parent )
75 : QDoubleSpinBox( parent ),
78 #if !defined(GLOBAL_DOUBLE_CONVERSION)
79 // VSR 01/07/2010: Disable thousands separator for spin box
80 // (to avoid incosistency of double-2-string and string-2-double conversion)
81 // see issue 14540 (old id 21219)
83 loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
87 // Use precision equal to default Qt decimals
88 myPrecision = decimals();
90 connect( lineEdit(), SIGNAL( textChanged( const QString& ) ),
91 this, SLOT( onTextChanged( const QString& ) ) );
97 Constructs a spin box with specified minimum, maximum and step value.
98 The precision is set to 2 decimal places.
99 The value is initially set to the minimum value.
101 \param min spin box minimum possible value
102 \param max spin box maximum possible value
103 \param step spin box increment/decrement value
104 \param parent parent object
106 QtxDoubleSpinBox::QtxDoubleSpinBox( double min, double max, double step, QWidget* parent )
107 : QDoubleSpinBox( parent ),
110 #if !defined(GLOBAL_DOUBLE_CONVERSION)
111 // VSR 01/07/2010: Disable thousands separator for spin box
112 // (to avoid incosistency of double-2-string and string-2-double conversion)
113 // see issue 14540 (old id 21219)
115 loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
119 // Use precision equal to default Qt decimals
120 myPrecision = decimals();
124 setSingleStep( step );
126 connect( lineEdit(), SIGNAL( textChanged( const QString& ) ),
127 this, SLOT( onTextChanged( const QString& ) ) );
133 Constructs a spin box with specified minimum, maximum and step value.
134 The precision is set to <prec> decimal places.
135 The value is initially set to the minimum value.
137 \param min spin box minimum possible value
138 \param max spin box maximum possible value
139 \param step spin box increment/decrement value
140 \param prec non-negative values means the number of digits after the decimal point,
141 negative value means the maximum number of significant digits for the scientific notation
142 \param dec number of digits after the decimal point passed to base Qt class (used for correct control sizing only!)
143 \param parent parent object
145 QtxDoubleSpinBox::QtxDoubleSpinBox( double min, double max, double step, int prec, int dec, QWidget* parent )
146 : QDoubleSpinBox( parent ),
150 #if !defined(GLOBAL_DOUBLE_CONVERSION)
151 // VSR 01/07/2010: Disable thousands separator for spin box
152 // (to avoid incosistency of double-2-string and string-2-double conversion)
153 // see issue 14540 (old id 21219)
155 loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
162 setSingleStep( step );
164 connect( lineEdit(), SIGNAL( textChanged( const QString& ) ),
165 this, SLOT( onTextChanged( const QString& ) ) );
171 QtxDoubleSpinBox::~QtxDoubleSpinBox()
176 \brief Check if spin box is in the "cleared" state.
177 \return \c true if spin box is cleared
180 bool QtxDoubleSpinBox::isCleared() const
186 \brief Change "cleared" status of the spin box.
187 \param on new "cleared" status
190 void QtxDoubleSpinBox::setCleared( const bool on )
192 if ( myCleared == on )
196 setSpecialValueText( specialValueText() );
200 \brief Set precision of the spin box
202 If precision value is less than 0, the 'g' format is used for value output,
203 otherwise 'f' format is used.
205 \param prec new precision value.
208 void QtxDoubleSpinBox::setPrecision( const int prec )
210 int newPrec = qMax( prec, 0 );
211 int oldPrec = qMax( myPrecision, 0 );
213 if ( newPrec != oldPrec )
218 \brief Get precision value of the spin box
219 \return current prevision value
222 int QtxDoubleSpinBox::getPrecision() const
228 \brief Interpret text entered by the user as a value.
229 \param text text entered by the user
233 double QtxDoubleSpinBox::valueFromText( const QString& text ) const
236 return text.toDouble();
238 return QDoubleSpinBox::valueFromText(text);
242 \brief This function is used by the spin box whenever it needs to display
245 \param val spin box value
246 \return text representation of the value
249 QString QtxDoubleSpinBox::textFromValue( double val ) const
251 QString s = locale().toString( val, myPrecision >= 0 ? 'f' : 'g', qAbs( myPrecision ) );
252 return removeTrailingZeroes( s );
256 \brief Return source string with removed leading and trailing zeros.
257 \param str source string
258 \return resulting string
260 QString QtxDoubleSpinBox::removeTrailingZeroes( const QString& src ) const
262 QString delim( locale().decimalPoint() );
264 int idx = src.lastIndexOf( delim );
268 QString iPart = src.left( idx );
269 QString fPart = src.mid( idx + 1 );
271 int idx1 = fPart.lastIndexOf( QRegExp( "e[+|-]?[0-9]+" ) );
273 ePart = fPart.mid( idx1 );
274 fPart = fPart.left( idx1 );
277 fPart.remove( QRegExp( "0+$" ) );
280 if ( !fPart.isEmpty() )
281 res += delim + fPart;
288 \brief Perform \a steps increment/decrement steps.
290 The \a steps value can be any integer number. If it is > 0,
291 the value incrementing is done, otherwise value is decremented
294 \param steps number of increment/decrement steps
296 void QtxDoubleSpinBox::stepBy( int steps )
300 QDoubleSpinBox::stepBy( steps );
301 double tmpval = value();
302 if ( qAbs( tmpval ) < PSEUDO_ZERO ) tmpval = 0.;
303 if ( tmpval < minimum() ) tmpval = minimum();
304 else if ( tmpval > maximum() ) tmpval = maximum();
309 \brief This function is used to determine whether input is valid.
310 \param str currently entered value
311 \param pos cursor position in the string
312 \return validating operation result
314 QValidator::State QtxDoubleSpinBox::validate( QString& str, int& pos ) const
316 QString pref = this->prefix();
317 QString suff = this->suffix();
318 int overhead = pref.length() + suff.length();
319 QValidator::State state = QValidator::Invalid;
321 QDoubleValidator v (NULL);
323 // If 'g' format is used (myPrecision < 0), then
324 // myPrecision - 1 digits are allowed after the decimal point.
325 // Otherwise, expect myPrecision digits after the decimal point.
326 int decs = myPrecision < 0 ? qAbs( myPrecision ) - 1 : myPrecision;
328 v.setDecimals( decs );
329 v.setBottom( minimum() );
330 v.setTop( maximum() );
331 v.setNotation( myPrecision >= 0 ? QDoubleValidator::StandardNotation :
332 QDoubleValidator::ScientificNotation );
335 state = v.validate( str, pos );
338 if ( str.length() >= overhead && str.startsWith( pref ) &&
339 str.right( suff.length() ) == suff )
341 QString core = str.mid( pref.length(), str.length() - overhead );
342 int corePos = pos - pref.length();
343 state = v.validate( core, corePos );
344 pos = corePos + pref.length();
345 str.replace( pref.length(), str.length() - overhead, core );
349 state = v.validate( str, pos );
350 if ( state == QValidator::Invalid )
352 QString special = this->specialValueText().trimmed();
353 QString candidate = str.trimmed();
354 if ( special.startsWith( candidate ) )
356 if ( candidate.length() == special.length() )
357 state = QValidator::Acceptable;
359 state = QValidator::Intermediate;
365 // Treat values ouside (min; max) range as Invalid
366 // This check is enabled by assigning "strict_validity_check" dynamic property
367 // with value "true" to the spin box instance.
368 if ( state == QValidator::Intermediate ){
370 double val = str.toDouble( &isOk );
372 QVariant propVal = property( "strict_validity_check" );
373 if ( propVal.isValid() && propVal.canConvert( QVariant::Bool ) && propVal.toBool() ){
374 if ( val < minimum() || val > maximum() )
375 state = QValidator::Invalid;
378 else if ( myPrecision < 0 ){
379 // Consider too large negative exponent as Invalid
380 QChar e( locale().exponential() );
381 int epos = str.indexOf( e, 0, Qt::CaseInsensitive );
383 epos++; // Skip exponential symbol itself
384 QString exponent = str.right( str.length() - epos );
385 int expValue = exponent.toInt( &isOk );
386 if ( isOk && expValue < std::numeric_limits<double>::min_exponent10 )
387 state = QValidator::Invalid;
396 \brief Called when user enters the text in the spin box.
397 \param txt current spin box text (not used)
399 void QtxDoubleSpinBox::onTextChanged( const QString& /*txt*/ )