Skip to content

Commit

Permalink
Merge pull request #7188 from elpaso/locale-formatting
Browse files Browse the repository at this point in the history
[bugfix] Fix double precision I/O in form widgets
  • Loading branch information
elpaso committed Jun 12, 2018
2 parents e899849 + d9afb89 commit 4e37f38
Show file tree
Hide file tree
Showing 7 changed files with 371 additions and 54 deletions.
16 changes: 12 additions & 4 deletions src/app/vertextool/qgsvertexeditor.cpp
Expand Up @@ -211,15 +211,23 @@ bool QgsVertexEditorModel::setData( const QModelIndex &index, const QVariant &va
return false;
}

double x = ( index.column() == 0 ? value.toDouble() : mSelectedFeature->vertexMap().at( index.row() )->point().x() );
double y = ( index.column() == 1 ? value.toDouble() : mSelectedFeature->vertexMap().at( index.row() )->point().y() );
// Get double value wrt current locale.
bool ok;
double doubleValue = QLocale().toDouble( value.toString(), &ok );
// If not valid and locale's decimal point is not '.' let's try with english locale
if ( ! ok && QLocale().decimalPoint() != '.' )
{
doubleValue = QLocale( QLocale::English ).toDouble( value.toString() );
}

double x = ( index.column() == 0 ? doubleValue : mSelectedFeature->vertexMap().at( index.row() )->point().x() );
double y = ( index.column() == 1 ? doubleValue : mSelectedFeature->vertexMap().at( index.row() )->point().y() );

