Add support for QRegularExpression to QSortFilterProxyModel

This patch implements the support for QRegularExpression in
QSortFilterProxyModel.

[ChangeLog][QtCore][QSFPM] QSortFilterProxyModel now supports
QRegularExpression.

Task-number: QTBUG-46810
Change-Id: If932d55f98f9b8bcf3a72c03ffd51da52cb50ad1
Reviewed-by: Christian Ehrlicher <ch.ehrlicher@gmx.de>
Reviewed-by: Luca Beldi <v.ronin@yahoo.it>
Reviewed-by: David Faure <david.faure@kdab.com>
This commit is contained in:
Samuel Gaist 2018-07-31 16:58:21 +02:00
parent bcd80eebf9
commit 346c15102b
13 changed files with 645 additions and 198 deletions

View File

@ -43,6 +43,7 @@
#include <qdebug.h> #include <qdebug.h>
#include <qdatetime.h> #include <qdatetime.h>
#include <qpair.h> #include <qpair.h>
#include <qregularexpression.h>
#include <qstringlist.h> #include <qstringlist.h>
#include <private/qabstractitemmodel_p.h> #include <private/qabstractitemmodel_p.h>
#include <private/qabstractproxymodel_p.h> #include <private/qabstractproxymodel_p.h>
@ -146,6 +147,133 @@ private:
int end; int end;
}; };
class RegularExpressionData {
private:
enum class ExpressionType {
RegExp,
#if QT_CONFIG(regularexpression)
RegularExpression
#endif
};
public:
RegularExpressionData() :
m_type(ExpressionType::RegExp)
{}
#if QT_CONFIG(regularexpression)
QRegularExpression regularExpression() const
{
if (m_type == ExpressionType::RegularExpression)
return m_regularExpression;
return QRegularExpression();
}
void setRegularExpression(const QRegularExpression &rx)
{
m_type = ExpressionType::RegularExpression;
m_regularExpression = rx;
m_regExp = QRegExp();
}
#endif
QRegExp regExp() const
{
if (m_type == ExpressionType::RegExp)
return m_regExp;
return QRegExp();
}
void setRegExp(const QRegExp &rx)
{
m_type = ExpressionType::RegExp;
m_regExp = rx;
#if QT_CONFIG(regularexpression)
m_regularExpression = QRegularExpression();
#endif
}
bool isEmpty() const
{
bool result = true;
switch (m_type) {
case ExpressionType::RegExp:
result = m_regExp.isEmpty();
break;
#if QT_CONFIG(regularexpression)
case ExpressionType::RegularExpression:
result = m_regularExpression.pattern().isEmpty();
break;
#endif
}
return result;
}
Qt::CaseSensitivity caseSensitivity() const
{
Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive;
switch (m_type) {
case ExpressionType::RegExp:
sensitivity = m_regExp.caseSensitivity();
break;
#if QT_CONFIG(regularexpression)
case ExpressionType::RegularExpression:
{
QRegularExpression::PatternOptions options = m_regularExpression.patternOptions();
if (!(options & QRegularExpression::CaseInsensitiveOption))
sensitivity = Qt::CaseSensitive;
}
break;
#endif
}
return sensitivity;
}
void setCaseSensitivity(Qt::CaseSensitivity cs)
{
switch (m_type) {
case ExpressionType::RegExp:
m_regExp.setCaseSensitivity(cs);
break;
#if QT_CONFIG(regularexpression)
case ExpressionType::RegularExpression:
{
QRegularExpression::PatternOptions options = m_regularExpression.patternOptions();
options.setFlag(QRegularExpression::CaseInsensitiveOption, cs == Qt::CaseSensitive);
m_regularExpression.setPatternOptions(options);
}
break;
#endif
}
}
bool hasMatch(const QString &str) const
{
bool result = false;
switch (m_type) {
case ExpressionType::RegExp:
result = str.contains(m_regExp);
break;
#if QT_CONFIG(regularexpression)
case ExpressionType::RegularExpression:
result = str.contains(m_regularExpression);
break;
#endif
}
return result;
}
private:
ExpressionType m_type;
QRegExp m_regExp;
#if QT_CONFIG(regularexpression)
QRegularExpression m_regularExpression;
#endif
};
class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate
{ {
Q_DECLARE_PUBLIC(QSortFilterProxyModel) Q_DECLARE_PUBLIC(QSortFilterProxyModel)
@ -171,7 +299,7 @@ public:
int filter_column; int filter_column;
int filter_role; int filter_role;
QRegExp filter_regexp; RegularExpressionData filter_data;
QModelIndex last_top_source; QModelIndex last_top_source;
bool filter_recursive; bool filter_recursive;
@ -1109,7 +1237,7 @@ void QSortFilterProxyModelPrivate::update_persistent_indexes(
*/ */
void QSortFilterProxyModelPrivate::filter_about_to_be_changed(const QModelIndex &source_parent) void QSortFilterProxyModelPrivate::filter_about_to_be_changed(const QModelIndex &source_parent)
{ {
if (!filter_regexp.pattern().isEmpty() && if (!filter_data.isEmpty() &&
source_index_mapping.constFind(source_parent) == source_index_mapping.constEnd()) source_index_mapping.constFind(source_parent) == source_index_mapping.constEnd())
create_mapping(source_parent); create_mapping(source_parent);
} }
@ -1786,9 +1914,9 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsMoved(
If a parent item doesn't match the filter, none of its children will be If a parent item doesn't match the filter, none of its children will be
shown. shown.
A common use case is to let the user specify the filter regexp, wildcard A common use case is to let the user specify the filter regular expression,
pattern, or fixed string in a QLineEdit and to connect the wildcard pattern, or fixed string in a QLineEdit and to connect the
\l{QLineEdit::textChanged()}{textChanged()} signal to setFilterRegExp(), \l{QLineEdit::textChanged()}{textChanged()} signal to setFilterRegularExpression(),
setFilterWildcard(), or setFilterFixedString() to reapply the filter. setFilterWildcard(), or setFilterFixedString() to reapply the filter.
Custom filtering behavior can be achieved by reimplementing the Custom filtering behavior can be achieved by reimplementing the
@ -1825,6 +1953,21 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsMoved(
\note Some general guidelines for subclassing models are available in the \note Some general guidelines for subclassing models are available in the
\l{Model Subclassing Reference}. \l{Model Subclassing Reference}.
\note With Qt 5, regular expression support has been improved through the
QRegularExpression class. QSortFilterProxyModel dating back prior to that
class creation, it originally supported only QRegExp. Since Qt 5.12,
QRegularExpression APIs have been added. Therefore, QRegExp APIs should be
considered deprecated and the QRegularExpression version should be used in
place.
\warning Don't mix calls to the getters and setters of different regexp types
as this will lead to unexpected results. For maximum compatibility, the original
implementation has been kept. Therefore, if, for example, a call to
setFilterRegularExpression is made followed by another one to
setFilterFixedString, the first call will setup a QRegularExpression object
to use as filter while the second will setup a QRegExp in FixedString mode.
However, this is an implementation detail that might change in the future.
\sa QAbstractProxyModel, QAbstractItemModel, {Model/View Programming}, \sa QAbstractProxyModel, QAbstractItemModel, {Model/View Programming},
{Basic Sort/Filter Model Example}, {Custom Sort/Filter Model Example}, QIdentityProxyModel {Basic Sort/Filter Model Example}, {Custom Sort/Filter Model Example}, QIdentityProxyModel
*/ */
@ -2437,17 +2580,47 @@ Qt::SortOrder QSortFilterProxyModel::sortOrder() const
QRegExp QSortFilterProxyModel::filterRegExp() const QRegExp QSortFilterProxyModel::filterRegExp() const
{ {
Q_D(const QSortFilterProxyModel); Q_D(const QSortFilterProxyModel);
return d->filter_regexp; return d->filter_data.regExp();
} }
void QSortFilterProxyModel::setFilterRegExp(const QRegExp &regExp) void QSortFilterProxyModel::setFilterRegExp(const QRegExp &regExp)
{ {
Q_D(QSortFilterProxyModel); Q_D(QSortFilterProxyModel);
d->filter_about_to_be_changed(); d->filter_about_to_be_changed();
d->filter_regexp = regExp; d->filter_data.setRegExp(regExp);
d->filter_changed(); d->filter_changed();
} }
#if QT_CONFIG(regularexpression)
/*!
\since 5.12
\property QSortFilterProxyModel::filterRegularExpression
\brief the QRegularExpression used to filter the contents of the source model
Setting this property overwrites the current
\l{QSortFilterProxyModel::filterCaseSensitivity}{filterCaseSensitivity}.
By default, the QRegularExpression is an empty string matching all contents.
If no QRegularExpression or an empty string is set, everything in the source
model will be accepted.
\sa filterCaseSensitivity, setFilterWildcard(), setFilterFixedString()
*/
QRegularExpression QSortFilterProxyModel::filterRegularExpression() const
{
Q_D(const QSortFilterProxyModel);
return d->filter_data.regularExpression();
}
void QSortFilterProxyModel::setFilterRegularExpression(const QRegularExpression &regularExpression)
{
Q_D(QSortFilterProxyModel);
d->filter_about_to_be_changed();
d->filter_data.setRegularExpression(regularExpression);
d->filter_changed();
}
#endif
/*! /*!
\property QSortFilterProxyModel::filterKeyColumn \property QSortFilterProxyModel::filterKeyColumn
\brief the column where the key used to filter the contents of the \brief the column where the key used to filter the contents of the
@ -2483,16 +2656,16 @@ void QSortFilterProxyModel::setFilterKeyColumn(int column)
Qt::CaseSensitivity QSortFilterProxyModel::filterCaseSensitivity() const Qt::CaseSensitivity QSortFilterProxyModel::filterCaseSensitivity() const
{ {
Q_D(const QSortFilterProxyModel); Q_D(const QSortFilterProxyModel);
return d->filter_regexp.caseSensitivity(); return d->filter_data.caseSensitivity();
} }
void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs) void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs)
{ {
Q_D(QSortFilterProxyModel); Q_D(QSortFilterProxyModel);
if (cs == d->filter_regexp.caseSensitivity()) if (cs == d->filter_data.caseSensitivity())
return; return;
d->filter_about_to_be_changed(); d->filter_about_to_be_changed();
d->filter_regexp.setCaseSensitivity(cs); d->filter_data.setCaseSensitivity(cs);
d->filter_changed(); d->filter_changed();
} }
@ -2558,11 +2731,33 @@ void QSortFilterProxyModel::setFilterRegExp(const QString &pattern)
{ {
Q_D(QSortFilterProxyModel); Q_D(QSortFilterProxyModel);
d->filter_about_to_be_changed(); d->filter_about_to_be_changed();
d->filter_regexp.setPatternSyntax(QRegExp::RegExp); QRegExp rx(pattern);
d->filter_regexp.setPattern(pattern); d->filter_data.setRegExp(rx);
d->filter_changed(); d->filter_changed();
} }
#if QT_CONFIG(regularexpression)
/*!
\since 5.12
Sets the regular expression used to filter the contents
of the source model to \a pattern.
This method should be preferred for new code as it will use
QRegularExpression internally.
\sa setFilterCaseSensitivity(), setFilterWildcard(), setFilterFixedString(), filterRegularExpression()
*/
void QSortFilterProxyModel::setFilterRegularExpression(const QString &pattern)
{
Q_D(QSortFilterProxyModel);
d->filter_about_to_be_changed();
QRegularExpression rx(pattern);
d->filter_data.setRegularExpression(rx);
d->filter_changed();
}
#endif
/*! /*!
Sets the wildcard expression used to filter the contents Sets the wildcard expression used to filter the contents
of the source model to the given \a pattern. of the source model to the given \a pattern.
@ -2573,8 +2768,8 @@ void QSortFilterProxyModel::setFilterWildcard(const QString &pattern)
{ {
Q_D(QSortFilterProxyModel); Q_D(QSortFilterProxyModel);
d->filter_about_to_be_changed(); d->filter_about_to_be_changed();
d->filter_regexp.setPatternSyntax(QRegExp::Wildcard); QRegExp rx(pattern, d->filter_data.caseSensitivity(), QRegExp::Wildcard);
d->filter_regexp.setPattern(pattern); d->filter_data.setRegExp(rx);
d->filter_changed(); d->filter_changed();
} }
@ -2588,8 +2783,8 @@ void QSortFilterProxyModel::setFilterFixedString(const QString &pattern)
{ {
Q_D(QSortFilterProxyModel); Q_D(QSortFilterProxyModel);
d->filter_about_to_be_changed(); d->filter_about_to_be_changed();
d->filter_regexp.setPatternSyntax(QRegExp::FixedString); QRegExp rx(pattern, d->filter_data.caseSensitivity(), QRegExp::FixedString);
d->filter_regexp.setPattern(pattern); d->filter_data.setRegExp(rx);
d->filter_changed(); d->filter_changed();
} }
@ -2814,14 +3009,15 @@ bool QSortFilterProxyModel::lessThan(const QModelIndex &source_left, const QMode
bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{ {
Q_D(const QSortFilterProxyModel); Q_D(const QSortFilterProxyModel);
if (d->filter_regexp.isEmpty())
if (d->filter_data.isEmpty())
return true; return true;
if (d->filter_column == -1) { if (d->filter_column == -1) {
int column_count = d->model->columnCount(source_parent); int column_count = d->model->columnCount(source_parent);
for (int column = 0; column < column_count; ++column) { for (int column = 0; column < column_count; ++column) {
QModelIndex source_index = d->model->index(source_row, column, source_parent); QModelIndex source_index = d->model->index(source_row, column, source_parent);
QString key = d->model->data(source_index, d->filter_role).toString(); QString key = d->model->data(source_index, d->filter_role).toString();
if (key.contains(d->filter_regexp)) if (d->filter_data.hasMatch(key))
return true; return true;
} }
return false; return false;
@ -2830,7 +3026,7 @@ bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &
if (!source_index.isValid()) // the column may not exist if (!source_index.isValid()) // the column may not exist
return true; return true;
QString key = d->model->data(source_index, d->filter_role).toString(); QString key = d->model->data(source_index, d->filter_role).toString();
return key.contains(d->filter_regexp); return d->filter_data.hasMatch(key);
} }
/*! /*!

View File

@ -42,6 +42,7 @@
#include <QtCore/qabstractproxymodel.h> #include <QtCore/qabstractproxymodel.h>
#include <QtCore/qregexp.h> #include <QtCore/qregexp.h>
#include <QtCore/qregularexpression.h>
QT_REQUIRE_CONFIG(sortfilterproxymodel); QT_REQUIRE_CONFIG(sortfilterproxymodel);
@ -59,6 +60,9 @@ class Q_CORE_EXPORT QSortFilterProxyModel : public QAbstractProxyModel
Q_OBJECT Q_OBJECT
Q_PROPERTY(QRegExp filterRegExp READ filterRegExp WRITE setFilterRegExp) Q_PROPERTY(QRegExp filterRegExp READ filterRegExp WRITE setFilterRegExp)
#if QT_CONFIG(regularexpression)
Q_PROPERTY(QRegularExpression filterRegularExpression READ filterRegularExpression WRITE setFilterRegularExpression)
#endif
Q_PROPERTY(int filterKeyColumn READ filterKeyColumn WRITE setFilterKeyColumn) Q_PROPERTY(int filterKeyColumn READ filterKeyColumn WRITE setFilterKeyColumn)
Q_PROPERTY(bool dynamicSortFilter READ dynamicSortFilter WRITE setDynamicSortFilter) Q_PROPERTY(bool dynamicSortFilter READ dynamicSortFilter WRITE setDynamicSortFilter)
Q_PROPERTY(Qt::CaseSensitivity filterCaseSensitivity READ filterCaseSensitivity WRITE setFilterCaseSensitivity) Q_PROPERTY(Qt::CaseSensitivity filterCaseSensitivity READ filterCaseSensitivity WRITE setFilterCaseSensitivity)
@ -83,6 +87,9 @@ public:
QRegExp filterRegExp() const; QRegExp filterRegExp() const;
void setFilterRegExp(const QRegExp &regExp); void setFilterRegExp(const QRegExp &regExp);
QRegularExpression filterRegularExpression() const;
void setFilterRegularExpression(const QRegularExpression &regularExpression);
int filterKeyColumn() const; int filterKeyColumn() const;
void setFilterKeyColumn(int column); void setFilterKeyColumn(int column);
@ -112,6 +119,9 @@ public:
public Q_SLOTS: public Q_SLOTS:
void setFilterRegExp(const QString &pattern); void setFilterRegExp(const QString &pattern);
#if QT_CONFIG(regularexpression)
void setFilterRegularExpression(const QString &pattern);
#endif
void setFilterWildcard(const QString &pattern); void setFilterWildcard(const QString &pattern);
void setFilterFixedString(const QString &pattern); void setFilterFixedString(const QString &pattern);
#if QT_DEPRECATED_SINCE(5, 11) #if QT_DEPRECATED_SINCE(5, 11)

View File

@ -11,7 +11,8 @@ qtHaveModule(gui): SUBDIRS += \
qtHaveModule(widgets) { qtHaveModule(widgets) {
SUBDIRS += \ SUBDIRS += \
qsortfilterproxymodel qsortfilterproxymodel_regexp \
qsortfilterproxymodel_regularexpression
qtHaveModule(sql): SUBDIRS += \ qtHaveModule(sql): SUBDIRS += \
qitemmodel qitemmodel

View File

@ -1 +0,0 @@
tst_qsortfilterproxymodel

View File

@ -1,9 +0,0 @@
CONFIG += testcase
TARGET = tst_qsortfilterproxymodel
QT += widgets testlib
mtdir = ../../../other/qabstractitemmodelutils
INCLUDEPATH += $$PWD/$${mtdir}
SOURCES += tst_qsortfilterproxymodel.cpp $${mtdir}/dynamictreemodel.cpp
HEADERS += $${mtdir}/dynamictreemodel.h

View File

@ -27,6 +27,7 @@
****************************************************************************/ ****************************************************************************/
#include <QtTest/QtTest> #include <QtTest/QtTest>
#include "tst_qsortfilterproxymodel.h"
#include "dynamictreemodel.h" #include "dynamictreemodel.h"
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
@ -36,137 +37,6 @@
#include <qdebug.h> #include <qdebug.h>
typedef QList<int> IntList;
typedef QPair<int, int> IntPair;
typedef QList<IntPair> IntPairList;
Q_DECLARE_METATYPE(QList<QPersistentModelIndex>)
class tst_QSortFilterProxyModel : public QObject
{
Q_OBJECT
public:
tst_QSortFilterProxyModel();
public slots:
void initTestCase();
void cleanupTestCase();
void cleanup();
private slots:
void getSetCheck();
void sort_data();
void sort();
void sortHierarchy_data();
void sortHierarchy();
void insertRows_data();
void insertRows();
void prependRow();
void removeRows_data();
void removeRows();
void removeColumns_data();
void removeColumns();
void insertAfterSelect();
void removeAfterSelect();
void filter_data();
void filter();
void filterHierarchy_data();
void filterHierarchy();
void filterColumns_data();
void filterColumns();
void filterTable();
void filterCurrent();
void filter_qtbug30662();
void changeSourceLayout();
void changeSourceLayoutFilteredOut();
void removeSourceRows_data();
void removeSourceRows();
void insertSourceRows_data();
void insertSourceRows();
void changeFilter_data();
void changeFilter();
void changeSourceData_data();
void changeSourceData();
void changeSourceDataKeepsStableSorting_qtbug1548();
void changeSourceDataForwardsRoles_qtbug35440();
void resortingDoesNotBreakTreeModels();
void dynamicFilterWithoutSort();
void sortFilterRole();
void selectionFilteredOut();
void match_data();
void match();
void insertIntoChildrenlessItem();
void invalidateMappedChildren();
void insertRowIntoFilteredParent();
void filterOutParentAndFilterInChild();
void sourceInsertRows();
void sourceModelDeletion();
void sortColumnTracking1();
void sortColumnTracking2();
void sortStable();
void hiddenColumns();
void insertRowsSort();
void staticSorting();
void dynamicSorting();
void fetchMore();
void hiddenChildren();
void mapFromToSource();
void removeRowsRecursive();
void doubleProxySelectionSetSourceModel();
void appearsAndSort();
void unnecessaryDynamicSorting();
void unnecessaryMapCreation();
void resetInvalidate_data();
void resetInvalidate();
void testMultipleProxiesWithSelection();
void mapSelectionFromSource();
void testResetInternalData();
void filteredColumns();
void headerDataChanged();
void testParentLayoutChanged();
void moveSourceRows();
void hierarchyFilterInvalidation();
void simpleFilterInvalidation();
void chainedProxyModelRoleNames();
void noMapAfterSourceDelete();
void forwardDropApi();
void canDropMimeData();
void filterHint();
void sourceLayoutChangeLeavesValidPersistentIndexes();
void rowMoveLeavesValidPersistentIndexes();
void emitLayoutChangedOnlyIfSortingChanged_data();
void emitLayoutChangedOnlyIfSortingChanged();
void checkSetNewModel();
void removeIntervals_data();
void removeIntervals();
protected:
void buildHierarchy(const QStringList &data, QAbstractItemModel *model);
void checkHierarchy(const QStringList &data, const QAbstractItemModel *model);
private:
QStandardItemModel *m_model;
QSortFilterProxyModel *m_proxy;
};
Q_DECLARE_METATYPE(QAbstractItemModel::LayoutChangeHint)
// Testing get/set functions // Testing get/set functions
void tst_QSortFilterProxyModel::getSetCheck() void tst_QSortFilterProxyModel::getSetCheck()
{ {
@ -204,7 +74,15 @@ void tst_QSortFilterProxyModel::cleanupTestCase()
void tst_QSortFilterProxyModel::cleanup() void tst_QSortFilterProxyModel::cleanup()
{ {
m_proxy->setFilterRegExp(QRegExp()); switch (m_filterType) {
case FilterType::RegExp:
m_proxy->setFilterRegExp(QRegExp());
break;
case FilterType::RegularExpression:
m_proxy->setFilterRegularExpression(QRegularExpression());
break;
}
m_proxy->sort(-1, Qt::AscendingOrder); m_proxy->sort(-1, Qt::AscendingOrder);
m_model->clear(); m_model->clear();
m_model->insertColumns(0, 1); m_model->insertColumns(0, 1);
@ -915,8 +793,9 @@ void tst_QSortFilterProxyModel::removeRows()
if (sortOrder != -1) if (sortOrder != -1)
proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder)); proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
if (!filter.isEmpty()) if (!filter.isEmpty())
proxy.setFilterRegExp(QRegExp(filter)); setupFilter(&proxy, filter);
// remove the rows // remove the rows
QCOMPARE(proxy.removeRows(position, count, QModelIndex()), success); QCOMPARE(proxy.removeRows(position, count, QModelIndex()), success);
@ -938,14 +817,29 @@ class MyFilteredColumnProxyModel : public QSortFilterProxyModel
{ {
Q_OBJECT Q_OBJECT
public: public:
MyFilteredColumnProxyModel(QObject *parent = 0) MyFilteredColumnProxyModel(FilterType filterType, QObject *parent = 0) :
: QSortFilterProxyModel(parent) { } QSortFilterProxyModel(parent),
m_filterType(filterType)
{ }
protected: protected:
bool filterAcceptsColumn(int sourceColumn, const QModelIndex &) const bool filterAcceptsColumn(int sourceColumn, const QModelIndex &) const
{ {
QString key = sourceModel()->headerData(sourceColumn, Qt::Horizontal).toString(); QString key = sourceModel()->headerData(sourceColumn, Qt::Horizontal).toString();
return key.contains(filterRegExp()); bool result = false;
switch (m_filterType) {
case FilterType::RegExp:
result = key.contains(filterRegExp());
break;
case FilterType::RegularExpression:
result = key.contains(filterRegularExpression());
break;
}
return result;
} }
private:
FilterType m_filterType;
}; };
void tst_QSortFilterProxyModel::removeColumns_data() void tst_QSortFilterProxyModel::removeColumns_data()
@ -1154,10 +1048,10 @@ void tst_QSortFilterProxyModel::removeColumns()
QFETCH(QStringList, expectedSource); QFETCH(QStringList, expectedSource);
QStandardItemModel model; QStandardItemModel model;
MyFilteredColumnProxyModel proxy; MyFilteredColumnProxyModel proxy(m_filterType);
proxy.setSourceModel(&model); proxy.setSourceModel(&model);
if (!filter.isEmpty()) if (!filter.isEmpty())
proxy.setFilterRegExp(QRegExp(filter)); setupFilter(&proxy, filter);
// prepare model // prepare model
model.setHorizontalHeaderLabels(initial); model.setHorizontalHeaderLabels(initial);
@ -1225,7 +1119,8 @@ void tst_QSortFilterProxyModel::filterColumns()
QModelIndex index = m_model->index(0, col, QModelIndex()); QModelIndex index = m_model->index(0, col, QModelIndex());
m_model->setData(index, initial.at(col), Qt::DisplayRole); m_model->setData(index, initial.at(col), Qt::DisplayRole);
} }
m_proxy->setFilterRegExp(pattern); setupFilter(m_proxy, pattern);
m_proxy->setFilterKeyColumn(-1); m_proxy->setFilterKeyColumn(-1);
// make sure the model is unchanged // make sure the model is unchanged
for (int col = 0; col < m_model->columnCount(QModelIndex()); ++col) { for (int col = 0; col < m_model->columnCount(QModelIndex()); ++col) {
@ -1291,6 +1186,7 @@ void tst_QSortFilterProxyModel::filter()
QFETCH(QString, pattern); QFETCH(QString, pattern);
QFETCH(QStringList, initial); QFETCH(QStringList, initial);
QFETCH(QStringList, expected); QFETCH(QStringList, expected);
// prepare model // prepare model
QVERIFY(m_model->insertRows(0, initial.count(), QModelIndex())); QVERIFY(m_model->insertRows(0, initial.count(), QModelIndex()));
QCOMPARE(m_model->rowCount(QModelIndex()), initial.count()); QCOMPARE(m_model->rowCount(QModelIndex()), initial.count());
@ -1300,7 +1196,7 @@ void tst_QSortFilterProxyModel::filter()
QModelIndex index = m_model->index(row, 0, QModelIndex()); QModelIndex index = m_model->index(row, 0, QModelIndex());
m_model->setData(index, initial.at(row), Qt::DisplayRole); m_model->setData(index, initial.at(row), Qt::DisplayRole);
} }
m_proxy->setFilterRegExp(pattern); setupFilter(m_proxy, pattern);
// make sure the proxy is unfiltered // make sure the proxy is unfiltered
QCOMPARE(m_proxy->columnCount(QModelIndex()), 1); QCOMPARE(m_proxy->columnCount(QModelIndex()), 1);
QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.count()); QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.count());
@ -1339,7 +1235,7 @@ void tst_QSortFilterProxyModel::filterHierarchy()
QFETCH(QStringList, initial); QFETCH(QStringList, initial);
QFETCH(QStringList, expected); QFETCH(QStringList, expected);
buildHierarchy(initial, m_model); buildHierarchy(initial, m_model);
m_proxy->setFilterRegExp(pattern); setupFilter(m_proxy, pattern);
checkHierarchy(initial, m_model); checkHierarchy(initial, m_model);
checkHierarchy(expected, m_proxy); checkHierarchy(expected, m_proxy);
} }
@ -1404,6 +1300,18 @@ void tst_QSortFilterProxyModel::checkHierarchy(const QStringList &l, const QAbst
} }
} }
void tst_QSortFilterProxyModel::setupFilter(QSortFilterProxyModel *model, const QString& pattern)
{
switch (m_filterType) {
case FilterType::RegExp:
model->setFilterRegExp(pattern);
break;
case FilterType::RegularExpression:
model->setFilterRegularExpression(pattern);
break;
}
}
class TestModel: public QAbstractTableModel class TestModel: public QAbstractTableModel
{ {
public: public:
@ -1422,7 +1330,7 @@ void tst_QSortFilterProxyModel::filterTable()
TestModel model; TestModel model;
QSortFilterProxyModel filter; QSortFilterProxyModel filter;
filter.setSourceModel(&model); filter.setSourceModel(&model);
filter.setFilterRegExp("9"); setupFilter(&filter, QLatin1String("9"));
for (int i = 0; i < filter.rowCount(); ++i) for (int i = 0; i < filter.rowCount(); ++i)
QVERIFY(filter.data(filter.index(i, 0)).toString().contains(QLatin1Char('9'))); QVERIFY(filter.data(filter.index(i, 0)).toString().contains(QLatin1Char('9')));
@ -1486,7 +1394,7 @@ void tst_QSortFilterProxyModel::filterCurrent()
view.setCurrentIndex(proxy.index(0, 0)); view.setCurrentIndex(proxy.index(0, 0));
QCOMPARE(spy.count(), 1); QCOMPARE(spy.count(), 1);
proxy.setFilterRegExp(QRegExp("^B")); setupFilter(&proxy, QLatin1String("^B"));
QCOMPARE(spy.count(), 2); QCOMPARE(spy.count(), 2);
} }
@ -1497,7 +1405,7 @@ void tst_QSortFilterProxyModel::filter_qtbug30662()
proxy.setSourceModel(&model); proxy.setSourceModel(&model);
// make sure the filter does not match any entry // make sure the filter does not match any entry
proxy.setFilterRegExp(QRegExp("[0-9]+")); setupFilter(&proxy, QLatin1String("[0-9]+"));
QStringList slSource; QStringList slSource;
slSource << "z" << "x" << "a" << "b"; slSource << "z" << "x" << "a" << "b";
@ -1507,7 +1415,7 @@ void tst_QSortFilterProxyModel::filter_qtbug30662()
model.setStringList(slSource); model.setStringList(slSource);
// without fix for QTBUG-30662 this will make all entries visible - but unsorted // without fix for QTBUG-30662 this will make all entries visible - but unsorted
proxy.setFilterRegExp(QRegExp("[a-z]+")); setupFilter(&proxy, QLatin1String("[a-z]+"));
QStringList slResult; QStringList slResult;
for (int i = 0; i < proxy.rowCount(); ++i) for (int i = 0; i < proxy.rowCount(); ++i)
@ -1553,7 +1461,8 @@ void tst_QSortFilterProxyModel::changeSourceLayoutFilteredOut()
QSignalSpy removeSpy(&proxy, &QSortFilterProxyModel::rowsRemoved); QSignalSpy removeSpy(&proxy, &QSortFilterProxyModel::rowsRemoved);
// Filter everything out // Filter everything out
proxy.setFilterRegExp(QRegExp("c")); setupFilter(&proxy, QLatin1String("c"));
QCOMPARE(removeSpy.count(), 1); QCOMPARE(removeSpy.count(), 1);
QCOMPARE(0, proxy.rowCount()); QCOMPARE(0, proxy.rowCount());
@ -1562,7 +1471,8 @@ void tst_QSortFilterProxyModel::changeSourceLayoutFilteredOut()
QSignalSpy insertSpy(&proxy, &QSortFilterProxyModel::rowsInserted); QSignalSpy insertSpy(&proxy, &QSortFilterProxyModel::rowsInserted);
// Remove filter; we expect an insert // Remove filter; we expect an insert
proxy.setFilterRegExp(QRegExp("")); setupFilter(&proxy, "");
QCOMPARE(insertSpy.count(), 1); QCOMPARE(insertSpy.count(), 1);
QCOMPARE(beforeSortFilter, proxy.rowCount()); QCOMPARE(beforeSortFilter, proxy.rowCount());
} }
@ -1842,7 +1752,7 @@ void tst_QSortFilterProxyModel::changeFilter()
QVERIFY(initialRemoveSpy.isValid()); QVERIFY(initialRemoveSpy.isValid());
QVERIFY(initialInsertSpy.isValid()); QVERIFY(initialInsertSpy.isValid());
proxy.setFilterRegExp(initialFilter); setupFilter(&proxy, initialFilter);
QCOMPARE(initialRemoveSpy.count(), initialRemoveIntervals.count()); QCOMPARE(initialRemoveSpy.count(), initialRemoveIntervals.count());
QCOMPARE(initialInsertSpy.count(), 0); QCOMPARE(initialInsertSpy.count(), 0);
@ -1866,7 +1776,7 @@ void tst_QSortFilterProxyModel::changeFilter()
QVERIFY(finalRemoveSpy.isValid()); QVERIFY(finalRemoveSpy.isValid());
QVERIFY(finalInsertSpy.isValid()); QVERIFY(finalInsertSpy.isValid());
proxy.setFilterRegExp(finalFilter); setupFilter(&proxy, finalFilter);
QCOMPARE(finalRemoveSpy.count(), finalRemoveIntervals.count()); QCOMPARE(finalRemoveSpy.count(), finalRemoveIntervals.count());
for (int i = 0; i < finalRemoveSpy.count(); ++i) { for (int i = 0; i < finalRemoveSpy.count(); ++i) {
@ -2060,7 +1970,7 @@ void tst_QSortFilterProxyModel::changeSourceData()
proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder)); proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
(void)proxy.rowCount(QModelIndex()); // force mapping (void)proxy.rowCount(QModelIndex()); // force mapping
proxy.setFilterRegExp(filter); setupFilter(&proxy, filter);
QCOMPARE(proxy.rowCount(), expectedInitialProxyItems.count()); QCOMPARE(proxy.rowCount(), expectedInitialProxyItems.count());
for (int i = 0; i < expectedInitialProxyItems.count(); ++i) { for (int i = 0; i < expectedInitialProxyItems.count(); ++i) {
@ -2253,7 +2163,8 @@ void tst_QSortFilterProxyModel::sortFilterRole()
model.setData(index, sourceItems.at(i).second, Qt::UserRole); model.setData(index, sourceItems.at(i).second, Qt::UserRole);
} }
proxy.setFilterRegExp("2"); setupFilter(&proxy, QLatin1String("2"));
QCOMPARE(proxy.rowCount(), 0); // Qt::DisplayRole is default role QCOMPARE(proxy.rowCount(), 0); // Qt::DisplayRole is default role
proxy.setFilterRole(Qt::UserRole); proxy.setFilterRole(Qt::UserRole);
@ -2262,7 +2173,8 @@ void tst_QSortFilterProxyModel::sortFilterRole()
proxy.setFilterRole(Qt::DisplayRole); proxy.setFilterRole(Qt::DisplayRole);
QCOMPARE(proxy.rowCount(), 0); QCOMPARE(proxy.rowCount(), 0);
proxy.setFilterRegExp("1|2|3"); setupFilter(&proxy, QLatin1String("1|2|3"));
QCOMPARE(proxy.rowCount(), 0); QCOMPARE(proxy.rowCount(), 0);
proxy.setFilterRole(Qt::UserRole); proxy.setFilterRole(Qt::UserRole);
@ -2273,7 +2185,8 @@ void tst_QSortFilterProxyModel::sortFilterRole()
proxy.setSortRole(Qt::UserRole); proxy.setSortRole(Qt::UserRole);
proxy.setFilterRole(Qt::DisplayRole); proxy.setFilterRole(Qt::DisplayRole);
proxy.setFilterRegExp("a|c"); setupFilter(&proxy, QLatin1String("a|c"));
QCOMPARE(proxy.rowCount(), orderedItems.count()); QCOMPARE(proxy.rowCount(), orderedItems.count());
for (int i = 0; i < proxy.rowCount(); ++i) { for (int i = 0; i < proxy.rowCount(); ++i) {
QModelIndex index = proxy.index(i, 0, QModelIndex()); QModelIndex index = proxy.index(i, 0, QModelIndex());
@ -2297,7 +2210,8 @@ void tst_QSortFilterProxyModel::selectionFilteredOut()
view.setCurrentIndex(proxy.index(0, 0)); view.setCurrentIndex(proxy.index(0, 0));
QCOMPARE(spy.count(), 1); QCOMPARE(spy.count(), 1);
proxy.setFilterRegExp(QRegExp("^B"));
setupFilter(&proxy, QLatin1String("^B"));
QCOMPARE(spy.count(), 2); QCOMPARE(spy.count(), 2);
} }
@ -2383,7 +2297,7 @@ void tst_QSortFilterProxyModel::match()
} }
proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder)); proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
proxy.setFilterRegExp(filter); setupFilter(&proxy, filter);
QModelIndex startIndex = proxy.index(proxyStartRow, 0); QModelIndex startIndex = proxy.index(proxyStartRow, 0);
QModelIndexList indexes = proxy.match(startIndex, Qt::DisplayRole, what, QModelIndexList indexes = proxy.match(startIndex, Qt::DisplayRole, what,
@ -2508,7 +2422,8 @@ void tst_QSortFilterProxyModel::filterOutParentAndFilterInChild()
QSortFilterProxyModel proxy; QSortFilterProxyModel proxy;
proxy.setSourceModel(&model); proxy.setSourceModel(&model);
proxy.setFilterRegExp("A|B"); setupFilter(&proxy, QLatin1String("A|B"));
QStandardItem *itemA = new QStandardItem("A"); QStandardItem *itemA = new QStandardItem("A");
model.appendRow(itemA); // not filtered model.appendRow(itemA); // not filtered
QStandardItem *itemB = new QStandardItem("B"); QStandardItem *itemB = new QStandardItem("B");
@ -2522,7 +2437,7 @@ void tst_QSortFilterProxyModel::filterOutParentAndFilterInChild()
QVERIFY(removedSpy.isValid()); QVERIFY(removedSpy.isValid());
QVERIFY(insertedSpy.isValid()); QVERIFY(insertedSpy.isValid());
proxy.setFilterRegExp("C"); // A and B will be filtered out, C filtered in setupFilter(&proxy, QLatin1String("C")); // A and B will be filtered out, C filtered in
// we should now have been notified that the subtree represented by itemA has been removed // we should now have been notified that the subtree represented by itemA has been removed
QCOMPARE(removedSpy.count(), 1); QCOMPARE(removedSpy.count(), 1);
@ -2935,7 +2850,7 @@ void tst_QSortFilterProxyModel::hiddenChildren()
itemA->appendRow(itemB); itemA->appendRow(itemB);
QStandardItem *itemC = new QStandardItem("C"); QStandardItem *itemC = new QStandardItem("C");
itemA->appendRow(itemC); itemA->appendRow(itemC);
proxy.setFilterRegExp("VISIBLE"); setupFilter(&proxy, QLatin1String("VISIBLE"));
QCOMPARE(proxy.rowCount(QModelIndex()) , 1); QCOMPARE(proxy.rowCount(QModelIndex()) , 1);
QPersistentModelIndex indexA = proxy.index(0,0); QPersistentModelIndex indexA = proxy.index(0,0);
@ -2962,7 +2877,8 @@ void tst_QSortFilterProxyModel::hiddenChildren()
QModelIndex indexC = proxy.index(0, 0, indexA); QModelIndex indexC = proxy.index(0, 0, indexA);
QCOMPARE(proxy.data(indexC).toString(), QString::fromLatin1("C VISIBLE")); QCOMPARE(proxy.data(indexC).toString(), QString::fromLatin1("C VISIBLE"));
proxy.setFilterRegExp("C"); setupFilter(&proxy, QLatin1String("C"));
QCOMPARE(proxy.rowCount(QModelIndex()), 0); QCOMPARE(proxy.rowCount(QModelIndex()), 0);
itemC->setText("invisible"); itemC->setText("invisible");
itemA->setText("AC"); itemA->setText("AC");
@ -3284,7 +3200,8 @@ void tst_QSortFilterProxyModel::mapSelectionFromSource()
QSortFilterProxyModel proxy; QSortFilterProxyModel proxy;
proxy.setDynamicSortFilter(true); proxy.setDynamicSortFilter(true);
proxy.setFilterRegExp("d.*"); setupFilter(&proxy, QLatin1String("d.*"));
proxy.setSourceModel(&model); proxy.setSourceModel(&model);
// Only "delta" remains. // Only "delta" remains.
@ -3839,13 +3756,13 @@ void tst_QSortFilterProxyModel::moveSourceRows()
filterProxy.setDynamicSortFilter(true); filterProxy.setDynamicSortFilter(true);
filterProxy.sort(0, Qt::AscendingOrder); filterProxy.sort(0, Qt::AscendingOrder);
filterProxy.setSourceModel(&proxy); filterProxy.setSourceModel(&proxy);
filterProxy.setFilterRegExp("6"); // One of the parents setupFilter(&filterProxy, QLatin1String("6")); // One of the parents
QSortFilterProxyModel filterBothProxy; QSortFilterProxyModel filterBothProxy;
filterBothProxy.setDynamicSortFilter(true); filterBothProxy.setDynamicSortFilter(true);
filterBothProxy.sort(0, Qt::AscendingOrder); filterBothProxy.sort(0, Qt::AscendingOrder);
filterBothProxy.setSourceModel(&proxy); filterBothProxy.setSourceModel(&proxy);
filterBothProxy.setFilterRegExp("5"); // The parents are 6 and 3. This filters both out. setupFilter(&filterBothProxy, QLatin1String("5")); // The parents are 6 and 3. This filters both out.
QSignalSpy modelBeforeSpy(&model, &DynamicTreeModel::rowsAboutToBeMoved); QSignalSpy modelBeforeSpy(&model, &DynamicTreeModel::rowsAboutToBeMoved);
QSignalSpy modelAfterSpy(&model, &DynamicTreeModel::rowsMoved); QSignalSpy modelAfterSpy(&model, &DynamicTreeModel::rowsMoved);
@ -4243,7 +4160,7 @@ void tst_QSortFilterProxyModel::filterHint()
QSortFilterProxyModel proxy2; QSortFilterProxyModel proxy2;
proxy2.setSourceModel(&proxy1); proxy2.setSourceModel(&proxy1);
proxy2.setFilterRole(Qt::DisplayRole); proxy2.setFilterRole(Qt::DisplayRole);
proxy2.setFilterRegExp("^[^ ]*$"); setupFilter(&proxy2, QLatin1String("^[^ ]*$"));
proxy2.setDynamicSortFilter(true); proxy2.setDynamicSortFilter(true);
QSignalSpy proxy1BeforeSpy(&proxy1, &QSortFilterProxyModel::layoutAboutToBeChanged); QSignalSpy proxy1BeforeSpy(&proxy1, &QSortFilterProxyModel::layoutAboutToBeChanged);
@ -4371,8 +4288,7 @@ void tst_QSortFilterProxyModel::sourceLayoutChangeLeavesValidPersistentIndexes()
QSortFilterProxyModel proxy1; QSortFilterProxyModel proxy1;
proxy1.setSourceModel(&model); proxy1.setSourceModel(&model);
Q_SET_OBJECT_NAME(proxy1); Q_SET_OBJECT_NAME(proxy1);
setupFilter(&proxy1, QLatin1String("1|2"));
proxy1.setFilterRegExp("1|2");
// The current state of things: // The current state of things:
// model proxy // model proxy
@ -4419,7 +4335,7 @@ void tst_QSortFilterProxyModel::rowMoveLeavesValidPersistentIndexes()
proxy1.setSourceModel(&model); proxy1.setSourceModel(&model);
Q_SET_OBJECT_NAME(proxy1); Q_SET_OBJECT_NAME(proxy1);
proxy1.setFilterRegExp("1|2"); setupFilter(&proxy1, QLatin1String("1|2"));
auto item5 = model.match(model.index(0, 0), Qt::DisplayRole, "5", 1, Qt::MatchRecursive).first(); auto item5 = model.match(model.index(0, 0), Qt::DisplayRole, "5", 1, Qt::MatchRecursive).first();
auto item3 = model.match(model.index(0, 0), Qt::DisplayRole, "3", 1, Qt::MatchRecursive).first(); auto item3 = model.match(model.index(0, 0), Qt::DisplayRole, "3", 1, Qt::MatchRecursive).first();
@ -4653,7 +4569,7 @@ void tst_QSortFilterProxyModel::removeIntervals()
if (sortOrder != -1) if (sortOrder != -1)
proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder)); proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
if (!filter.isEmpty()) if (!filter.isEmpty())
proxy.setFilterRegExp(QRegExp(filter)); setupFilter(&proxy, filter);
(void)proxy.rowCount(QModelIndex()); // force mapping (void)proxy.rowCount(QModelIndex()); // force mapping
@ -4746,5 +4662,4 @@ void tst_QSortFilterProxyModel::checkSetNewModel()
QCoreApplication::processEvents(); QCoreApplication::processEvents();
} }
QTEST_MAIN(tst_QSortFilterProxyModel)
#include "tst_qsortfilterproxymodel.moc" #include "tst_qsortfilterproxymodel.moc"

