fix(clipboard): format null attribute values in system clipboard

Numeric attributes with a NULL value were represented with a 0 value
in the system text and html clipboard.

Related to #57710.
This commit is contained in:
Jacky Volpes 2024-06-21 17:49:22 +02:00 committed by Nyall Dawson
parent 5caed99074
commit baec93e78f
2 changed files with 46 additions and 26 deletions

View File

@ -200,11 +200,14 @@ void QgsClipboard::generateClipboardText( QString &textContent, QString &htmlCon
if ( useJSONFromVariant )
{
value = QString::fromStdString( QgsJsonUtils::jsonFromVariant( attributes.at( idx ) ).dump() );
value = QString::fromStdString( QgsJsonUtils::jsonFromVariant( variant ).dump() );
}
else
{
value = attributes.at( idx ).toString();
if ( QgsVariantUtils::isNull( variant ) && variant.isValid() )
value = "";
else
value = variant.toString();
}
if ( value.contains( '\n' ) || value.contains( '\t' ) )
@ -215,11 +218,14 @@ void QgsClipboard::generateClipboardText( QString &textContent, QString &htmlCon
}
if ( useJSONFromVariant )
{
value = QString::fromStdString( QgsJsonUtils::jsonFromVariant( attributes.at( idx ) ).dump() );
value = QString::fromStdString( QgsJsonUtils::jsonFromVariant( variant ).dump() );
}
else
{
value = attributes.at( idx ).toString();
if ( QgsVariantUtils::isNull( variant ) && variant.isValid() )
value = "";
else
value = variant.toString();
}
value.replace( '\n', QLatin1String( "<br>" ) ).replace( '\t', QLatin1String( "&emsp;" ) );
htmlFields += QStringLiteral( "<td>%1</td>" ).arg( value );

View File

@ -131,18 +131,27 @@ void TestQgisAppClipboard::copyToText()
//set clipboard to some QgsFeatures
QgsFields fields;
fields.append( QgsField( QStringLiteral( "int_field" ), QMetaType::Type::Int ) );
fields.append( QgsField( QStringLiteral( "double_field" ), QMetaType::Type::Double ) );
fields.append( QgsField( QStringLiteral( "string_field" ), QMetaType::Type::QString ) );
QgsFeature feat( fields, 5 );
feat.setAttribute( QStringLiteral( "int_field" ), 9 );
feat.setAttribute( QStringLiteral( "double_field" ), 9.9 );
feat.setAttribute( QStringLiteral( "string_field" ), "val" );
feat.setGeometry( QgsGeometry( new QgsPoint( 5, 6 ) ) );
QgsFeature feat2( fields, 6 );
feat2.setAttribute( QStringLiteral( "int_field" ), 19 );
feat2.setAttribute( QStringLiteral( "double_field" ), 19.19 );
feat2.setAttribute( QStringLiteral( "string_field" ), "val2" );
feat2.setGeometry( QgsGeometry( new QgsPoint( 7, 8 ) ) );
QgsFeature feat3( fields, 7 ); // NULL field values
feat3.setAttribute( QStringLiteral( "int_field" ), QVariant( QVariant::Int ) );
feat3.setAttribute( QStringLiteral( "double_field" ), QVariant( QVariant::Double ) );
feat3.setAttribute( QStringLiteral( "string_field" ), QVariant( QVariant::String ) );
feat3.setGeometry( QgsGeometry( new QgsPoint( 9, 10 ) ) );
QgsFeatureStore feats;
feats.addFeature( feat );
feats.addFeature( feat2 );
feats.addFeature( feat3 );
feats.setFields( fields );
mQgisApp->clipboard()->replaceWithCopyOf( feats );
@ -151,30 +160,32 @@ void TestQgisAppClipboard::copyToText()
settings.setEnumValue( QStringLiteral( "/qgis/copyFeatureFormat" ), QgsClipboard::AttributesOnly );
QString result, resultHtml;
mQgisApp->clipboard()->generateClipboardText( result, resultHtml );
QCOMPARE( result, QString( "int_field\tstring_field\n9\tval\n19\tval2" ) );
QCOMPARE( result, QString( "int_field\tdouble_field\tstring_field\n9\t9.9\tval\n19\t19.19\tval2\n\t\t" ) );
// attributes with WKT
settings.setEnumValue( QStringLiteral( "/qgis/copyFeatureFormat" ), QgsClipboard::AttributesWithWKT );
mQgisApp->clipboard()->generateClipboardText( result, resultHtml );
QCOMPARE( result, QString( "wkt_geom\tint_field\tstring_field\nPoint (5 6)\t9\tval\nPoint (7 8)\t19\tval2" ) );
QCOMPARE( result, QString( "wkt_geom\tint_field\tdouble_field\tstring_field\nPoint (5 6)\t9\t9.9\tval\nPoint (7 8)\t19\t19.19\tval2\nPoint (9 10)\t\t\t" ) );
// attributes with WKB
settings.setEnumValue( QStringLiteral( "/qgis/copyFeatureFormat" ), QgsClipboard::AttributesWithWKB );
mQgisApp->clipboard()->generateClipboardText( result, resultHtml );
QCOMPARE( result, QString( "wkb_geom\tint_field\tstring_field\n010100000000000000000014400000000000001840\t9\tval\n01010000000000000000001c400000000000002040\t19\tval2" ) );
QCOMPARE( result, QString( "wkb_geom\tint_field\tdouble_field\tstring_field\n010100000000000000000014400000000000001840\t9\t9.9\tval\n01010000000000000000001c400000000000002040\t19\t19.19\tval2\n010100000000000000000022400000000000002440\t\t\t" ) );
// HTML test
mQgisApp->clipboard()->replaceWithCopyOf( feats );
result = mQgisApp->clipboard()->data( "text/html" );
QCOMPARE( result, QString( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"><html><head><meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/></head><body><table border=\"1\"><tr><td>wkb_geom</td><td>int_field</td><td>string_field</td></tr><tr><td>010100000000000000000014400000000000001840</td><td>9</td><td>val</td></tr><tr><td>01010000000000000000001c400000000000002040</td><td>19</td><td>val2</td></tr></table></body></html>" ) );
QCOMPARE( result, QString( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"><html><head><meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/></head><body><table border=\"1\"><tr><td>wkb_geom</td><td>int_field</td><td>double_field</td><td>string_field</td></tr><tr><td>010100000000000000000014400000000000001840</td><td>9</td><td>9.9</td><td>val</td></tr><tr><td>01010000000000000000001c400000000000002040</td><td>19</td><td>19.19</td><td>val2</td></tr><tr><td>010100000000000000000022400000000000002440</td><td></td><td></td><td></td></tr></table></body></html>" ) );
// GeoJSON
settings.setEnumValue( QStringLiteral( "/qgis/copyFeatureFormat" ), QgsClipboard::GeoJSON );
mQgisApp->clipboard()->generateClipboardText( result, resultHtml );
const QString expected = "{\"features\":[{\"geometry\":{\"coordinates\":[5.0,6.0],\"type\":\"Point\"},\"id\":5,"
"\"properties\":{\"int_field\":9,\"string_field\":\"val\"},\"type\":\"Feature\"},"
"\"properties\":{\"double_field\":9.9,\"int_field\":9,\"string_field\":\"val\"},\"type\":\"Feature\"},"
"{\"geometry\":{\"coordinates\":[7.0,8.0],\"type\":\"Point\"},\"id\":6,"
"\"properties\":{\"int_field\":19,\"string_field\":\"val2\"},\"type\":\"Feature\"}],"
"\"properties\":{\"double_field\":19.19,\"int_field\":19,\"string_field\":\"val2\"},\"type\":\"Feature\"},"
"{\"geometry\":{\"coordinates\":[9.0,10.0],\"type\":\"Point\"},\"id\":7,"
"\"properties\":{\"double_field\":null,\"int_field\":null,\"string_field\":null},\"type\":\"Feature\"}],"
"\"type\":\"FeatureCollection\"}";
QCOMPARE( result, expected );
@ -204,22 +215,25 @@ void TestQgisAppClipboard::copyToText()
QCOMPARE( y, -38 );
// test that multiline text fields are quoted to render correctly as csv files in WKT mode
QgsFeature feat3( fields, 7 );
feat3.setAttribute( QStringLiteral( "string_field" ), "Single line text" );
feat3.setAttribute( QStringLiteral( "int_field" ), 1 );
feat3.setGeometry( QgsGeometry( new QgsPoint( 5, 6 ) ) );
QgsFeature feat4( fields, 8 );
feat4.setAttribute( QStringLiteral( "string_field" ), "Unix Multiline \nText" );
feat4.setAttribute( QStringLiteral( "int_field" ), 2 );
feat4.setGeometry( QgsGeometry( new QgsPoint( 7, 8 ) ) );
QgsFeature feat5( fields, 9 );
feat5.setAttribute( QStringLiteral( "string_field" ), "Windows Multiline \r\nText" );
feat5.setAttribute( QStringLiteral( "int_field" ), 3 );
feat5.setGeometry( QgsGeometry( new QgsPoint( 9, 10 ) ) );
QgsFeature feat4( fields, 7 );
feat4.setAttribute( QStringLiteral( "string_field" ), "Single line text" );
feat4.setAttribute( QStringLiteral( "int_field" ), 1 );
feat4.setAttribute( QStringLiteral( "double_field" ), 1.1 );
feat4.setGeometry( QgsGeometry( new QgsPoint( 5, 6 ) ) );
QgsFeature feat5( fields, 8 );
feat5.setAttribute( QStringLiteral( "string_field" ), "Unix Multiline \nText" );
feat5.setAttribute( QStringLiteral( "int_field" ), 2 );
feat5.setAttribute( QStringLiteral( "double_field" ), 2.2 );
feat5.setGeometry( QgsGeometry( new QgsPoint( 7, 8 ) ) );
QgsFeature feat6( fields, 9 );
feat6.setAttribute( QStringLiteral( "string_field" ), "Windows Multiline \r\nText" );
feat6.setAttribute( QStringLiteral( "int_field" ), 3 );
feat6.setAttribute( QStringLiteral( "double_field" ), 3.3 );
feat6.setGeometry( QgsGeometry( new QgsPoint( 9, 10 ) ) );
QgsFeatureStore featsML;
featsML.addFeature( feat3 );
featsML.addFeature( feat4 );
featsML.addFeature( feat5 );
featsML.addFeature( feat6 );
featsML.setFields( fields );
mQgisApp->clipboard()->replaceWithCopyOf( featsML );
@ -227,17 +241,17 @@ void TestQgisAppClipboard::copyToText()
settings.setEnumValue( QStringLiteral( "/qgis/copyFeatureFormat" ), QgsClipboard::AttributesOnly );
mQgisApp->clipboard()->generateClipboardText( result, resultHtml );
qDebug() << result;
QCOMPARE( result, QString( "int_field\tstring_field\n1\tSingle line text\n2\t\"Unix Multiline \nText\"\n3\t\"Windows Multiline \r\nText\"" ) );
QCOMPARE( result, QString( "int_field\tdouble_field\tstring_field\n1\t1.1\tSingle line text\n2\t2.2\t\"Unix Multiline \nText\"\n3\t3.3\t\"Windows Multiline \r\nText\"" ) );
// attributes with WKT
settings.setEnumValue( QStringLiteral( "/qgis/copyFeatureFormat" ), QgsClipboard::AttributesWithWKT );
mQgisApp->clipboard()->generateClipboardText( result, resultHtml );
QCOMPARE( result, QString( "wkt_geom\tint_field\tstring_field\nPoint (5 6)\t1\tSingle line text\nPoint (7 8)\t2\t\"Unix Multiline \nText\"\nPoint (9 10)\t3\t\"Windows Multiline \r\nText\"" ) );
QCOMPARE( result, QString( "wkt_geom\tint_field\tdouble_field\tstring_field\nPoint (5 6)\t1\t1.1\tSingle line text\nPoint (7 8)\t2\t2.2\t\"Unix Multiline \nText\"\nPoint (9 10)\t3\t3.3\t\"Windows Multiline \r\nText\"" ) );
// attributes with WKB
settings.setEnumValue( QStringLiteral( "/qgis/copyFeatureFormat" ), QgsClipboard::AttributesWithWKB );
mQgisApp->clipboard()->generateClipboardText( result, resultHtml );
QCOMPARE( result, QString( "wkb_geom\tint_field\tstring_field\n010100000000000000000014400000000000001840\t1\tSingle line text\n01010000000000000000001c400000000000002040\t2\t\"Unix Multiline \nText\"\n010100000000000000000022400000000000002440\t3\t\"Windows Multiline \r\nText\"" ) );
QCOMPARE( result, QString( "wkb_geom\tint_field\tdouble_field\tstring_field\n010100000000000000000014400000000000001840\t1\t1.1\tSingle line text\n01010000000000000000001c400000000000002040\t2\t2.2\t\"Unix Multiline \nText\"\n010100000000000000000022400000000000002440\t3\t3.3\t\"Windows Multiline \r\nText\"" ) );
}