if ( index.column() == mRCol ) // radius modified
{
if ( index.row() == 0 || index.row() >= mSelectedFeature->vertexMap().count() - 1 )
return false;

double r = value.toDouble();
double x1 = mSelectedFeature->vertexMap().at( index.row() - 1 )->point().x();
double y1 = mSelectedFeature->vertexMap().at( index.row() - 1 )->point().y();
double x2 = x;
Expand All @@ -228,7 +236,7 @@ bool QgsVertexEditorModel::setData( const QModelIndex &index, const QVariant &va
double y3 = mSelectedFeature->vertexMap().at( index.row() + 1 )->point().y();

QgsPoint result;
if ( QgsGeometryUtils::segmentMidPoint( QgsPoint( x1, y1 ), QgsPoint( x3, y3 ), result, r, QgsPoint( x2, y2 ) ) )
if ( QgsGeometryUtils::segmentMidPoint( QgsPoint( x1, y1 ), QgsPoint( x3, y3 ), result, doubleValue, QgsPoint( x2, y2 ) ) )
{
x = result.x();
y = result.y();
Expand Down
112 changes: 109 additions & 3 deletions src/core/qgsfield.cpp
Expand Up @@ -22,6 +22,7 @@

#include <QDataStream>
#include <QIcon>
#include <QLocale>

/***************************************************************************
* This class is considered CRITICAL and any change MUST be accompanied with
Expand Down Expand Up @@ -208,9 +209,51 @@ QString QgsField::displayString( const QVariant &v ) const
return QgsApplication::nullRepresentation();
}

if ( d->type == QVariant::Double && d->precision > 0 )
return QString::number( v.toDouble(), 'f', d->precision );

// Special treatment for numeric types if group separator is set or decimalPoint is not a dot
if ( d->type == QVariant::Double )
{
// Locales with decimal point != '.' or that require group separator: use QLocale
if ( QLocale().decimalPoint() != '.' ||
!( QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator ) )
{
if ( d->precision > 0 )
{
return QLocale().toString( v.toDouble(), 'f', d->precision );
}
else
{
// Precision is not set, let's guess it from the
// standard conversion to string
QString s( v.toString() );
int dotPosition( s.indexOf( '.' ) );
int precision;
if ( dotPosition < 0 )
{
precision = 0;
}
else
{
precision = s.length() - dotPosition - 1;
}
return QLocale().toString( v.toDouble(), 'f', precision );
}
}
// Default for doubles with precision
else if ( d->type == QVariant::Double && d->precision > 0 )
{
return QString::number( v.toDouble(), 'f', d->precision );
}
}
// Other numeric types than doubles
else if ( isNumeric() &&
! QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator )
{
bool ok;
qlonglong converted( v.toLongLong( &ok ) );
if ( ok )
return QLocale().toString( converted );
}
// Fallback if special rules do not apply
return v.toString();
}

Expand All @@ -234,6 +277,68 @@ bool QgsField::convertCompatible( QVariant &v ) const
return false;
}

// Give it a chance to convert to double since for not '.' locales
// we accept both comma and dot as decimal point
if ( d->type == QVariant::Double && v.type() == QVariant::String )
{
QVariant tmp( v );
if ( !tmp.convert( d->type ) )
{
// This might be a string with thousand separator: use locale to convert
bool ok;
double d = QLocale().toDouble( v.toString(), &ok );
if ( ok )
{
v = QVariant( d );
return true;
}
// For not 'dot' locales, we also want to accept '.'
if ( QLocale().decimalPoint() != '.' )
{
d = QLocale( QLocale::English ).toDouble( v.toString(), &ok );
if ( ok )
{
v = QVariant( d );
return true;
}
}
}
}

// For string representation of an int we also might have thousand separator
if ( d->type == QVariant::Int && v.type() == QVariant::String )
{
QVariant tmp( v );
if ( !tmp.convert( d->type ) )
{
// This might be a string with thousand separator: use locale to convert
bool ok;
int i = QLocale().toInt( v.toString(), &ok );
if ( ok )
{
v = QVariant( i );
return true;
}
}
}

// For string representation of a long we also might have thousand separator
if ( d->type == QVariant::LongLong && v.type() == QVariant::String )
{
QVariant tmp( v );
if ( !tmp.convert( d->type ) )
{
// This might be a string with thousand separator: use locale to convert
bool ok;
qlonglong l = QLocale().toLongLong( v.toString(), &ok );
if ( ok )
{
v = QVariant( l );
return true;
}
}
}

//String representations of doubles in QVariant will return false to convert( QVariant::Int )
//work around this by first converting to double, and then checking whether the double is convertible to int
if ( d->type == QVariant::Int && v.canConvert( QVariant::Double ) )
Expand All @@ -258,6 +363,7 @@ bool QgsField::convertCompatible( QVariant &v ) const
return true;
}


if ( !v.convert( d->type ) )
{
v = QVariant( d->type );
Expand Down
4 changes: 3 additions & 1 deletion src/gui/editorwidgets/qgstexteditwrapper.cpp
Expand Up @@ -217,7 +217,9 @@ void QgsTextEditWrapper::setWidgetValue( const QVariant &val )
v = QgsApplication::nullRepresentation();
}
else
v = val.toString();
{
v = field().displayString( val );
}

if ( mTextEdit )
{
Expand Down
28 changes: 20 additions & 8 deletions src/gui/qgsfieldvalidator.cpp
Expand Up @@ -56,7 +56,16 @@ QgsFieldValidator::QgsFieldValidator( QObject *parent, const QgsField &field, co
{
if ( mField.length() > 0 && mField.precision() > 0 )
{
QString re = QStringLiteral( "-?\\d{0,%1}(\\.\\d{0,%2})?" ).arg( mField.length() - mField.precision() ).arg( mField.precision() );
QString re;
// Also accept locale's decimalPoint if it's not a dot
if ( QLocale().decimalPoint() != '.' )
{
re = QStringLiteral( "-?\\d{0,%1}([\\.%2]\\d{0,%3})?" ).arg( mField.length() - mField.precision() ).arg( QLocale().decimalPoint() ).arg( mField.precision() );
}
else
{
re = QStringLiteral( "-?\\d{0,%1}([\\.,]\\d{0,%2})?" ).arg( mField.length() - mField.precision() ).arg( mField.precision() );
}
mValidator = new QRegExpValidator( QRegExp( re ), parent );
}
else if ( mField.length() > 0 && mField.precision() == 0 )
Expand All @@ -66,7 +75,16 @@ QgsFieldValidator::QgsFieldValidator( QObject *parent, const QgsField &field, co
}
else if ( mField.precision() > 0 )
{
QString re = QStringLiteral( "-?\\d*(\\.\\d{0,%1})?" ).arg( mField.precision() );
QString re;
// Also accept locale's decimalPoint if it's not a dot
if ( QLocale().decimalPoint() != '.' )
{
re = QStringLiteral( "-?\\d*([\\.%1]\\d{0,%2})?" ).arg( QLocale().decimalPoint(), mField.precision() );
}
else
{
re = QStringLiteral( "-?\\d*([\\.]\\d{0,%1})?" ).arg( mField.precision() );
}
mValidator = new QRegExpValidator( QRegExp( re ), parent );
}
else
Expand Down Expand Up @@ -112,12 +130,6 @@ QValidator::State QgsFieldValidator::validate( QString &s, int &i ) const
// delegate to the child validator if any
if ( mValidator )
{
// force to use the '.' as a decimal point or in case we are using QDoubleValidator
// we can get a valid number with a comma depending on current locale
// ... but this will fail subsequently when converted from string to double and
// becomes a NULL!
if ( mField.type() == QVariant::Double )
s = s.replace( ',', '.' );
QValidator::State result = mValidator->validate( s, i );
return result;
}
Expand Down

0 comments on commit 4e37f38

Please sign in to comment.