View File

@ -0,0 +1,183 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef TST_QSORTFILTERPROXYMODEL_H
#define TST_QSORTFILTERPROXYMODEL_H
#include <QtTest/QtTest>
#include "dynamictreemodel.h"
#include <QtCore/QCoreApplication>
#include <QtGui/QStandardItem>
#include <QtWidgets/QTreeView>
#include <QtWidgets/QTableView>
#include <qdebug.h>
typedef QList<int> IntList;
typedef QPair<int, int> IntPair;
typedef QList<IntPair> IntPairList;
enum class FilterType {
RegExp,
RegularExpression
};
Q_DECLARE_METATYPE(QList<QPersistentModelIndex>)
class tst_QSortFilterProxyModel : public QObject
{
Q_OBJECT
public:
tst_QSortFilterProxyModel();
public slots:
void initTestCase();
void cleanupTestCase();
void cleanup();
private slots:
void getSetCheck();
void sort_data();
void sort();
void sortHierarchy_data();
void sortHierarchy();
void insertRows_data();
void insertRows();
void prependRow();
void removeRows_data();
void removeRows();
void removeColumns_data();
void removeColumns();
void insertAfterSelect();
void removeAfterSelect();
void filter_data();
void filter();
void filterHierarchy_data();
void filterHierarchy();
void filterColumns_data();
void filterColumns();
void filterTable();
void filterCurrent();
void filter_qtbug30662();
void changeSourceLayout();
void changeSourceLayoutFilteredOut();
void removeSourceRows_data();
void removeSourceRows();
void insertSourceRows_data();
void insertSourceRows();
void changeFilter_data();
void changeFilter();
void changeSourceData_data();
void changeSourceData();
void changeSourceDataKeepsStableSorting_qtbug1548();
void changeSourceDataForwardsRoles_qtbug35440();
void resortingDoesNotBreakTreeModels();
void dynamicFilterWithoutSort();
void sortFilterRole();
void selectionFilteredOut();
void match_data();
void match();
void insertIntoChildrenlessItem();
void invalidateMappedChildren();
void insertRowIntoFilteredParent();
void filterOutParentAndFilterInChild();
void sourceInsertRows();
void sourceModelDeletion();
void sortColumnTracking1();
void sortColumnTracking2();
void sortStable();
void hiddenColumns();
void insertRowsSort();
void staticSorting();
void dynamicSorting();
void fetchMore();
void hiddenChildren();
void mapFromToSource();
void removeRowsRecursive();
void doubleProxySelectionSetSourceModel();
void appearsAndSort();
void unnecessaryDynamicSorting();
void unnecessaryMapCreation();
void resetInvalidate_data();
void resetInvalidate();
void testMultipleProxiesWithSelection();
void mapSelectionFromSource();
void testResetInternalData();
void filteredColumns();
void headerDataChanged();
void testParentLayoutChanged();
void moveSourceRows();
void hierarchyFilterInvalidation();
void simpleFilterInvalidation();
void chainedProxyModelRoleNames();
void noMapAfterSourceDelete();
void forwardDropApi();
void canDropMimeData();
void filterHint();
void sourceLayoutChangeLeavesValidPersistentIndexes();
void rowMoveLeavesValidPersistentIndexes();
void emitLayoutChangedOnlyIfSortingChanged_data();
void emitLayoutChangedOnlyIfSortingChanged();
void checkSetNewModel();
void removeIntervals_data();
void removeIntervals();
protected:
void buildHierarchy(const QStringList &data, QAbstractItemModel *model);
void checkHierarchy(const QStringList &data, const QAbstractItemModel *model);
void setupFilter(QSortFilterProxyModel *model, const QString& pattern);
protected:
FilterType m_filterType;
private:
QStandardItemModel *m_model;
QSortFilterProxyModel *m_proxy;
};
Q_DECLARE_METATYPE(QAbstractItemModel::LayoutChangeHint)
#endif // TST_QSORTFILTERPROXYMODEL_H

