Skip to content

Commit e657f61

Browse files
committed
Consolidate all qvariant sort methods to use qgsVariantLessThan,
make sure qgsVariantLessThan incorporates all functionality from other duplicate implementations, and add tests (fixes #14671) (cherry-picked from 647f32)
1 parent 6663fd2 commit e657f61

File tree

11 files changed

+144
-222
lines changed

11 files changed

+144
-222
lines changed

python/core/qgis.sip

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,17 @@ class QGis
233233
static double DEFAULT_HIGHLIGHT_MIN_WIDTH_MM;
234234
};
235235

236+
//! Compares two QVariant values and returns whether the first is less than the second.
237+
//! Useful for sorting lists of variants, correctly handling sorting of the various
238+
//! QVariant data types (such as strings, numeric values, dates and times)
239+
//! @see qgsVariantGreaterThan()
240+
bool qgsVariantLessThan( const QVariant& lhs, const QVariant& rhs );
241+
242+
//! Compares two QVariant values and returns whether the first is greater than the second.
243+
//! Useful for sorting lists of variants, correctly handling sorting of the various
244+
//! QVariant data types (such as strings, numeric values, dates and times)
245+
//! @see qgsVariantLessThan()
246+
bool qgsVariantGreaterThan( const QVariant& lhs, const QVariant& rhs );
236247

