Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Fix QgsDoubleValidator for negative sign in some locales
Some locales use the − character [Unicode Character “−” (U+2212)] as negative sign, instead of the - character [Unicode Character “-” (U+002D)]
  • Loading branch information
agiudiceandrea authored and github-actions[bot] committed May 5, 2023
1 parent 0c0816b commit 721f4a1
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 23 deletions.
12 changes: 6 additions & 6 deletions src/gui/qgsdoublevalidator.cpp
Expand Up @@ -25,15 +25,15 @@

#include "qgsdoublevalidator.h"

const QString PERMISSIVE_DOUBLE = R"(-?[\d]{0,1000}([\.%1][\d]{0,1000})?(e[+-]?[\d]{0,%2})?)";
const QString PERMISSIVE_DOUBLE = R"([-%3]?[\d]{0,1000}([\.%1][\d]{0,1000})?(e[+\-%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 ) );
const QRegularExpression reg( PERMISSIVE_DOUBLE.arg( locale().decimalPoint() ).arg( 1000 ).arg( locale().negativeSign() ) );
setRegularExpression( reg );
}

Expand All @@ -51,7 +51,7 @@ 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 ) );
const QRegularExpression reg( PERMISSIVE_DOUBLE.arg( locale().decimalPoint() ).arg( 1000 ).arg( locale().negativeSign() ) );
setRegularExpression( reg );
}

Expand All @@ -61,7 +61,7 @@ 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 ) ) );
const QRegularExpression reg( PERMISSIVE_DOUBLE.arg( locale().decimalPoint() ).arg( QString::number( decimal ) ).arg( locale().negativeSign() ) );
setRegularExpression( reg );
}

Expand All @@ -71,13 +71,13 @@ 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 ) ) );
const QRegularExpression reg( PERMISSIVE_DOUBLE.arg( locale().decimalPoint() ).arg( QString::number( decimal ) ).arg( locale().negativeSign() ) );
setRegularExpression( reg );
}

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

Expand Down
54 changes: 37 additions & 17 deletions tests/src/gui/testqgsdoublevalidator.cpp
Expand Up @@ -58,22 +58,27 @@ void TestQgsDoubleValidator::validate_data()
{
QTest::addColumn<QString>( "actualState" );
QTest::addColumn<int>( "expState" );
QTest::addColumn<bool>( "negative" );

QTest::newRow( "C decimal" ) << QString( "4cd6" ) << int( QValidator::Acceptable );
QTest::newRow( "locale decimal" ) << QString( "4ld6" ) << int( QValidator::Acceptable );
QTest::newRow( "locale decimal" ) << QString( "4444ld6" ) << int( QValidator::Acceptable );
QTest::newRow( "C decimal" ) << QString( "4cd6" ) << int( QValidator::Acceptable ) << false;
QTest::newRow( "locale decimal" ) << QString( "4ld6" ) << int( QValidator::Acceptable ) << false;
QTest::newRow( "locale decimal" ) << QString( "4444ld6" ) << int( QValidator::Acceptable ) << false;

QTest::newRow( "C negative C decimal" ) << QString( "cn4cd6" ) << int( QValidator::Acceptable ) << true;
QTest::newRow( "locale negative locale decimal" ) << QString( "ln4ld6" ) << int( QValidator::Acceptable ) << true;
QTest::newRow( "locale negative locale decimal" ) << QString( "ln4444ld6" ) << int( QValidator::Acceptable ) << true;

// 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 );
QTest::newRow( "locale group separator misplaced + locale decimal" ) << QString( "44lg44ld6" ) << int( QValidator::Intermediate );
QTest::newRow( "locale group separator + c decimal" ) << QString( "4lg444cd6" ) << int( QValidator::Invalid );
QTest::newRow( "c group separator + locale decimal" ) << QString( "4cg444ld6" ) << int( QValidator::Invalid );
QTest::newRow( "c group separator + c decimal" ) << QString( "4cg444cd6" ) << int( QValidator::Intermediate );
QTest::newRow( "locale group separator + locale decimal" ) << QString( "4lg444ld6" ) << int( QValidator::Intermediate ) << false;
QTest::newRow( "locale group separator misplaced + locale decimal" ) << QString( "44lg44ld6" ) << int( QValidator::Intermediate ) << false;
QTest::newRow( "locale group separator + c decimal" ) << QString( "4lg444cd6" ) << int( QValidator::Invalid ) << false;
QTest::newRow( "c group separator + locale decimal" ) << QString( "4cg444ld6" ) << int( QValidator::Invalid ) << false;
QTest::newRow( "c group separator + c decimal" ) << QString( "4cg444cd6" ) << int( QValidator::Intermediate ) << false;

QTest::newRow( "outside the range + local decimal" ) << QString( "3ld6" ) << int( QValidator::Intermediate );
QTest::newRow( "outside the range + c decimal" ) << QString( "3cd6" ) << int( QValidator::Intermediate );
QTest::newRow( "string" ) << QString( "string" ) << int( QValidator::Invalid );
QTest::newRow( "outside the range + local decimal" ) << QString( "3ld6" ) << int( QValidator::Intermediate ) << false;
QTest::newRow( "outside the range + c decimal" ) << QString( "3cd6" ) << int( QValidator::Intermediate ) << false;
QTest::newRow( "string" ) << QString( "string" ) << int( QValidator::Invalid ) << false;

}