View File

@ -0,0 +1 @@
tst_qsortfilterproxymodel_regexp

View File

@ -0,0 +1,16 @@
CONFIG += testcase
TARGET = tst_qsortfilterproxymodel_regexp
QT += widgets testlib
mtdir = ../../../other/qabstractitemmodelutils
qsfpmdir = ../qsortfilterproxymodel_common
INCLUDEPATH += $$PWD/$${mtdir} $$PWD/$${qsfpmdir}
SOURCES += \
tst_qsortfilterproxymodel_regexp.cpp \
$${qsfpmdir}/tst_qsortfilterproxymodel.cpp \
$${mtdir}/dynamictreemodel.cpp
HEADERS += \
$${qsfpmdir}/tst_qsortfilterproxymodel.h \
$${mtdir}/dynamictreemodel.h

View File

@ -0,0 +1,59 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include "tst_qsortfilterproxymodel.h"
class tst_QSortFilterProxyModelRegExp : public tst_QSortFilterProxyModel
{
Q_OBJECT
public:
tst_QSortFilterProxyModelRegExp();
private slots:
void tst_invalid();
};
tst_QSortFilterProxyModelRegExp::tst_QSortFilterProxyModelRegExp() :
tst_QSortFilterProxyModel()
{
m_filterType = FilterType::RegExp;
}
void tst_QSortFilterProxyModelRegExp::tst_invalid()
{
const QLatin1String pattern("test");
QSortFilterProxyModel model;
model.setFilterRegExp(pattern);
QCOMPARE(model.filterRegExp(), QRegExp(pattern));
model.setFilterRegularExpression(pattern);
QCOMPARE(model.filterRegExp(), QRegExp());
}
QTEST_MAIN(tst_QSortFilterProxyModelRegExp)
#include "tst_qsortfilterproxymodel_regexp.moc"

