diff --git a/SortFilterProxyModel.pri b/SortFilterProxyModel.pri index 22f7a83..5c42037 100644 --- a/SortFilterProxyModel.pri +++ b/SortFilterProxyModel.pri @@ -27,7 +27,8 @@ HEADERS += $$PWD/qqmlsortfilterproxymodel.h \ $$PWD/proxyroles/singlerole.h \ $$PWD/proxyroles/regexprole.h \ $$PWD/sorters/filtersorter.h \ - $$PWD/proxyroles/filterrole.h + $$PWD/proxyroles/filterrole.h \ + $$PWD/utils/utils.h SOURCES += $$PWD/qqmlsortfilterproxymodel.cpp \ $$PWD/filters/filter.cpp \ @@ -57,4 +58,5 @@ SOURCES += $$PWD/qqmlsortfilterproxymodel.cpp \ $$PWD/proxyroles/singlerole.cpp \ $$PWD/proxyroles/regexprole.cpp \ $$PWD/sorters/filtersorter.cpp \ - $$PWD/proxyroles/filterrole.cpp + $$PWD/proxyroles/filterrole.cpp \ + $$PWD/utils/utils.cpp diff --git a/SortFilterProxyModel.qbs b/SortFilterProxyModel.qbs index 7be48f4..23ec847 100644 --- a/SortFilterProxyModel.qbs +++ b/SortFilterProxyModel.qbs @@ -57,6 +57,8 @@ Group { "sorters/sortersqmltypes.cpp", "sorters/stringsorter.cpp", "sorters/stringsorter.h", + "utils/utils.cpp", + "utils/utils.h", "qqmlsortfilterproxymodel.cpp", "qqmlsortfilterproxymodel.h" ] diff --git a/filters/filtercontainer.cpp b/filters/filtercontainer.cpp index 5bba02d..ad955e1 100644 --- a/filters/filtercontainer.cpp +++ b/filters/filtercontainer.cpp @@ -56,13 +56,13 @@ void FilterContainer::append_filter(QQmlListProperty* list, Filter* filt that->appendFilter(filter); } -int FilterContainer::count_filter(QQmlListProperty* list) +qsizetype FilterContainer::count_filter(QQmlListProperty* list) { QList* filters = static_cast*>(list->data); return filters->count(); } -Filter* FilterContainer::at_filter(QQmlListProperty* list, int index) +Filter* FilterContainer::at_filter(QQmlListProperty* list, qsizetype index) { QList* filters = static_cast*>(list->data); return filters->at(index); diff --git a/filters/filtercontainer.h b/filters/filtercontainer.h index 4fc06f3..9adf41f 100644 --- a/filters/filtercontainer.h +++ b/filters/filtercontainer.h @@ -31,8 +31,8 @@ class FilterContainer { virtual void onFiltersCleared() = 0; static void append_filter(QQmlListProperty* list, Filter* filter); - static int count_filter(QQmlListProperty* list); - static Filter* at_filter(QQmlListProperty* list, int index); + static qsizetype count_filter(QQmlListProperty* list); + static Filter* at_filter(QQmlListProperty* list, qsizetype index); static void clear_filters(QQmlListProperty* list); }; diff --git a/filters/rangefilter.cpp b/filters/rangefilter.cpp index 2a6fde2..b36adba 100644 --- a/filters/rangefilter.cpp +++ b/filters/rangefilter.cpp @@ -1,4 +1,5 @@ #include "rangefilter.h" +#include "../utils/utils.h" namespace qqsfpm { @@ -128,7 +129,7 @@ void RangeFilter::setMaximumInclusive(bool maximumInclusive) bool RangeFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const { - QVariant value = sourceData(sourceIndex, proxyModel); + const QVariant value = sourceData(sourceIndex, proxyModel); bool lessThanMin = m_minimumValue.isValid() && (m_minimumInclusive ? value < m_minimumValue : value <= m_minimumValue); bool moreThanMax = m_maximumValue.isValid() && diff --git a/filters/regexpfilter.cpp b/filters/regexpfilter.cpp index f308765..7c6b6f0 100644 --- a/filters/regexpfilter.cpp +++ b/filters/regexpfilter.cpp @@ -35,6 +35,12 @@ namespace qqsfpm { \sa syntax */ +RegExpFilter::RegExpFilter() : + m_caseSensitivity(m_regExp.patternOptions().testFlag( + QRegularExpression::CaseInsensitiveOption) ? Qt::CaseInsensitive : Qt::CaseSensitive) +{ +} + QString RegExpFilter::pattern() const { return m_pattern; @@ -51,38 +57,6 @@ void RegExpFilter::setPattern(const QString& pattern) invalidate(); } -/*! - \qmlproperty enum RegExpFilter::syntax - - The pattern used to filter the contents of the source model. - - Only the source model's value having their \l RoleFilter::roleName data matching this \l pattern with the specified \l syntax will be kept. - - \value RegExpFilter.RegExp A rich Perl-like pattern matching syntax. This is the default. - \value RegExpFilter.Wildcard This provides a simple pattern matching syntax similar to that used by shells (command interpreters) for "file globbing". - \value RegExpFilter.FixedString The pattern is a fixed string. This is equivalent to using the RegExp pattern on a string in which all metacharacters are escaped. - \value RegExpFilter.RegExp2 Like RegExp, but with greedy quantifiers. - \value RegExpFilter.WildcardUnix This is similar to Wildcard but with the behavior of a Unix shell. The wildcard characters can be escaped with the character "\". - \value RegExpFilter.W3CXmlSchema11 The pattern is a regular expression as defined by the W3C XML Schema 1.1 specification. - - \sa pattern -*/ -RegExpFilter::PatternSyntax RegExpFilter::syntax() const -{ - return m_syntax; -} - -void RegExpFilter::setSyntax(RegExpFilter::PatternSyntax syntax) -{ - if (m_syntax == syntax) - return; - - m_syntax = syntax; - m_regExp.setPatternSyntax(static_cast(syntax)); - Q_EMIT syntaxChanged(); - invalidate(); -} - /*! \qmlproperty Qt::CaseSensitivity RegExpFilter::caseSensitivity @@ -99,15 +73,18 @@ void RegExpFilter::setCaseSensitivity(Qt::CaseSensitivity caseSensitivity) return; m_caseSensitivity = caseSensitivity; - m_regExp.setCaseSensitivity(caseSensitivity); + QRegularExpression::PatternOptions patternOptions = m_regExp.patternOptions(); + if (caseSensitivity == Qt::CaseInsensitive) + patternOptions.setFlag(QRegularExpression::CaseInsensitiveOption); + m_regExp.setPatternOptions(patternOptions); Q_EMIT caseSensitivityChanged(); invalidate(); } bool RegExpFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const { - QString string = sourceData(sourceIndex, proxyModel).toString(); - return m_regExp.indexIn(string) != -1; + const QString string = sourceData(sourceIndex, proxyModel).toString(); + return m_regExp.match(string).hasMatch(); } } diff --git a/filters/regexpfilter.h b/filters/regexpfilter.h index 2c20a6a..6594564 100644 --- a/filters/regexpfilter.h +++ b/filters/regexpfilter.h @@ -3,32 +3,23 @@ #include "rolefilter.h" +#include + namespace qqsfpm { class RegExpFilter : public RoleFilter { Q_OBJECT Q_PROPERTY(QString pattern READ pattern WRITE setPattern NOTIFY patternChanged) - Q_PROPERTY(PatternSyntax syntax READ syntax WRITE setSyntax NOTIFY syntaxChanged) Q_PROPERTY(Qt::CaseSensitivity caseSensitivity READ caseSensitivity WRITE setCaseSensitivity NOTIFY caseSensitivityChanged) public: - enum PatternSyntax { - RegExp = QRegExp::RegExp, - Wildcard = QRegExp::Wildcard, - FixedString = QRegExp::FixedString, - RegExp2 = QRegExp::RegExp2, - WildcardUnix = QRegExp::WildcardUnix, - W3CXmlSchema11 = QRegExp::W3CXmlSchema11 }; - Q_ENUMS(PatternSyntax) - using RoleFilter::RoleFilter; + RegExpFilter(); + QString pattern() const; void setPattern(const QString& pattern); - PatternSyntax syntax() const; - void setSyntax(PatternSyntax syntax); - Qt::CaseSensitivity caseSensitivity() const; void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity); @@ -37,13 +28,11 @@ class RegExpFilter : public RoleFilter { Q_SIGNALS: void patternChanged(); - void syntaxChanged(); void caseSensitivityChanged(); private: - QRegExp m_regExp; - Qt::CaseSensitivity m_caseSensitivity = m_regExp.caseSensitivity(); - PatternSyntax m_syntax = static_cast(m_regExp.patternSyntax()); + QRegularExpression m_regExp; + Qt::CaseSensitivity m_caseSensitivity; QString m_pattern = m_regExp.pattern(); }; diff --git a/proxyroles/proxyrolecontainer.cpp b/proxyroles/proxyrolecontainer.cpp index f8ea665..8418fec 100644 --- a/proxyroles/proxyrolecontainer.cpp +++ b/proxyroles/proxyrolecontainer.cpp @@ -43,13 +43,13 @@ void ProxyRoleContainer::append_proxyRole(QQmlListProperty* list, Pro that->appendProxyRole(proxyRole); } -int ProxyRoleContainer::count_proxyRole(QQmlListProperty* list) +qsizetype ProxyRoleContainer::count_proxyRole(QQmlListProperty* list) { QList* ProxyRoles = static_cast*>(list->data); return ProxyRoles->count(); } -ProxyRole* ProxyRoleContainer::at_proxyRole(QQmlListProperty* list, int index) +ProxyRole* ProxyRoleContainer::at_proxyRole(QQmlListProperty* list, qsizetype index) { QList* ProxyRoles = static_cast*>(list->data); return ProxyRoles->at(index); diff --git a/proxyroles/proxyrolecontainer.h b/proxyroles/proxyrolecontainer.h index bcd932e..415b8c0 100644 --- a/proxyroles/proxyrolecontainer.h +++ b/proxyroles/proxyrolecontainer.h @@ -29,8 +29,8 @@ class ProxyRoleContainer { virtual void onProxyRolesCleared() = 0; static void append_proxyRole(QQmlListProperty* list, ProxyRole* proxyRole); - static int count_proxyRole(QQmlListProperty* list); - static ProxyRole* at_proxyRole(QQmlListProperty* list, int index); + static qsizetype count_proxyRole(QQmlListProperty* list); + static ProxyRole* at_proxyRole(QQmlListProperty* list, qsizetype index); static void clear_proxyRoles(QQmlListProperty* list); }; diff --git a/qqmlsortfilterproxymodel.cpp b/qqmlsortfilterproxymodel.cpp index bd06435..6a3e77c 100644 --- a/qqmlsortfilterproxymodel.cpp +++ b/qqmlsortfilterproxymodel.cpp @@ -92,37 +92,20 @@ void QQmlSortFilterProxyModel::setFilterRoleName(const QString& filterRoleName) QString QQmlSortFilterProxyModel::filterPattern() const { - return filterRegExp().pattern(); + return filterRegularExpression().pattern(); } void QQmlSortFilterProxyModel::setFilterPattern(const QString& filterPattern) { - QRegExp regExp = filterRegExp(); + QRegularExpression regExp = filterRegularExpression(); if (regExp.pattern() == filterPattern) return; regExp.setPattern(filterPattern); - QSortFilterProxyModel::setFilterRegExp(regExp); + QSortFilterProxyModel::setFilterRegularExpression(regExp); Q_EMIT filterPatternChanged(); } -QQmlSortFilterProxyModel::PatternSyntax QQmlSortFilterProxyModel::filterPatternSyntax() const -{ - return static_cast(filterRegExp().patternSyntax()); -} - -void QQmlSortFilterProxyModel::setFilterPatternSyntax(QQmlSortFilterProxyModel::PatternSyntax patternSyntax) -{ - QRegExp regExp = filterRegExp(); - QRegExp::PatternSyntax patternSyntaxTmp = static_cast(patternSyntax); - if (regExp.patternSyntax() == patternSyntaxTmp) - return; - - regExp.setPatternSyntax(patternSyntaxTmp); - QSortFilterProxyModel::setFilterRegExp(regExp); - Q_EMIT filterPatternSyntaxChanged(); -} - const QVariant& QQmlSortFilterProxyModel::filterValue() const { return m_filterValue; diff --git a/qqmlsortfilterproxymodel.h b/qqmlsortfilterproxymodel.h index dbe0229..bf4f32e 100644 --- a/qqmlsortfilterproxymodel.h +++ b/qqmlsortfilterproxymodel.h @@ -26,7 +26,6 @@ class QQmlSortFilterProxyModel : public QSortFilterProxyModel, Q_PROPERTY(QString filterRoleName READ filterRoleName WRITE setFilterRoleName NOTIFY filterRoleNameChanged) Q_PROPERTY(QString filterPattern READ filterPattern WRITE setFilterPattern NOTIFY filterPatternChanged) - Q_PROPERTY(PatternSyntax filterPatternSyntax READ filterPatternSyntax WRITE setFilterPatternSyntax NOTIFY filterPatternSyntaxChanged) Q_PROPERTY(QVariant filterValue READ filterValue WRITE setFilterValue NOTIFY filterValueChanged) Q_PROPERTY(QString sortRoleName READ sortRoleName WRITE setSortRoleName NOTIFY sortRoleNameChanged) @@ -37,15 +36,6 @@ class QQmlSortFilterProxyModel : public QSortFilterProxyModel, Q_PROPERTY(QQmlListProperty proxyRoles READ proxyRolesListProperty) public: - enum PatternSyntax { - RegExp = QRegExp::RegExp, - Wildcard = QRegExp::Wildcard, - FixedString = QRegExp::FixedString, - RegExp2 = QRegExp::RegExp2, - WildcardUnix = QRegExp::WildcardUnix, - W3CXmlSchema11 = QRegExp::W3CXmlSchema11 }; - Q_ENUMS(PatternSyntax) - QQmlSortFilterProxyModel(QObject* parent = 0); int count() const; @@ -59,9 +49,6 @@ class QQmlSortFilterProxyModel : public QSortFilterProxyModel, QString filterPattern() const; void setFilterPattern(const QString& filterPattern); - PatternSyntax filterPatternSyntax() const; - void setFilterPatternSyntax(PatternSyntax patternSyntax); - const QVariant& filterValue() const; void setFilterValue(const QVariant& filterValue); @@ -97,7 +84,6 @@ class QQmlSortFilterProxyModel : public QSortFilterProxyModel, void delayedChanged(); void filterRoleNameChanged(); - void filterPatternSyntaxChanged(); void filterPatternChanged(); void filterValueChanged(); @@ -109,7 +95,7 @@ class QQmlSortFilterProxyModel : public QSortFilterProxyModel, bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override; protected Q_SLOTS: - void resetInternalData(); + void resetInternalData() override; private Q_SLOTS: void queueInvalidateFilter(); diff --git a/sorters/rolesorter.cpp b/sorters/rolesorter.cpp index db2d446..d98e9b1 100644 --- a/sorters/rolesorter.cpp +++ b/sorters/rolesorter.cpp @@ -1,5 +1,6 @@ #include "rolesorter.h" #include "qqmlsortfilterproxymodel.h" +#include "../utils/utils.h" namespace qqsfpm { @@ -56,14 +57,8 @@ QPair RoleSorter::sourceData(const QModelIndex &sourceLeft, int RoleSorter::compare(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const { - QPair pair = sourceData(sourceLeft, sourceRight, proxyModel); - QVariant leftValue = pair.first; - QVariant rightValue = pair.second; - if (leftValue < rightValue) - return -1; - if (leftValue > rightValue) - return 1; - return 0; + const QPair pair = sourceData(sourceLeft, sourceRight, proxyModel); + return compareVariants(pair.first, pair.second); } } diff --git a/sorters/sortercontainer.cpp b/sorters/sortercontainer.cpp index f986e37..5255e29 100644 --- a/sorters/sortercontainer.cpp +++ b/sorters/sortercontainer.cpp @@ -56,13 +56,13 @@ void SorterContainer::append_sorter(QQmlListProperty* list, Sorter* sort that->appendSorter(sorter); } -int SorterContainer::count_sorter(QQmlListProperty* list) +qsizetype SorterContainer::count_sorter(QQmlListProperty* list) { QList* sorters = static_cast*>(list->data); return sorters->count(); } -Sorter* SorterContainer::at_sorter(QQmlListProperty* list, int index) +Sorter* SorterContainer::at_sorter(QQmlListProperty* list, qsizetype index) { QList* sorters = static_cast*>(list->data); return sorters->at(index); diff --git a/sorters/sortercontainer.h b/sorters/sortercontainer.h index 016cc6d..c60a067 100644 --- a/sorters/sortercontainer.h +++ b/sorters/sortercontainer.h @@ -31,8 +31,8 @@ class SorterContainer { virtual void onSortersCleared() = 0; static void append_sorter(QQmlListProperty* list, Sorter* sorter); - static int count_sorter(QQmlListProperty* list); - static Sorter* at_sorter(QQmlListProperty* list, int index); + static qsizetype count_sorter(QQmlListProperty* list); + static Sorter* at_sorter(QQmlListProperty* list, qsizetype index); static void clear_sorters(QQmlListProperty* list); }; diff --git a/tests/BLACKLIST b/tests/BLACKLIST new file mode 100644 index 0000000..f881ba0 --- /dev/null +++ b/tests/BLACKLIST @@ -0,0 +1,2 @@ +[StringSorterTests::test_stringSorters:doNotIgnorePunctuation] +macos diff --git a/tests/tst_rolesorter.qml b/tests/tst_rolesorter.qml index f082227..19bfde2 100644 --- a/tests/tst_rolesorter.qml +++ b/tests/tst_rolesorter.qml @@ -60,12 +60,11 @@ Item { verify(testModel.count === sorter.expectedValues.length, "Expected count " + sorter.expectedValues.length + ", actual count: " + testModel.count); - for (var i = 0; i < testModel.count; i++) - { - var modelValue = testModel.get(i, sorter.roleName); - verify(modelValue === sorter.expectedValues[i], - "Expected testModel value " + sorter.expectedValues[i] + ", actual: " + modelValue); + let actualValues = []; + for (var i = 0; i < testModel.count; i++) { + actualValues.push(testModel.get(i, sorter.roleName)); } + compare(actualValues, sorter.expectedValues); } } } diff --git a/tests/tst_stringsorter.qml b/tests/tst_stringsorter.qml index f4d4ea9..3e22c65 100644 --- a/tests/tst_stringsorter.qml +++ b/tests/tst_stringsorter.qml @@ -75,12 +75,11 @@ Item { verify(testModel.count === sorter.expectedValues.length, "Expected count " + sorter.expectedValues.length + ", actual count: " + testModel.count); - for (var i = 0; i < testModel.count; i++) - { - var modelValue = testModel.get(i, sorter.roleName); - verify(modelValue === sorter.expectedValues[i], - "Expected testModel value " + sorter.expectedValues[i] + ", actual: " + modelValue); + let actualValues = []; + for (var i = 0; i < testModel.count; i++) { + actualValues.push(testModel.get(i, sorter.roleName)); } + compare(actualValues, sorter.expectedValues); } } } diff --git a/utils/utils.cpp b/utils/utils.cpp new file mode 100644 index 0000000..e32f509 --- /dev/null +++ b/utils/utils.cpp @@ -0,0 +1,59 @@ +#include "utils.h" + +#include + +namespace qqsfpm { + +int compareVariants(const QVariant &lhs, const QVariant &rhs) +{ + // Do the QString check first because otherwise the canConvert check will get hit for strings. + if (lhs.typeId() == QMetaType::QString && rhs.typeId() == QMetaType::QString) { + const auto lhsValue = lhs.toString(); + const auto rhsValue = rhs.toString(); + if (lhsValue == rhsValue) + return 0; + return lhsValue.compare(rhsValue, Qt::CaseInsensitive); + } else if (lhs.typeId() == QMetaType::Bool && rhs.typeId() == QMetaType::Bool) { + const auto lhsValue = lhs.toBool(); + const auto rhsValue = rhs.toBool(); + if (lhsValue == rhsValue) + return 0; + // false < true. + return !lhsValue ? -1 : 1; + } else if (lhs.typeId() == QMetaType::QDate && rhs.typeId() == QMetaType::QDate) { + const auto lhsValue = lhs.toDate(); + const auto rhsValue = rhs.toDate(); + if (lhsValue == rhsValue) + return 0; + return lhsValue < rhsValue ? -1 : 1; + } else if (lhs.typeId() == QMetaType::QDateTime && rhs.typeId() == QMetaType::QDateTime) { + const auto lhsValue = lhs.toDateTime(); + const auto rhsValue = rhs.toDateTime(); + if (lhsValue == rhsValue) + return 0; + return lhsValue < rhsValue ? -1 : 1; + } else if (lhs.typeId() == QMetaType::QStringList && rhs.typeId() == QMetaType::QStringList) { + const auto lhsValue = lhs.toStringList(); + const auto rhsValue = rhs.toStringList(); + if (lhsValue == rhsValue) + return 0; + return lhsValue < rhsValue ? -1 : 1; + } else if (lhs.canConvert() && rhs.canConvert()) { + const auto lhsValue = lhs.toInt(); + const auto rhsValue = rhs.toInt(); + if (lhsValue == rhsValue) + return 0; + return lhsValue < rhsValue ? -1 : 1; + } else if (lhs.canConvert() && rhs.canConvert()) { + const auto lhsValue = lhs.toReal(); + const auto rhsValue = rhs.toReal(); + if (qFuzzyCompare(lhsValue, rhsValue)) + return 0; + return lhsValue < rhsValue ? -1 : 1; + } + + qWarning() << "Don't know how to compare" << lhs << "against" << rhs << "- returning 0"; + return 0; +} + +} diff --git a/utils/utils.h b/utils/utils.h new file mode 100644 index 0000000..199c57b --- /dev/null +++ b/utils/utils.h @@ -0,0 +1,17 @@ +#ifndef UTILS_H +#define UTILS_H + +#include + +namespace qqsfpm { + +int compareVariants(const QVariant &lhs, const QVariant &rhs); + +inline bool operator<(const QVariant &lhs, const QVariant &rhs) { return compareVariants(lhs, rhs) < 0; } +inline bool operator<=(const QVariant &lhs, const QVariant &rhs) { return compareVariants(lhs, rhs) <= 0; } +inline bool operator>(const QVariant &lhs, const QVariant &rhs) { return compareVariants(lhs, rhs) > 0; } +inline bool operator>=(const QVariant &lhs, const QVariant &rhs) { return compareVariants(lhs, rhs) >= 0; } + +} + +#endif // UTILS_H