Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Fix QgsDoubleValidator for exponential sign
Also allow to use <E> and the locale exponential sign (for Ukrainian language)
  • Loading branch information
agiudiceandrea authored and github-actions[bot] committed May 5, 2023
1 parent 868d5ea commit f94d76d
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 23 deletions.
29 changes: 22 additions & 7 deletions src/gui/qgsdoublevalidator.cpp
Expand Up @@ -25,15 +25,18 @@

#include "qgsdoublevalidator.h"

const QString PERMISSIVE_DOUBLE = R"([+\-%3]?[\d]{0,1000}([\.%1][\d]{0,1000})?(e[+\-%3]?[\d]{0,%2})?)";
const QString PERMISSIVE_DOUBLE = R"([+\-%3]?[\d]{0,1000}([\.%1][\d]{0,1000})?([eE%4][+\-%3]?[\d]{0,%2})?)";

QgsDoubleValidator::QgsDoubleValidator( QObject *parent )
: QRegularExpressionValidator( parent )
, mMinimum( std::numeric_limits<qreal>::lowest() )
, mMaximum( std::numeric_limits<qreal>::max() )
{
// The regular expression accept double with point as decimal point but also the locale decimal point
const QRegularExpression reg( PERMISSIVE_DOUBLE.arg( locale().decimalPoint() ).arg( 1000 ).arg( locale().negativeSign() ) );
const QRegularExpression reg( PERMISSIVE_DOUBLE.arg( QLocale().decimalPoint() )
.arg( 1000 )
.arg( QLocale().negativeSign() )
.arg( QLocale().exponential() ) );
setRegularExpression( reg );
}

