-// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE
//
-// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
-// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
//
-// This library is free software; you can redistribute it and/or
-// modify it under the terms of the GNU Lesser General Public
-// License as published by the Free Software Foundation; either
-// version 2.1 of the License.
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
//
-// This library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Lesser General Public License for more details.
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-//
-// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
//
+
// File: QtxDoubleSpinBox.cxx
// Author: Sergey TELKOV
//
#include <QLineEdit>
#include <QDoubleValidator>
+#include <QVariant>
+
+#include <limits>
+
+const double PSEUDO_ZERO = 1.e-20;
/*!
\class QtxDoubleSpinBox
... // process entered value
}
\endcode
+
+ Another useful feature is possibility to use scientific notation (e.g. 1.234e+18)
+ for the widegt text. To enable this, negative precision should be specified either
+ through a constructor or using setPrecision() method.
+
+ Note that "decimals" property of QDoubleSpinBox is almost completely substituted
+ by "myPrecision" field of QtxDoubleSpinBox class. "decimals" is still used
+ for proper size hint calculation and for rounding minimum and maximum bounds of
+ the spin box range.
*/
/*!
*/
QtxDoubleSpinBox::QtxDoubleSpinBox( QWidget* parent )
: QDoubleSpinBox( parent ),
- myCleared( false ),
- myPrecision(0)
+ myCleared( false )
{
+ // VSR 01/07/2010: Disable thousands separator for spin box
+ // (to avoid incosistency of double-2-string and string-2-double conversion)
+ QLocale loc;
+ loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
+ setLocale(loc);
+
+ // Use precision equal to default Qt decimals
+ myPrecision = decimals();
+
connect( lineEdit(), SIGNAL( textChanged( const QString& ) ),
- this, SLOT( onTextChanged( const QString& ) ) );
+ this, SLOT( onTextChanged( const QString& ) ) );
}
/*!
*/
QtxDoubleSpinBox::QtxDoubleSpinBox( double min, double max, double step, QWidget* parent )
: QDoubleSpinBox( parent ),
- myCleared( false ),
- myPrecision( 0 )
+ myCleared( false )
{
+ // VSR 01/07/2010: Disable thousands separator for spin box
+ // (to avoid incosistency of double-2-string and string-2-double conversion)
+ QLocale loc;
+ loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
+ setLocale(loc);
+
+ // Use precision equal to default Qt decimals
+ myPrecision = decimals();
+
setMinimum( min );
setMaximum( max );
setSingleStep( step );
connect( lineEdit(), SIGNAL( textChanged( const QString& ) ),
- this, SLOT( onTextChanged( const QString& ) ) );
+ this, SLOT( onTextChanged( const QString& ) ) );
}
/*!
\brief Constructor.
Constructs a spin box with specified minimum, maximum and step value.
- The precision is set to 2 decimal places.
+ The precision is set to <prec> decimal places.
The value is initially set to the minimum value.
\param min spin box minimum possible value
\param max spin box maximum possible value
\param step spin box increment/decrement value
+ \param prec non-negative values means the number of digits after the decimal point,
+ negative value means the maximum number of significant digits for the scientific notation
+ \param dec number of digits after the decimal point passed to base Qt class (used for correct control sizing only!)
\param parent parent object
*/
QtxDoubleSpinBox::QtxDoubleSpinBox( double min, double max, double step, int prec, int dec, QWidget* parent )
myCleared( false ),
myPrecision( prec )
{
+ // VSR 01/07/2010: Disable thousands separator for spin box
+ // (to avoid incosistency of double-2-string and string-2-double conversion)
+ QLocale loc;
+ loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
+ setLocale(loc);
+
setDecimals( dec );
setMinimum( min );
setMaximum( max );
setSingleStep( step );
connect( lineEdit(), SIGNAL( textChanged( const QString& ) ),
- this, SLOT( onTextChanged( const QString& ) ) );
+ this, SLOT( onTextChanged( const QString& ) ) );
}
/*!
*/
QString QtxDoubleSpinBox::textFromValue( double val ) const
{
- QString s = QLocale().toString( val, myPrecision >= 0 ? 'f' : 'g', myPrecision == 0 ? 6 : qAbs( myPrecision ) );
+ QString s = locale().toString( val, myPrecision >= 0 ? 'f' : 'g', qAbs( myPrecision ) );
return removeTrailingZeroes( s );
}
*/
QString QtxDoubleSpinBox::removeTrailingZeroes( const QString& src ) const
{
- QString delim( QLocale().decimalPoint() );
+ QString delim( locale().decimalPoint() );
int idx = src.lastIndexOf( delim );
if ( idx == -1 )
QString iPart = src.left( idx );
QString fPart = src.mid( idx + 1 );
+ QString ePart = "";
+ int idx1 = fPart.lastIndexOf( QRegExp( "e[+|-]?[0-9]+" ) );
+ if ( idx1 >= 0 ) {
+ ePart = fPart.mid( idx1 );
+ fPart = fPart.left( idx1 );
+ }
- while ( !fPart.isEmpty() && fPart.at( fPart.length() - 1 ) == '0' )
- fPart.remove( fPart.length() - 1, 1 );
+ fPart.remove( QRegExp( "0+$" ) );
QString res = iPart;
if ( !fPart.isEmpty() )
res += delim + fPart;
+ res += ePart;
return res;
}
myCleared = false;
QDoubleSpinBox::stepBy( steps );
+ double tmpval = value();
+ if ( qAbs( tmpval ) < PSEUDO_ZERO ) tmpval = 0.;
+ if ( tmpval < minimum() ) tmpval = minimum();
+ else if ( tmpval > maximum() ) tmpval = maximum();
+ setValue( tmpval );
}
/*!
*/
QValidator::State QtxDoubleSpinBox::validate( QString& str, int& pos ) const
{
- if (myPrecision >= 0)
- return QDoubleSpinBox::validate(str, pos);
-
QString pref = this->prefix();
QString suff = this->suffix();
uint overhead = pref.length() + suff.length();
QValidator::State state = QValidator::Invalid;
-
+
QDoubleValidator v (NULL);
- v.setDecimals( decimals() );
+
+ // If 'g' format is used (myPrecision < 0), then
+ // myPrecision - 1 digits are allowed after the decimal point.
+ // Otherwise, expect myPrecision digits after the decimal point.
+ int decs = myPrecision < 0 ? qAbs( myPrecision ) - 1 : myPrecision;
+
+ v.setDecimals( decs );
v.setBottom( minimum() );
v.setTop( maximum() );
- v.setNotation( QDoubleValidator::ScientificNotation );
+ v.setNotation( myPrecision >= 0 ? QDoubleValidator::StandardNotation :
+ QDoubleValidator::ScientificNotation );
if ( overhead == 0 )
state = v.validate( str, pos );
else
{
if ( str.length() >= overhead && str.startsWith( pref ) &&
- str.right( suff.length() ) == suff )
- {
- QString core = str.mid( pref.length(), str.length() - overhead );
- int corePos = pos - pref.length();
- state = v.validate( core, corePos );
- pos = corePos + pref.length();
- str.replace( pref.length(), str.length() - overhead, core );
- }
+ str.right( suff.length() ) == suff )
+ {
+ QString core = str.mid( pref.length(), str.length() - overhead );
+ int corePos = pos - pref.length();
+ state = v.validate( core, corePos );
+ pos = corePos + pref.length();
+ str.replace( pref.length(), str.length() - overhead, core );
+ }
else
- {
- state = v.validate( str, pos );
- if ( state == QValidator::Invalid )
- {
- QString special = this->specialValueText().trimmed();
- QString candidate = str.trimmed();
- if ( special.startsWith( candidate ) )
- {
- if ( candidate.length() == special.length() )
- state = QValidator::Acceptable;
- else
- state = QValidator::Intermediate;
- }
- }
- }
+ {
+ state = v.validate( str, pos );
+ if ( state == QValidator::Invalid )
+ {
+ QString special = this->specialValueText().trimmed();
+ QString candidate = str.trimmed();
+ if ( special.startsWith( candidate ) )
+ {
+ if ( candidate.length() == special.length() )
+ state = QValidator::Acceptable;
+ else
+ state = QValidator::Intermediate;
+ }
+ }
+ }
}
+
+ // Treat values ouside (min; max) range as Invalid
+ // This check is enabled by assigning "strict_validity_check" dynamic property
+ // with value "true" to the spin box instance.
+ if ( state == QValidator::Intermediate ){
+ bool isOk;
+ double val = str.toDouble( &isOk );
+ if ( isOk ){
+ QVariant propVal = property( "strict_validity_check" );
+ if ( propVal.isValid() && propVal.canConvert( QVariant::Bool ) && propVal.toBool() ){
+ if ( val < minimum() || val > maximum() )
+ state = QValidator::Invalid;
+ }
+ }
+ else if ( myPrecision < 0 ){
+ // Consider too large negative exponent as Invalid
+ QChar e( locale().exponential() );
+ int epos = str.indexOf( e, 0, Qt::CaseInsensitive );
+ if ( epos != -1 ){
+ epos++; // Skip exponential symbol itself
+ QString exponent = str.right( str.length() - epos );
+ int expValue = exponent.toInt( &isOk );
+ if ( isOk && expValue < std::numeric_limits<double>::min_exponent10 )
+ state = QValidator::Invalid;
+ }
+ }
+ }
+
return state;
}