237248
/** Wkt string that represents a geographic coord sys
238249
* @note added to replace GEOWkt

src/core/composer/qgsatlascomposition.cpp

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -166,23 +166,10 @@ class FieldSorter
166166

167167
bool operator()( const QPair< QgsFeatureId, QString > & id1, const QPair< QgsFeatureId, QString >& id2 )
168168
{
169-
bool result = true;
170-
171-
if ( mKeys[ id1.first ].type() == QVariant::Int )
172-
{
173-
result = mKeys[ id1.first ].toInt() < mKeys[ id2.first ].toInt();
174-
}
175-
else if ( mKeys[ id1.first ].type() == QVariant::Double )
176-
{
177-
result = mKeys[ id1.first ].toDouble() < mKeys[ id2.first ].toDouble();
178-
}
179-
else if ( mKeys[ id1.first ].type() == QVariant::String )
180-
{
181-
result = ( QString::localeAwareCompare( mKeys[ id1.first ].toString(), mKeys[ id2.first ].toString() ) < 0 );
182-
}
183-
184-
return mAscending ? result : !result;
169+
return mAscending ? qgsVariantLessThan( mKeys.value( id1.first ), mKeys.value( id2.first ) )
170+
: qgsVariantGreaterThan( mKeys.value( id1.first ), mKeys.value( id2.first ) );
185171
}
172+
186173
private:
187174
QgsAtlasComposition::SorterKeys& mKeys;
188175
bool mAscending;

src/core/composer/qgscomposerattributetable.cpp

Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -32,60 +32,8 @@ QgsComposerAttributeTableCompare::QgsComposerAttributeTableCompare()
3232

3333
bool QgsComposerAttributeTableCompare::operator()( const QgsAttributeMap& m1, const QgsAttributeMap& m2 )
3434
{
35-
QVariant v1 = m1[mCurrentSortColumn];
36-
QVariant v2 = m2[mCurrentSortColumn];
37-
38-
bool less = false;
39-
40-
//sort null values first
41-
if ( v1.isNull() && v2.isNull() )
42-
{
43-
less = false;
44-
}
45-
else if ( v1.isNull() )
46-
{
47-
less = true;
48-
}
49-
else if ( v2.isNull() )
50-
{
51-
less = false;
52-
}
53-
else
54-
{
55-
//otherwise sort by converting to corresponding type and comparing
56-
switch ( v1.type() )
57-
{
58-
case QVariant::Int:
59-
case QVariant::UInt:
60-
case QVariant::LongLong:
61-
case QVariant::ULongLong:
62-
less = v1.toLongLong() < v2.toLongLong();
63-
break;
64-
65-
case QVariant::Double:
66-
less = v1.toDouble() < v2.toDouble();
67-
break;
68-
69-
case QVariant::Date:
70-
less = v1.toDate() < v2.toDate();
71-
break;
72-
73-
case QVariant::DateTime:
74-
less = v1.toDateTime() < v2.toDateTime();
75-
break;
76-
77-
case QVariant::Time:
78-
less = v1.toTime() < v2.toTime();
79-
break;
80-
81-
default:
82-
//use locale aware compare for strings
83-
less = v1.toString().localeAwareCompare( v2.toString() ) < 0;
84-
break;
85-
}
86-
}
87-
88-
return ( mAscending ? less : !less );
35+
return ( mAscending ? qgsVariantLessThan( m1[mCurrentSortColumn], m2[mCurrentSortColumn] )
36+
: qgsVariantGreaterThan( m1[mCurrentSortColumn], m2[mCurrentSortColumn] ) );
8937
}
9038

9139

src/core/composer/qgscomposerattributetablev2.cpp

Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -37,60 +37,8 @@ QgsComposerAttributeTableCompareV2::QgsComposerAttributeTableCompareV2()
3737

3838
bool QgsComposerAttributeTableCompareV2::operator()( const QgsComposerTableRow& m1, const QgsComposerTableRow& m2 )
3939
{
40-
QVariant v1 = m1[mCurrentSortColumn];
41-
QVariant v2 = m2[mCurrentSortColumn];
42-
43-
bool less = false;
44-
45-
//sort null values first
46-
if ( v1.isNull() && v2.isNull() )
47-
{
48-
less = false;
49-
}
50-
else if ( v1.isNull() )
51-
{
52-
less = true;
53-
}
54-
else if ( v2.isNull() )
55-
{
56-
less = false;
57-
}
58-
else
59-
{
60-
//otherwise sort by converting to corresponding type and comparing
61-
switch ( v1.type() )
62-
{
63-
case QVariant::Int:
64-
case QVariant::UInt:
65-
case QVariant::LongLong:
66-
case QVariant::ULongLong:
67-
less = v1.toLongLong() < v2.toLongLong();
68-
break;
69-
70-
case QVariant::Double:
71-
less = v1.toDouble() < v2.toDouble();
72-
break;
73-
74-
case QVariant::Date:
75-
less = v1.toDate() < v2.toDate();
76-
break;
77-
78-
case QVariant::DateTime:
79-
less = v1.toDateTime() < v2.toDateTime();
80-
break;
81-
82-
case QVariant::Time:
83-
less = v1.toTime() < v2.toTime();
84-
break;
85-
86-
default:
87-
//use locale aware compare for strings
88-
less = v1.toString().localeAwareCompare( v2.toString() ) < 0;
89-
break;
90-
}
91-
}
92-
93-
return ( mAscending ? less : !less );
40+
return ( mAscending ? qgsVariantLessThan( m1[mCurrentSortColumn], m2[mCurrentSortColumn] )
41+
: qgsVariantGreaterThan( m1[mCurrentSortColumn], m2[mCurrentSortColumn] ) );
9442
}
9543

9644
//

src/core/qgis.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,14 @@ void qgsFree( void *ptr )
260260

261261
bool qgsVariantLessThan( const QVariant& lhs, const QVariant& rhs )
262262
{
263+
// invalid < NULL < any value
264+
if ( !lhs.isValid() )
265+
return rhs.isValid();
266+
else if ( lhs.isNull() )
267+
return rhs.isValid() && !rhs.isNull();
268+
else if ( !rhs.isValid() || rhs.isNull() )
269+
return false;
270+
263271
switch ( lhs.type() )
264272
{
265273
case QVariant::Int:
@@ -280,6 +288,39 @@ bool qgsVariantLessThan( const QVariant& lhs, const QVariant& rhs )
280288
return lhs.toTime() < rhs.toTime();
281289
case QVariant::DateTime:
282290
return lhs.toDateTime() < rhs.toDateTime();
291+
case QVariant::Bool:
292+
return lhs.toBool() < rhs.toBool();
293+
294+
case QVariant::List:
295+
{
296+
const QList<QVariant> &lhsl = lhs.toList();
297+
const QList<QVariant> &rhsl = rhs.toList();
298+
299+
int i, n = qMin( lhsl.size(), rhsl.size() );
300+
for ( i = 0; i < n && lhsl[i].type() == rhsl[i].type() && lhsl[i].isNull() == rhsl[i].isNull() && lhsl[i] == rhsl[i]; i++ )
301+
;
302+
303+
if ( i == n )
304+
return lhsl.size() < rhsl.size();
305+
else
306+
return qgsVariantLessThan( lhsl[i], rhsl[i] );
307+
}
308+
309+
case QVariant::StringList:
310+
{
311+
const QStringList &lhsl = lhs.toStringList();
312+
const QStringList &rhsl = rhs.toStringList();
313+
314+
int i, n = qMin( lhsl.size(), rhsl.size() );
315+
for ( i = 0; i < n && lhsl[i] == rhsl[i]; i++ )
316+
;
317+
318+
if ( i == n )
319+
return lhsl.size() < rhsl.size();
320+
else
321+
return lhsl[i] < rhsl[i];
322+
}
323+
283324
default:
284325
return QString::localeAwareCompare( lhs.toString(), rhs.toString() ) < 0;
285326
}

src/core/qgis.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,17 @@ inline double qgsRound( double x )
314314
return x < 0.0 ? std::ceil( x - 0.5 ) : std::floor( x + 0.5 );
315315
}
316316

317-
bool qgsVariantLessThan( const QVariant& lhs, const QVariant& rhs );
318-
319-
bool qgsVariantGreaterThan( const QVariant& lhs, const QVariant& rhs );
317+
//! Compares two QVariant values and returns whether the first is less than the second.
318+
//! Useful for sorting lists of variants, correctly handling sorting of the various
319+
//! QVariant data types (such as strings, numeric values, dates and times)
320+
//! @see qgsVariantGreaterThan()
321+
CORE_EXPORT bool qgsVariantLessThan( const QVariant& lhs, const QVariant& rhs );
322+
323+
//! Compares two QVariant values and returns whether the first is greater than the second.
324+
//! Useful for sorting lists of variants, correctly handling sorting of the various
325+
//! QVariant data types (such as strings, numeric values, dates and times)
326+
//! @see qgsVariantLessThan()
327+
CORE_EXPORT bool qgsVariantGreaterThan( const QVariant& lhs, const QVariant& rhs );
320328

321329
CORE_EXPORT QString qgsVsiPrefix( const QString& path );
322330

src/core/symbology-ng/qgssymbollayerv2utils.cpp

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3590,40 +3590,6 @@ void QgsSymbolLayerV2Utils::premultiplyColor( QColor &rgb, int alpha )
35903590
}
35913591
}
35923592

3593-
#if 0
3594-
static bool _QVariantLessThan( const QVariant& lhs, const QVariant& rhs )
3595-
{
3596-
switch ( lhs.type() )
3597-
{
3598-
case QVariant::Int:
3599-
return lhs.toInt() < rhs.toInt();
3600-
case QVariant::UInt:
3601-
return lhs.toUInt() < rhs.toUInt();
3602-
case QVariant::LongLong:
3603-
return lhs.toLongLong() < rhs.toLongLong();
3604-
case QVariant::ULongLong:
3605-
return lhs.toULongLong() < rhs.toULongLong();
3606-
case QVariant::Double:
3607-
return lhs.toDouble() < rhs.toDouble();
3608-
case QVariant::Char:
3609-
return lhs.toChar() < rhs.toChar();
3610-
case QVariant::Date:
3611-
return lhs.toDate() < rhs.toDate();
3612-
case QVariant::Time:
3613-
return lhs.toTime() < rhs.toTime();
3614-
case QVariant::DateTime:
3615-
return lhs.toDateTime() < rhs.toDateTime();
3616-
default:
3617-
return QString::localeAwareCompare( lhs.toString(), rhs.toString() ) < 0;
3618-
}
3619-
}
3620-
3621-
static bool _QVariantGreaterThan( const QVariant& lhs, const QVariant& rhs )
3622-
{
3623-
return ! _QVariantLessThan( lhs, rhs );
3624-
}
3625-
#endif
3626-
36273593
void QgsSymbolLayerV2Utils::sortVariantList( QList<QVariant>& list, Qt::SortOrder order )
36283594
{
36293595
if ( order == Qt::AscendingOrder )

src/gui/attributetable/qgsattributetablefiltermodel.cpp

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include <QItemSelectionModel>
1717

18+
#include "qgis.h"
1819
#include "qgsattributetablefiltermodel.h"
1920
#include "qgsattributetablemodel.h"
2021
#include "qgsvectorlayer.h"
@@ -56,39 +57,8 @@ bool QgsAttributeTableFilterModel::lessThan( const QModelIndex &left, const QMod
5657
}
5758
}
5859

59-
60-
QVariant leftData = left.data( QgsAttributeTableModel::SortRole );
61-
QVariant rightData = right.data( QgsAttributeTableModel::SortRole );
62-
63-
if ( leftData.isNull() )
64-
return true;
65-
66-
if ( rightData.isNull() )
67-
return false;
68-
69-
switch ( leftData.type() )
70-
{
71-
case QVariant::Int:
72-
case QVariant::UInt:
73-
case QVariant::LongLong:
74-
case QVariant::ULongLong:
75-
return leftData.toLongLong() < rightData.toLongLong();
76-
77-
case QVariant::Double:
78-
return leftData.toDouble() < rightData.toDouble();
79-
80-
case QVariant::Date:
81-
return leftData.toDate() < rightData.toDate();
82-
83-
case QVariant::Time:
84-
return leftData.toTime() < rightData.toTime();
85-
86-
case QVariant::DateTime:
87-
return leftData.toDateTime() < rightData.toDateTime();
88-
89-
default:
90-
return leftData.toString().localeAwareCompare( rightData.toString() ) < 0;
91-
}
60+
return qgsVariantLessThan( left.data( QgsAttributeTableModel::SortRole ),
61+
right.data( QgsAttributeTableModel::SortRole ) );
9262
}
9363

9464
void QgsAttributeTableFilterModel::sort( int column, Qt::SortOrder order )

src/gui/editorwidgets/qgsrelationreferencewidget.cpp

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,22 +34,6 @@
3434
#include "qgsvectorlayer.h"
3535
#include "qgsattributetablemodel.h"
3636

37-
bool orderByLessThan( const QgsRelationReferenceWidget::ValueRelationItem& p1
38-
, const QgsRelationReferenceWidget::ValueRelationItem& p2 )
39-
{
40-
switch ( p1.first.type() )
41-
{
42-
case QVariant::String:
43-
return p1.first.toString() < p2.first.toString();
44-
45-
case QVariant::Double:
46-
return p1.first.toDouble() < p2.first.toDouble();
47-
48-
default:
49-
return p1.first.toInt() < p2.first.toInt();
50-
}
51-
}
52-
5337
QgsRelationReferenceWidget::QgsRelationReferenceWidget( QWidget* parent )
5438
: QWidget( parent )
5539
, mEditorContext( QgsAttributeEditorContext() )

src/gui/editorwidgets/qgsvaluerelationwidgetwrapper.cpp

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "qgsvaluerelationwidgetwrapper.h"
1717

18+
#include "qgis.h"
1819
#include "qgsfield.h"
1920
#include "qgsmaplayerregistry.h"
2021
#include "qgsvaluerelationwidgetfactory.h"
@@ -27,23 +28,13 @@
2728
bool QgsValueRelationWidgetWrapper::orderByKeyLessThan( const QgsValueRelationWidgetWrapper::ValueRelationItem& p1
2829
, const QgsValueRelationWidgetWrapper::ValueRelationItem& p2 )
2930
{
30-
switch ( p1.first.type() )
31-
{
32-
case QVariant::String:
33-
return p1.first.toString() < p2.first.toString();
34-
35-
case QVariant::Double:
36-
return p1.first.toDouble() < p2.first.toDouble();
37-
38-
default:
39-
return p1.first.toInt() < p2.first.toInt();
40-
}
31+
return qgsVariantLessThan( p1.first, p2.first );
4132
}
4233

4334
bool QgsValueRelationWidgetWrapper::orderByValueLessThan( const QgsValueRelationWidgetWrapper::ValueRelationItem& p1
4435
, const QgsValueRelationWidgetWrapper::ValueRelationItem& p2 )
4536
{
46-
return p1.second < p2.second;
37+
return qgsVariantLessThan( p1.second, p2.second );
4738
}
4839

4940
QgsValueRelationWidgetWrapper::QgsValueRelationWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent )

0 commit comments

Comments
 (0)