Expand All @@ -86,6 +91,10 @@ void TestQgsDoubleValidator::toDouble_data()
QTest::newRow( "locale decimal" ) << QString( "4ld6" ) << 4.6;
QTest::newRow( "locale decimal" ) << QString( "4444ld6" ) << 4444.6;

QTest::newRow( "C negative C decimal" ) << QString( "cn4cd6" ) << -4.6;
QTest::newRow( "locale negative locale decimal" ) << QString( "ln4ld6" ) << -4.6;
QTest::newRow( "locale negative locale decimal" ) << QString( "ln4444ld6" ) << -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 @@ -103,15 +112,22 @@ void TestQgsDoubleValidator::toDouble_data()
void TestQgsDoubleValidator::validate()
{
QLineEdit *lineEdit = new QLineEdit();
QgsDoubleValidator *validator = new QgsDoubleValidator( 4, 10000, lineEdit );
lineEdit->setValidator( validator );
QgsDoubleValidator *validator = new QgsDoubleValidator( lineEdit );

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

const QVector<QLocale>listLocale( {QLocale::English, QLocale::French, QLocale::German, QLocale::Italian} );
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} );
QLocale loc;
for ( int i = 0; i < listLocale.count(); ++i )
{
Expand All @@ -122,7 +138,9 @@ void TestQgsDoubleValidator::validate()
value = value.replace( "ld", QLocale().decimalPoint() )
.replace( "cd", QLocale( QLocale::C ).decimalPoint() )
.replace( "lg", QLocale().groupSeparator() )
.replace( "cg", QLocale( QLocale::C ).groupSeparator() );
.replace( "cg", QLocale( QLocale::C ).groupSeparator() )
.replace( "ln", QLocale().negativeSign() )
.replace( "cn", QLocale( QLocale::C ).negativeSign() );
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 Down Expand Up @@ -161,7 +179,7 @@ void TestQgsDoubleValidator::toDouble()
QString value;
double expectedValue;

const QVector<QLocale>listLocale( {QLocale::English, QLocale::French, QLocale::German, QLocale::Italian} );
const QVector<QLocale>listLocale( {QLocale::English, QLocale::French, QLocale::German, QLocale::Italian, QLocale::NorwegianBokmal} );
QLocale loc;
for ( int i = 0; i < listLocale.count(); ++i )
{
Expand All @@ -171,7 +189,9 @@ void TestQgsDoubleValidator::toDouble()
value = value.replace( "ld", QLocale().decimalPoint() )
.replace( "cd", QLocale( QLocale::C ).decimalPoint() )
.replace( "lg", QLocale().groupSeparator() )
.replace( "cg", QLocale( QLocale::C ).groupSeparator() );
.replace( "cg", QLocale( QLocale::C ).groupSeparator() )
.replace( "ln", QLocale().negativeSign() )
.replace( "cn", QLocale( QLocale::C ).negativeSign() );
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 Down

0 comments on commit 721f4a1

Please sign in to comment.