Expand All @@ -51,7 +54,10 @@ QgsDoubleValidator::QgsDoubleValidator( double bottom, double top, QObject *pare
, mMaximum( top )
{
// The regular expression accept double with point as decimal point but also the locale decimal point
const QRegularExpression reg( PERMISSIVE_DOUBLE.arg( locale().decimalPoint() ).arg( 1000 ).arg( locale().negativeSign() ) );
const QRegularExpression reg( PERMISSIVE_DOUBLE.arg( QLocale().decimalPoint() )
.arg( 1000 )
.arg( QLocale().negativeSign() )
.arg( QLocale().exponential() ) );
setRegularExpression( reg );
}

Expand All @@ -61,7 +67,10 @@ QgsDoubleValidator::QgsDoubleValidator( double bottom, double top, int decimal,
, mMaximum( top )
{
// The regular expression accept double with point as decimal point but also the locale decimal point
const QRegularExpression reg( PERMISSIVE_DOUBLE.arg( locale().decimalPoint() ).arg( QString::number( decimal ) ).arg( locale().negativeSign() ) );
const QRegularExpression reg( PERMISSIVE_DOUBLE.arg( QLocale().decimalPoint() )
.arg( QString::number( decimal ) )
.arg( QLocale().negativeSign() )
.arg( QLocale().exponential() ) );
setRegularExpression( reg );
}

Expand All @@ -71,13 +80,19 @@ QgsDoubleValidator::QgsDoubleValidator( int decimal, QObject *parent )
, mMaximum( std::numeric_limits<qreal>::max() )
{
// The regular expression accept double with point as decimal point but also the locale decimal point
const QRegularExpression reg( PERMISSIVE_DOUBLE.arg( locale().decimalPoint() ).arg( QString::number( decimal ) ).arg( locale().negativeSign() ) );
const QRegularExpression reg( PERMISSIVE_DOUBLE.arg( QLocale().decimalPoint() )
.arg( QString::number( decimal ) )
.arg( QLocale().negativeSign() )
.arg( QLocale().exponential() ) );
setRegularExpression( reg );
}

void QgsDoubleValidator::setMaxDecimals( int maxDecimals )
{
const QRegularExpression reg( PERMISSIVE_DOUBLE.arg( locale().decimalPoint() ).arg( QString::number( maxDecimals ) ).arg( locale().negativeSign() ) );
const QRegularExpression reg( PERMISSIVE_DOUBLE.arg( QLocale().decimalPoint() )
.arg( QString::number( maxDecimals ) )
.arg( QLocale().negativeSign() )
.arg( QLocale().exponential() ) );
setRegularExpression( reg );
}

Expand Down Expand Up @@ -142,7 +157,7 @@ double QgsDoubleValidator::toDouble( const QString &input, bool *ok )
// Still non ok? Try without locale's group separator
if ( ! *ok && !( QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator ) )
{
value = QLocale( ).toDouble( QString( input ).replace( QLocale().groupSeparator(), QString() ), ok );
value = QLocale().toDouble( QString( input ).replace( QLocale().groupSeparator(), QString() ), ok );
}
return value ;
}
47 changes: 31 additions & 16 deletions tests/src/gui/testqgsdoublevalidator.cpp
Expand Up @@ -70,6 +70,13 @@ void TestQgsDoubleValidator::validate_data()

QTest::newRow( "positive sign C decimal" ) << QString( "+4cd6" ) << int( QValidator::Acceptable ) << false;

QTest::newRow( "exponent <e> C negative" ) << QString( "44446ecn1" ) << int( QValidator::Acceptable ) << false;
QTest::newRow( "exponent <e> locale negative" ) << QString( "44446eln1" ) << int( QValidator::Acceptable ) << false;
QTest::newRow( "locale decimal exponent <E> positive" ) << QString( "444ld46E1" ) << int( QValidator::Acceptable ) << false;
QTest::newRow( "locale decimal exponent <E> positive sign" ) << QString( "444ld46E+1" ) << int( QValidator::Acceptable ) << false;
QTest::newRow( "locale exponent locale negative" ) << QString( "4446leln1" ) << int( QValidator::Acceptable ) << false;


// QgsDoubleValidator doesn't expect group separator but it tolerates it,
// so the result will be QValidator::Intermediate and not QValidator::Acceptable
QTest::newRow( "locale group separator + locale decimal" ) << QString( "4lg444ld6" ) << int( QValidator::Intermediate ) << false;
Expand Down Expand Up @@ -99,6 +106,12 @@ void TestQgsDoubleValidator::toDouble_data()

QTest::newRow( "positive sign C decimal" ) << QString( "+4cd6" ) << 4.6;

QTest::newRow( "exponent <e> C negative" ) << QString( "44446ecn1" ) << 4444.6;
QTest::newRow( "exponent <e> locale negative" ) << QString( "44446eln1" ) << 4444.6;
QTest::newRow( "locale decimal exponent <E> positive" ) << QString( "444ld46E1" ) << 4444.6;
QTest::newRow( "locale decimal exponent <E> positive sign" ) << QString( "444ld46E+1" ) << 4444.6;
QTest::newRow( "locale exponent locale negative" ) << QString( "44446leln1" ) << 4444.6;

// QgsDoubleValidator doesn't expect group separator but it tolerates it,
// so the result will be QValidator::Intermediate and not QValidator::Acceptable
QTest::newRow( "locale group separator + locale decimal" ) << QString( "4lg444ld6" ) << 4444.6;
Expand All @@ -116,35 +129,36 @@ void TestQgsDoubleValidator::toDouble_data()
void TestQgsDoubleValidator::validate()
{
QLineEdit *lineEdit = new QLineEdit();
QgsDoubleValidator *validator = new QgsDoubleValidator( lineEdit );

QFETCH( QString, actualState );
QFETCH( int, expState );
QFETCH( bool, negative );
QString value;
int expectedValue;

if ( negative )
validator->setRange( -10000, -4 );
else
validator->setRange( 4, 10000 );

lineEdit->setValidator( validator );

const QVector<QLocale>listLocale( {QLocale::English, QLocale::French, QLocale::German, QLocale::Italian, QLocale::NorwegianBokmal} );
const QVector<QLocale>listLocale( {QLocale::English, QLocale::French, QLocale::German, QLocale::Italian, QLocale::NorwegianBokmal, QLocale::Ukrainian} );
QLocale loc;
for ( int i = 0; i < listLocale.count(); ++i )
{
loc = listLocale.at( i );
QLocale::setDefault( loc );

QgsDoubleValidator *validator = new QgsDoubleValidator( lineEdit );
if ( negative )
validator->setRange( -10000, -4 );
else
validator->setRange( 4, 10000 );
validator->setLocale( loc );
lineEdit->setValidator( validator );

value = actualState;
value = value.replace( "ld", QLocale().decimalPoint() )
.replace( "cd", QLocale( QLocale::C ).decimalPoint() )
.replace( "lg", QLocale().groupSeparator() )
.replace( "cg", QLocale( QLocale::C ).groupSeparator() )
.replace( "ln", QLocale().negativeSign() )
.replace( "cn", QLocale( QLocale::C ).negativeSign() );
.replace( "cn", QLocale( QLocale::C ).negativeSign() )
.replace( "le", QLocale().exponential() );
expectedValue = expState;
// if the local group separator / decimal point is equal to the C one,
// expected result will be different for double with test with mixed
Expand All @@ -165,8 +179,8 @@ void TestQgsDoubleValidator::validate()
// to the C decimal point and there is no decimal point,
// in that case the value is valid, because the fall
// back check is to test after removing all group separators
if ( QLocale( ).groupSeparator() == QLocale( QLocale::C ).decimalPoint()
&& ! value.contains( QLocale( ).decimalPoint() )
if ( QLocale().groupSeparator() == QLocale( QLocale::C ).decimalPoint()
&& ! value.contains( QLocale().decimalPoint() )
&& value != "string" && expectedValue == 0 )
{
expectedValue = 1;
Expand All @@ -183,7 +197,7 @@ void TestQgsDoubleValidator::toDouble()
QString value;
double expectedValue;

const QVector<QLocale>listLocale( {QLocale::English, QLocale::French, QLocale::German, QLocale::Italian, QLocale::NorwegianBokmal} );
const QVector<QLocale>listLocale( {QLocale::English, QLocale::French, QLocale::German, QLocale::Italian, QLocale::NorwegianBokmal, QLocale::Ukrainian} );
QLocale loc;
for ( int i = 0; i < listLocale.count(); ++i )
{
Expand All @@ -195,7 +209,8 @@ void TestQgsDoubleValidator::toDouble()
.replace( "lg", QLocale().groupSeparator() )
.replace( "cg", QLocale( QLocale::C ).groupSeparator() )
.replace( "ln", QLocale().negativeSign() )
.replace( "cn", QLocale( QLocale::C ).negativeSign() );
.replace( "cn", QLocale( QLocale::C ).negativeSign() )
.replace( "le", QLocale().exponential() );
expectedValue = expValue;
// if the local group separator / decimal point is equal to the C one,
// expected result will be different for double with test with mixed
Expand All @@ -216,8 +231,8 @@ void TestQgsDoubleValidator::toDouble()
// to the C decimal point and there is no decimal point,
// in that case the value is valid, because the fall
// back check is to test after removing all group separators
if ( QLocale( ).groupSeparator() == QLocale( QLocale::C ).decimalPoint()
&& ! value.contains( QLocale( ).decimalPoint() )
if ( QLocale().groupSeparator() == QLocale( QLocale::C ).decimalPoint()
&& ! value.contains( QLocale().decimalPoint() )
&& value != "string" && expectedValue == 0 )
{
expectedValue = 44446;
Expand Down

0 comments on commit f94d76d

Please sign in to comment.