#include <QLineEdit>
#include <QDoubleValidator>
+#include <limits>
+
/*!
\class QtxDoubleSpinBox
\brief Enhanced version of the Qt's double spin box.
... // 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.
*/
/*!
\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 )
*/
QString QtxDoubleSpinBox::textFromValue( double val ) const
{
- QString s = QLocale().toString( val, myPrecision >= 0 ? 'f' : 'g', myPrecision == 0 ? 6 : qAbs( myPrecision ) );
+ QString s = QLocale().toString( val, myPrecision >= 0 ? 'f' : 'g', qAbs( myPrecision ) );
return removeTrailingZeroes( s );
}
*/
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 );
}
}
}
+
+ // Treat values ouside (min; max) range as Invalid
+ if ( state == QValidator::Intermediate ){
+ bool isOk;
+ double val = str.toDouble( &isOk );
+ if ( isOk ){
+ if ( val < minimum() || val > maximum() )
+ state = QValidator::Invalid;
+ }
+ else if ( myPrecision < 0 ){
+ // Consider too large negative exponent as Invalid
+ QChar e( QLocale().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;
}
#include <QKeyEvent>
#include <QLineEdit>
+#include <QToolTip>
+#include <QRegExp>
#include <string>
myDefaultValue( 0.0 ),
myIsRangeSet( false ),
myMinimum( 0.0 ),
- myMaximum( 99.99 )
+ myMaximum( 99.99 ),
+ myAcceptNames( true ),
+ myShowTip( true )
{
connectSignalsAndSlots();
}
myDefaultValue( 0.0 ),
myIsRangeSet( false ),
myMinimum( min ),
- myMaximum( max )
+ myMaximum( max ),
+ myAcceptNames( true ),
+ myShowTip( true )
{
connectSignalsAndSlots();
}
\param max spin box maximum possible value
\param step spin box increment/decrement value
\param parent parent object
+ \param acceptNames if true, enables variable names in the spin box
+ \param showTip if true, makes the widget show a tooltip when invalid text is entered by the user
*/
-SalomeApp_DoubleSpinBox::SalomeApp_DoubleSpinBox( double min, double max, double step, int prec, int dec, QWidget* parent )
+SalomeApp_DoubleSpinBox::SalomeApp_DoubleSpinBox( double min,
+ double max,
+ double step,
+ int prec,
+ int dec,
+ QWidget* parent,
+ bool acceptNames,
+ bool showTip )
: QtxDoubleSpinBox( min, max, step, prec, dec, parent ),
myDefaultValue( 0.0 ),
myIsRangeSet( false ),
myMinimum( min ),
- myMaximum( max )
+ myMaximum( max ),
+ myAcceptNames( acceptNames ),
+ myShowTip( showTip )
{
connectSignalsAndSlots();
}
{
}
+/*!
+ \brief Perform \a steps increment/decrement steps.
+
+ Re-implemented to handle cases when Notebook variable
+ name is specified by the user as the widget text.
+ Otherwise, simply calls the base implementation.
+
+ \param steps number of increment/decrement steps
+*/
+void SalomeApp_DoubleSpinBox::stepBy( int steps )
+{
+ QString str = text();
+ QString pref = prefix();
+ QString suff = suffix();
+
+ if ( pref.length() && str.startsWith( pref ) )
+ str = str.right( str.length() - pref.length() );
+ if ( suff.length() && str.endsWith( suff ) )
+ str = str.left( str.length() - suff.length() );
+
+ QRegExp varNameMask( "([a-z]|[A-Z]|_).*" );
+ if ( varNameMask.exactMatch( str ) )
+ return;
+
+ QtxDoubleSpinBox::stepBy( steps );
+}
+
/*!
\brief Connect signals and slots.
*/
*/
QValidator::State SalomeApp_DoubleSpinBox::validate( QString& str, int& pos ) const
{
- return QValidator::Acceptable;
+ QValidator::State res = QValidator::Invalid;
+
+ // Considering the input text as a variable name
+ // Applying Python identifier syntax:
+ // either a string starting with a letter, or a string starting with
+ // an underscore followed by at least one alphanumeric character
+ if ( isAcceptNames() ){
+ QRegExp varNameMask( "(([a-z]|[A-Z])([a-z]|[A-Z]|[0-9]|_)*)|(_([a-z]|[A-Z]|[0-9])+([a-z]|[A-Z]|[0-9]|_)*)" );
+ if ( varNameMask.exactMatch( str ) )
+ res = QValidator::Acceptable;
+
+ if ( res == QValidator::Invalid ){
+ varNameMask.setPattern( "_" );
+ if ( varNameMask.exactMatch( str ) )
+ res = QValidator::Intermediate;
+ }
+ }
+
+ // Trying to interpret the current input text as a numeric value
+ if ( res == QValidator::Invalid )
+ res = QtxDoubleSpinBox::validate( str, pos );
+
+ // Show tooltip in case of invalid manual input
+ if ( isShowTipOnValidate() && lineEdit()->hasFocus() ){
+ if ( res != QValidator::Acceptable ){ // san: do we need to warn the user in Intermediate state???
+ SalomeApp_DoubleSpinBox* that = const_cast<SalomeApp_DoubleSpinBox*>( this );
+ QPoint pos( size().width(), 0. );
+ QPoint globalPos = mapToGlobal( pos );
+ QString minVal = textFromValue( minimum() );
+ QString maxVal = textFromValue( maximum() );
+
+ // Same stuff as in QtxDoubleSpinBox::textFromValue()
+ int digits = getPrecision();
+
+ // For 'g' format, max. number of digits after the decimal point is getPrecision() - 1
+ // See also QtxDoubleSpinBox::validate()
+ if ( digits < 0 )
+ digits = qAbs( digits ) - 1;
+
+ QString templ( isAcceptNames() ? tr( "VALID_RANGE_VAR_MSG" ) : tr( "VALID_RANGE_NOVAR_MSG" ) );
+ QString msg( templ.arg( minVal ).arg( maxVal ).arg( digits ) );
+
+ // Add extra hints to the message (if any passed through dynamic properties)
+ QVariant propVal = property( "validity_tune_hint" );
+ if ( propVal.isValid() ){
+ QString extraInfo = propVal.toString();
+ if ( !extraInfo.isEmpty() ){
+ msg += "\n";
+ msg += extraInfo;
+ }
+ }
+
+ QToolTip::showText( globalPos,
+ msg,
+ that );
+ }
+ else
+ QToolTip::hideText();
+ }
+
+ return res;
}
/*!
{
setText( myTextValue );
}
+
+/*!
+ \brief Enables or disables variable names in the spin box.
+ By default, variable names are enabled.
+ \param flag If true, variable names are enabled.
+*/
+void SalomeApp_DoubleSpinBox::setAcceptNames( const bool flag )
+{
+ myAcceptNames = flag;
+}
+
+/*!
+ \brief Returns true if the spin box accepts variable names.
+*/
+bool SalomeApp_DoubleSpinBox::isAcceptNames() const
+{
+ return myAcceptNames;
+}
+
+/*!
+ \brief Enables or disables tooltips in case of invalid or intermediate-state input.
+ Tooltips are enabled by default.
+ \param flag If true, tooltips are enabled.
+*/
+void SalomeApp_DoubleSpinBox::setShowTipOnValidate( const bool flag )
+{
+ myShowTip = myShowTip;
+}
+
+/*!
+ \brief Returns true if tooltip should be shown in case of invalid or intermediate-state input.
+*/
+bool SalomeApp_DoubleSpinBox::isShowTipOnValidate() const
+{
+ return myShowTip;
+}
public:
SalomeApp_DoubleSpinBox( QWidget* = 0 );
SalomeApp_DoubleSpinBox( double, double, double = 1, QWidget* = 0 );
- SalomeApp_DoubleSpinBox( double, double, double, int, int, QWidget* = 0 );
+ SalomeApp_DoubleSpinBox( double, double, double, int, int, QWidget* = 0, bool = true, bool = true );
virtual ~SalomeApp_DoubleSpinBox();
+ virtual void stepBy( int );
+
virtual double valueFromText( const QString& ) const;
virtual QString textFromValue( double ) const;
virtual void setText(const QString& );
+ void setAcceptNames( const bool );
+ bool isAcceptNames() const;
+
+ void setShowTipOnValidate( const bool );
+ bool isShowTipOnValidate() const;
+
signals:
void textChanged( const QString& );
QString myCorrectValue;
QString myTextValue;
+
+ bool myAcceptNames;
+ bool myShowTip;
};
#endif
#include <QKeyEvent>
#include <QLineEdit>
+#include <QToolTip>
+#include <QRegExp>
#include <string>
*/
SalomeApp_IntSpinBox::SalomeApp_IntSpinBox( QWidget* parent )
: QtxIntSpinBox( parent ),
- myDefaultValue( 0 )
+ myDefaultValue( 0 ),
+ myAcceptNames( true ),
+ myShowTip( true )
{
connectSignalsAndSlots();
}
\param max spin box maximum possible value
\param step spin box increment/decrement value
\param parent parent object
+ \param acceptNames if true, enables variable names in the spin box
+ \param showTip if true, makes the widget show a tooltip when invalid text is entered by the user
*/
-SalomeApp_IntSpinBox::SalomeApp_IntSpinBox( int min, int max, int step, QWidget* parent )
+SalomeApp_IntSpinBox::SalomeApp_IntSpinBox( int min,
+ int max,
+ int step,
+ QWidget* parent,
+ bool acceptNames,
+ bool showTip )
: QtxIntSpinBox( min, max, step, parent ),
- myDefaultValue( 0 )
+ myDefaultValue( 0 ),
+ myAcceptNames( acceptNames ),
+ myShowTip( showTip )
{
connectSignalsAndSlots();
}
{
}
+
+/*!
+ \brief Perform \a steps increment/decrement steps.
+
+ Re-implemented to handle cases when Notebook variable
+ name is specified by the user as the widget text.
+ Otherwise, simply calls the base implementation.
+
+ \param steps number of increment/decrement steps
+*/
+void SalomeApp_IntSpinBox::stepBy( int steps )
+{
+ QString str = text();
+ QString pref = prefix();
+ QString suff = suffix();
+
+ if ( pref.length() && str.startsWith( pref ) )
+ str = str.right( str.length() - pref.length() );
+ if ( suff.length() && str.endsWith( suff ) )
+ str = str.left( str.length() - suff.length() );
+
+ QRegExp varNameMask( "([a-z]|[A-Z]|_).*" );
+ if ( varNameMask.exactMatch( str ) )
+ return;
+
+ QtxIntSpinBox::stepBy( steps );
+}
+
/*!
\brief Connect signals and slots.
*/
*/
QValidator::State SalomeApp_IntSpinBox::validate( QString& str, int& pos ) const
{
- return QValidator::Acceptable;
+ //return QValidator::Acceptable;
+ QValidator::State res = QValidator::Invalid;
+
+ // Considering the input text as a variable name
+ // Applying Python identifier syntax:
+ // either a string starting with a letter, or a string starting with
+ // an underscore followed by at least one alphanumeric character
+ if ( isAcceptNames() ){
+ QRegExp varNameMask( "(([a-z]|[A-Z])([a-z]|[A-Z]|[0-9]|_)*)|(_([a-z]|[A-Z]|[0-9])+([a-z]|[A-Z]|[0-9]|_)*)" );
+ if ( varNameMask.exactMatch( str ) )
+ res = QValidator::Acceptable;
+
+ if ( res == QValidator::Invalid ){
+ varNameMask.setPattern( "_" );
+ if ( varNameMask.exactMatch( str ) )
+ res = QValidator::Intermediate;
+ }
+ }
+
+ // Trying to interpret the current input text as a numeric value
+ if ( res == QValidator::Invalid )
+ res = QtxIntSpinBox::validate( str, pos );
+
+ // Show tooltip in case of invalid manual input
+ if ( isShowTipOnValidate() && lineEdit()->hasFocus() ){
+ if ( res != QValidator::Acceptable ){ // san: do we need to warn the user in Intermediate state???
+ SalomeApp_IntSpinBox* that = const_cast<SalomeApp_IntSpinBox*>( this );
+ QPoint pos( size().width(), 0. );
+ QPoint globalPos = mapToGlobal( pos );
+ QString minVal = textFromValue( minimum() );
+ QString maxVal = textFromValue( maximum() );
+
+ QString templ( isAcceptNames() ? tr( "VALID_RANGE_VAR_MSG" ) : tr( "VALID_RANGE_NOVAR_MSG" ) );
+ QString msg( templ.arg( minVal ).arg( maxVal ) );
+
+ // Add extra hints to the message (if any passed through dynamic properties)
+ QVariant propVal = property( "validity_tune_hint" );
+ if ( propVal.isValid() ){
+ QString extraInfo = propVal.toString();
+ if ( !extraInfo.isEmpty() ){
+ msg += "\n";
+ msg += extraInfo;
+ }
+ }
+
+ QToolTip::showText( globalPos,
+ msg,
+ that );
+ }
+ else
+ QToolTip::hideText();
+ }
+
+ return res;
}
/*!
{
setText( myTextValue );
}
+
+/*!
+ \brief Enables or disables variable names in the spin box.
+ By default, variable names are enabled.
+ \param flag If true, variable names are enabled.
+*/
+void SalomeApp_IntSpinBox::setAcceptNames( const bool flag )
+{
+ myAcceptNames = flag;
+}
+
+/*!
+ \brief Returns true if the spin box accepts variable names.
+*/
+bool SalomeApp_IntSpinBox::isAcceptNames() const
+{
+ return myAcceptNames;
+}
+
+/*!
+ \brief Enables or disables tooltips in case of invalid or intermediate-state input.
+ Tooltips are enabled by default.
+ \param flag If true, tooltips are enabled.
+*/
+void SalomeApp_IntSpinBox::setShowTipOnValidate( const bool flag )
+{
+ myShowTip = myShowTip;
+}
+
+/*!
+ \brief Returns true if tooltip should be shown in case of invalid or intermediate-state input.
+*/
+bool SalomeApp_IntSpinBox::isShowTipOnValidate() const
+{
+ return myShowTip;
+}
public:
SalomeApp_IntSpinBox( QWidget* = 0 );
- SalomeApp_IntSpinBox( int, int, int = 1, QWidget* = 0 );
+ SalomeApp_IntSpinBox( int, int, int = 1, QWidget* = 0, bool = true, bool = true );
virtual ~SalomeApp_IntSpinBox();
+ virtual void stepBy( int );
+
virtual int valueFromText( const QString& ) const;
virtual QString textFromValue( int ) const;
virtual void setText(const QString& );
+ void setAcceptNames( const bool );
+ bool isAcceptNames() const;
+
+ void setShowTipOnValidate( const bool );
+ bool isShowTipOnValidate() const;
+
signals:
void textChanged( const QString& );
QString myCorrectValue;
QString myTextValue;
+
+ bool myAcceptNames;
+ bool myShowTip;
};
#endif
<translation>Failed to update study!</translation>
</message>
</context>
+<context>
+ <name>SalomeApp_DoubleSpinBox</name>
+ <message>
+ <source>VALID_RANGE_VAR_MSG</source>
+ <translation>Specify either a variable name or
+a floating-point value in range ( %1; %2 )
+with %3-digit precision</translation>
+ </message>
+ <message>
+ <source>VALID_RANGE_NOVAR_MSG</source>
+ <translation>Specify a floating-point value in range ( %1; %2 )
+with %3-digit precision</translation>
+ </message>
+</context>
+<context>
+ <name>SalomeApp_IntSpinBox</name>
+ <message>
+ <source>VALID_RANGE_VAR_MSG</source>
+ <translation>Specify either a variable name or
+an integer value in range ( %1; %2 )</translation>
+ </message>
+ <message>
+ <source>VALID_RANGE_NOVAR_MSG</source>
+ <translation>Specify an integer value in range ( %1; %2 )</translation>
+ </message>
+</context>
</TS>