View File

@ -0,0 +1 @@
tst_qsortfilterproxymodel_regularexpression

View File

@ -0,0 +1,16 @@
CONFIG += testcase
TARGET = tst_qsortfilterproxymodel_regularexpression
QT += widgets testlib
mtdir = ../../../other/qabstractitemmodelutils
qsfpmdir = ../qsortfilterproxymodel_common
INCLUDEPATH += $$PWD/$${mtdir} $${qsfpmdir}
SOURCES += \
tst_qsortfilterproxymodel_regularexpression.cpp \
$${qsfpmdir}/tst_qsortfilterproxymodel.cpp \
$${mtdir}/dynamictreemodel.cpp
HEADERS += \
$${qsfpmdir}/tst_qsortfilterproxymodel.h \
$${mtdir}/dynamictreemodel.h

View File

@ -0,0 +1,59 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include "tst_qsortfilterproxymodel.h"
class tst_QSortFilterProxyModelRegularExpression : public tst_QSortFilterProxyModel
{
Q_OBJECT
public:
tst_QSortFilterProxyModelRegularExpression();
private slots:
void tst_invalid();
};
tst_QSortFilterProxyModelRegularExpression::tst_QSortFilterProxyModelRegularExpression() :
tst_QSortFilterProxyModel()
{
m_filterType = FilterType::RegularExpression;
}
void tst_QSortFilterProxyModelRegularExpression::tst_invalid()
{
const QLatin1String pattern("test");
QSortFilterProxyModel model;
model.setFilterRegularExpression(pattern);
QCOMPARE(model.filterRegularExpression(), QRegularExpression(pattern));
model.setFilterRegExp(pattern);
QCOMPARE(model.filterRegularExpression(), QRegularExpression());
}
QTEST_MAIN(tst_QSortFilterProxyModelRegularExpression)
#include "tst_qsortfilterproxymodel_regularexpression.moc"