diff options
Diffstat (limited to 'src/corelib/itemmodels/qsortfilterproxymodel.cpp')
-rw-r--r-- | src/corelib/itemmodels/qsortfilterproxymodel.cpp | 802 |
1 files changed, 503 insertions, 299 deletions
diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.cpp b/src/corelib/itemmodels/qsortfilterproxymodel.cpp index 728be151c6..a5284dbad4 100644 --- a/src/corelib/itemmodels/qsortfilterproxymodel.cpp +++ b/src/corelib/itemmodels/qsortfilterproxymodel.cpp @@ -1,57 +1,21 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** 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-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qsortfilterproxymodel.h" #include "qitemselectionmodel.h" #include <qsize.h> #include <qdebug.h> #include <qdatetime.h> -#include <qpair.h> #include <qstringlist.h> #include <private/qabstractitemmodel_p.h> #include <private/qabstractproxymodel_p.h> +#include <private/qproperty_p.h> #include <algorithm> QT_BEGIN_NAMESPACE -typedef QList<QPair<QModelIndex, QPersistentModelIndex>> QModelIndexPairList; +using QModelIndexPairList = QList<std::pair<QModelIndex, QPersistentModelIndex>>; struct QSortFilterProxyModelDataChanged { @@ -144,9 +108,9 @@ private: class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate { +public: Q_DECLARE_PUBLIC(QSortFilterProxyModel) -public: enum class Direction { Rows = 1, Columns = 2, @@ -164,35 +128,143 @@ public: mutable QHash<QModelIndex, Mapping*> source_index_mapping; - int source_sort_column; - int proxy_sort_column; - Qt::SortOrder sort_order; - Qt::CaseSensitivity sort_casesensitivity; - int sort_role; - bool sort_localeaware; + void setSortCaseSensitivityForwarder(Qt::CaseSensitivity cs) + { + q_func()->setSortCaseSensitivity(cs); + } + void sortCaseSensitivityChangedForwarder(Qt::CaseSensitivity cs) + { + emit q_func()->sortCaseSensitivityChanged(cs); + } + + void setSortRoleForwarder(int role) { q_func()->setSortRole(role); } + void sortRoleChangedForwarder(int role) { emit q_func()->sortRoleChanged(role); } - int filter_column; - int filter_role; - QRegularExpression filter_data; - QModelIndex last_top_source; + void setSortLocaleAwareForwarder(bool on) { q_func()->setSortLocaleAware(on); } + void sortLocaleAwareChangedForwarder(bool on) { emit q_func()->sortLocaleAwareChanged(on); } + + void setFilterKeyColumnForwarder(int column) { q_func()->setFilterKeyColumn(column); } + + void setFilterRoleForwarder(int role) { q_func()->setFilterRole(role); } + void filterRoleChangedForwarder(int role) { emit q_func()->filterRoleChanged(role); } + + void setRecursiveFilteringEnabledForwarder(bool recursive) + { + q_func()->setRecursiveFilteringEnabled(recursive); + } + void recursiveFilteringEnabledChangedForwarder(bool recursive) + { + emit q_func()->recursiveFilteringEnabledChanged(recursive); + } + + void setAutoAcceptChildRowsForwarder(bool accept) { q_func()->setAutoAcceptChildRows(accept); } + void autoAcceptChildRowsChangedForwarder(bool accept) + { + emit q_func()->autoAcceptChildRowsChanged(accept); + } + + void setDynamicSortFilterForwarder(bool enable) { q_func()->setDynamicSortFilter(enable); } + + void setFilterCaseSensitivityForwarder(Qt::CaseSensitivity cs) + { + q_func()->setFilterCaseSensitivity(cs); + } + void filterCaseSensitivityChangedForwarder(Qt::CaseSensitivity cs) + { + emit q_func()->filterCaseSensitivityChanged(cs); + } + + void setFilterRegularExpressionForwarder(const QRegularExpression &re) + { + q_func()->setFilterRegularExpression(re); + } + + int source_sort_column = -1; + int proxy_sort_column = -1; + Qt::SortOrder sort_order = Qt::AscendingOrder; + bool complete_insert = false; + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS( + QSortFilterProxyModelPrivate, Qt::CaseSensitivity, sort_casesensitivity, + &QSortFilterProxyModelPrivate::setSortCaseSensitivityForwarder, + &QSortFilterProxyModelPrivate::sortCaseSensitivityChangedForwarder, Qt::CaseSensitive) + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QSortFilterProxyModelPrivate, int, sort_role, + &QSortFilterProxyModelPrivate::setSortRoleForwarder, + &QSortFilterProxyModelPrivate::sortRoleChangedForwarder, + Qt::DisplayRole) + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QSortFilterProxyModelPrivate, int, filter_column, + &QSortFilterProxyModelPrivate::setFilterKeyColumnForwarder, + 0) + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QSortFilterProxyModelPrivate, int, filter_role, + &QSortFilterProxyModelPrivate::setFilterRoleForwarder, + &QSortFilterProxyModelPrivate::filterRoleChangedForwarder, + Qt::DisplayRole) + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS( + QSortFilterProxyModelPrivate, bool, sort_localeaware, + &QSortFilterProxyModelPrivate::setSortLocaleAwareForwarder, + &QSortFilterProxyModelPrivate::sortLocaleAwareChangedForwarder, false) + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS( + QSortFilterProxyModelPrivate, bool, filter_recursive, + &QSortFilterProxyModelPrivate::setRecursiveFilteringEnabledForwarder, + &QSortFilterProxyModelPrivate::recursiveFilteringEnabledChangedForwarder, false) + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS( + QSortFilterProxyModelPrivate, bool, accept_children, + &QSortFilterProxyModelPrivate::setAutoAcceptChildRowsForwarder, + &QSortFilterProxyModelPrivate::autoAcceptChildRowsChangedForwarder, false) + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QSortFilterProxyModelPrivate, bool, dynamic_sortfilter, + &QSortFilterProxyModelPrivate::setDynamicSortFilterForwarder, + true) + + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS( + QSortFilterProxyModelPrivate, Qt::CaseSensitivity, filter_casesensitive, + &QSortFilterProxyModelPrivate::setFilterCaseSensitivityForwarder, + &QSortFilterProxyModelPrivate::filterCaseSensitivityChangedForwarder, Qt::CaseSensitive) - bool filter_recursive; - bool accept_children; - bool complete_insert; - bool dynamic_sortfilter; + Q_OBJECT_COMPAT_PROPERTY(QSortFilterProxyModelPrivate, QRegularExpression, + filter_regularexpression, + &QSortFilterProxyModelPrivate::setFilterRegularExpressionForwarder) + + QModelIndex last_top_source; QRowsRemoval itemsBeingRemoved; QModelIndexPairList saved_persistent_indexes; QList<QPersistentModelIndex> saved_layoutChange_parents; + std::array<QMetaObject::Connection, 18> sourceConnections; + QHash<QModelIndex, Mapping *>::const_iterator create_mapping( const QModelIndex &source_parent) const; + QHash<QModelIndex, Mapping *>::const_iterator create_mapping_recursive( + const QModelIndex &source_parent) const; QModelIndex proxy_to_source(const QModelIndex &proxyIndex) const; QModelIndex source_to_proxy(const QModelIndex &sourceIndex) const; bool can_create_mapping(const QModelIndex &source_parent) const; void remove_from_mapping(const QModelIndex &source_parent); + /* + * Legacy: changing the pattern through a string does not change the + * case sensitivity. + */ + void set_filter_pattern(const QString &pattern) + { + QRegularExpression re = filter_regularexpression.valueBypassingBindings(); + const auto cs = re.patternOptions() & QRegularExpression::CaseInsensitiveOption; + re.setPattern(pattern); + re.setPatternOptions(cs); + // This is a helper function, which is supposed to be called from a + // more complicated context. Because of that, the caller is responsible + // for calling notify() and removeBindingUnlessInWrapper(), if needed. + filter_regularexpression.setValueBypassingBindings(re); + } + inline QHash<QModelIndex, Mapping *>::const_iterator index_to_iterator( const QModelIndex &proxy_index) const { @@ -260,10 +332,10 @@ public: int find_source_sort_column() const; void sort_source_rows(QList<int> &source_rows, const QModelIndex &source_parent) const; - QList<QPair<int, QList<int>>> proxy_intervals_for_source_items_to_add( + QList<std::pair<int, QList<int>>> proxy_intervals_for_source_items_to_add( const QList<int> &proxy_to_source, const QList<int> &source_items, const QModelIndex &source_parent, Qt::Orientation orient) const; - QList<QPair<int, int>> proxy_intervals_for_source_items( + QList<std::pair<int, int>> proxy_intervals_for_source_items( const QList<int> &source_to_proxy, const QList<int> &source_items) const; void insert_source_items( QList<int> &source_to_proxy, QList<int> &proxy_to_source, @@ -379,7 +451,7 @@ bool QSortFilterProxyModelPrivate::recursiveChildAcceptsRow(int source_row, cons void QSortFilterProxyModelPrivate::remove_from_mapping(const QModelIndex &source_parent) { if (Mapping *m = source_index_mapping.take(source_parent)) { - for (const QModelIndex &mappedIdx : qAsConst(m->mapped_children)) + for (const QModelIndex &mappedIdx : std::as_const(m->mapped_children)) remove_from_mapping(mappedIdx); delete m; } @@ -445,6 +517,29 @@ IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping( return it; } +// Go up the tree, creating mappings, unless of course the parent is filtered out +IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping_recursive(const QModelIndex &source_parent) const +{ + if (source_parent.isValid()) { + const QModelIndex source_grand_parent = source_parent.parent(); + IndexMap::const_iterator it = source_index_mapping.constFind(source_grand_parent); + IndexMap::const_iterator end = source_index_mapping.constEnd(); + if (it == end) { + it = create_mapping_recursive(source_grand_parent); + end = source_index_mapping.constEnd(); + if (it == end) + return end; + } + Mapping *gm = it.value(); + if (gm->proxy_rows.at(source_parent.row()) == -1 || + gm->proxy_columns.at(source_parent.column()) == -1) { + // Can't do, parent is filtered + return end; + } + } + return create_mapping(source_parent); +} + QModelIndex QSortFilterProxyModelPrivate::proxy_to_source(const QModelIndex &proxy_index) const { if (!proxy_index.isValid()) @@ -593,8 +688,10 @@ void QSortFilterProxyModelPrivate::sort_source_rows( QSortFilterProxyModelGreaterThan gt(source_sort_column, source_parent, model, q); std::stable_sort(source_rows.begin(), source_rows.end(), gt); } - } else { // restore the source model order - std::stable_sort(source_rows.begin(), source_rows.end()); + } else if (sort_order == Qt::AscendingOrder) { + std::stable_sort(source_rows.begin(), source_rows.end(), std::less{}); + } else { + std::stable_sort(source_rows.begin(), source_rows.end(), std::greater{}); } } @@ -609,10 +706,10 @@ void QSortFilterProxyModelPrivate::sort_source_rows( The result is a vector of pairs, where each pair represents a (start, end) tuple, sorted in ascending order. */ -QList<QPair<int, int>> QSortFilterProxyModelPrivate::proxy_intervals_for_source_items( +QList<std::pair<int, int>> QSortFilterProxyModelPrivate::proxy_intervals_for_source_items( const QList<int> &source_to_proxy, const QList<int> &source_items) const { - QList<QPair<int, int>> proxy_intervals; + QList<std::pair<int, int>> proxy_intervals; if (source_items.isEmpty()) return proxy_intervals; @@ -629,22 +726,19 @@ QList<QPair<int, int>> QSortFilterProxyModelPrivate::proxy_intervals_for_source_ ++source_items_index; } // Add interval to result - proxy_intervals.append(QPair<int, int>(first_proxy_item, last_proxy_item)); + proxy_intervals.emplace_back(first_proxy_item, last_proxy_item); } std::stable_sort(proxy_intervals.begin(), proxy_intervals.end()); // Consolidate adjacent intervals for (int i = proxy_intervals.size()-1; i > 0; --i) { - QPair<int, int> &interval = proxy_intervals[i]; - QPair<int, int> &preceeding_interval = proxy_intervals[i - 1]; + std::pair<int, int> &interval = proxy_intervals[i]; + std::pair<int, int> &preceeding_interval = proxy_intervals[i - 1]; if (interval.first == preceeding_interval.second + 1) { preceeding_interval.second = interval.second; interval.first = interval.second = -1; } } - proxy_intervals.erase( - std::remove_if(proxy_intervals.begin(), proxy_intervals.end(), - [](QPair<int, int> &interval) { return interval.first < 0; }), - proxy_intervals.end()); + proxy_intervals.removeIf([](std::pair<int, int> interval) { return interval.first < 0; }); return proxy_intervals; } @@ -663,15 +757,17 @@ void QSortFilterProxyModelPrivate::remove_source_items( { Q_Q(QSortFilterProxyModel); QModelIndex proxy_parent = q->mapFromSource(source_parent); - if (!proxy_parent.isValid() && source_parent.isValid()) + if (!proxy_parent.isValid() && source_parent.isValid()) { + proxy_to_source.clear(); return; // nothing to do (already removed) + } const auto proxy_intervals = proxy_intervals_for_source_items( source_to_proxy, source_items); const auto end = proxy_intervals.rend(); for (auto it = proxy_intervals.rbegin(); it != end; ++it) { - const QPair<int, int> &interval = *it; + const std::pair<int, int> &interval = *it; const int proxy_start = interval.first; const int proxy_end = interval.second; remove_proxy_interval(source_to_proxy, proxy_to_source, proxy_start, proxy_end, @@ -725,22 +821,21 @@ void QSortFilterProxyModelPrivate::remove_proxy_interval( items), where items is a vector containing the (sorted) source items that should be inserted at that proxy model location. */ -QList<QPair<int, QList<int>>> QSortFilterProxyModelPrivate::proxy_intervals_for_source_items_to_add( +QList<std::pair<int, QList<int>>> QSortFilterProxyModelPrivate::proxy_intervals_for_source_items_to_add( const QList<int> &proxy_to_source, const QList<int> &source_items, const QModelIndex &source_parent, Qt::Orientation orient) const { Q_Q(const QSortFilterProxyModel); - QList<QPair<int, QList<int>>> proxy_intervals; + QList<std::pair<int, QList<int>>> proxy_intervals; if (source_items.isEmpty()) return proxy_intervals; int proxy_low = 0; int proxy_item = 0; int source_items_index = 0; - QList<int> source_items_in_interval; bool compare = (orient == Qt::Vertical && source_sort_column >= 0 && dynamic_sortfilter); while (source_items_index < source_items.size()) { - source_items_in_interval.clear(); + QList<int> source_items_in_interval; int first_new_source_item = source_items.at(source_items_index); source_items_in_interval.append(first_new_source_item); ++source_items_index; @@ -786,7 +881,7 @@ QList<QPair<int, QList<int>>> QSortFilterProxyModelPrivate::proxy_intervals_for_ } // Add interval to result - proxy_intervals.append(QPair<int, QList<int>>(proxy_item, source_items_in_interval)); + proxy_intervals.emplace_back(proxy_item, std::move(source_items_in_interval)); } return proxy_intervals; } @@ -814,7 +909,7 @@ void QSortFilterProxyModelPrivate::insert_source_items( const auto end = proxy_intervals.rend(); for (auto it = proxy_intervals.rbegin(); it != end; ++it) { - const QPair<int, QList<int>> &interval = *it; + const std::pair<int, QList<int>> &interval = *it; const int proxy_start = interval.first; const QList<int> &source_items = interval.second; const int proxy_end = proxy_start + source_items.size() - 1; @@ -826,8 +921,9 @@ void QSortFilterProxyModelPrivate::insert_source_items( q->beginInsertColumns(proxy_parent, proxy_start, proxy_end); } - for (int i = 0; i < source_items.size(); ++i) - proxy_to_source.insert(proxy_start + i, source_items.at(i)); + // TODO: use the range QList::insert() overload once it is implemented (QTBUG-58633). + proxy_to_source.insert(proxy_start, source_items.size(), 0); + std::copy(source_items.cbegin(), source_items.cend(), proxy_to_source.begin() + proxy_start); build_source_to_proxy_mapping(proxy_to_source, source_to_proxy, proxy_start); @@ -865,12 +961,12 @@ void QSortFilterProxyModelPrivate::source_items_inserted( it = create_mapping(source_parent); Mapping *m = it.value(); QModelIndex proxy_parent = q->mapFromSource(source_parent); - if (m->source_rows.count() > 0) { - q->beginInsertRows(proxy_parent, 0, m->source_rows.count() - 1); + if (m->source_rows.size() > 0) { + q->beginInsertRows(proxy_parent, 0, m->source_rows.size() - 1); q->endInsertRows(); } - if (m->source_columns.count() > 0) { - q->beginInsertColumns(proxy_parent, 0, m->source_columns.count() - 1); + if (m->source_columns.size() > 0) { + q->beginInsertColumns(proxy_parent, 0, m->source_columns.size() - 1); q->endInsertColumns(); } return; @@ -1042,7 +1138,7 @@ void QSortFilterProxyModelPrivate::updateChildrenMapping(const QModelIndex &sour Qt::Orientation orient, int start, int end, int delta_item_count, bool remove) { // see if any mapped children should be (re)moved - QList<QPair<QModelIndex, Mapping *>> moved_source_index_mappings; + QList<std::pair<QModelIndex, Mapping *>> moved_source_index_mappings; auto it2 = parent_mapping->mapped_children.begin(); for ( ; it2 != parent_mapping->mapped_children.end();) { const QModelIndex source_child_index = *it2; @@ -1076,12 +1172,12 @@ void QSortFilterProxyModelPrivate::updateChildrenMapping(const QModelIndex &sour Mapping *cm = source_index_mapping.take(source_child_index); Q_ASSERT(cm); // we do not reinsert right away, because the new index might be identical with another, old index - moved_source_index_mappings.append(QPair<QModelIndex, Mapping*>(new_index, cm)); + moved_source_index_mappings.emplace_back(new_index, cm); } } // reinsert moved, mapped indexes - for (auto &pair : qAsConst(moved_source_index_mappings)) { + for (auto &pair : std::as_const(moved_source_index_mappings)) { pair.second->source_parent = pair.first; source_index_mapping.insert(pair.first, pair.second); } @@ -1096,7 +1192,7 @@ void QSortFilterProxyModelPrivate::proxy_item_range( { proxy_low = INT_MAX; proxy_high = INT_MIN; - for (int i = 0; i < source_items.count(); ++i) { + for (int i = 0; i < source_items.size(); ++i) { int proxy_item = source_to_proxy.at(source_items.at(i)); Q_ASSERT(proxy_item != -1); if (proxy_item < proxy_low) @@ -1129,11 +1225,11 @@ QModelIndexPairList QSortFilterProxyModelPrivate::store_persistent_indexes() con { Q_Q(const QSortFilterProxyModel); QModelIndexPairList source_indexes; - source_indexes.reserve(persistent.indexes.count()); - for (const QPersistentModelIndexData *data : qAsConst(persistent.indexes)) { + source_indexes.reserve(persistent.indexes.size()); + for (const QPersistentModelIndexData *data : std::as_const(persistent.indexes)) { const QModelIndex &proxy_index = data->index; QModelIndex source_index = q->mapToSource(proxy_index); - source_indexes.append(qMakePair(proxy_index, QPersistentModelIndex(source_index))); + source_indexes.emplace_back(proxy_index, source_index); } return source_indexes; } @@ -1149,7 +1245,7 @@ void QSortFilterProxyModelPrivate::update_persistent_indexes( { Q_Q(QSortFilterProxyModel); QModelIndexList from, to; - const int numSourceIndexes = source_indexes.count(); + const int numSourceIndexes = source_indexes.size(); from.reserve(numSourceIndexes); to.reserve(numSourceIndexes); for (const auto &indexPair : source_indexes) { @@ -1171,9 +1267,10 @@ void QSortFilterProxyModelPrivate::update_persistent_indexes( */ void QSortFilterProxyModelPrivate::filter_about_to_be_changed(const QModelIndex &source_parent) { - if (!filter_data.pattern().isEmpty() && - source_index_mapping.constFind(source_parent) == source_index_mapping.constEnd()) - create_mapping(source_parent); + if (!filter_regularexpression.valueBypassingBindings().pattern().isEmpty() + && source_index_mapping.constFind(source_parent) == source_index_mapping.constEnd()) { + create_mapping(source_parent); + } } @@ -1232,7 +1329,7 @@ QSet<int> QSortFilterProxyModelPrivate::handle_filter_changed( Q_Q(QSortFilterProxyModel); // Figure out which mapped items to remove QList<int> source_items_remove; - for (int i = 0; i < proxy_to_source.count(); ++i) { + for (int i = 0; i < proxy_to_source.size(); ++i) { const int source_item = proxy_to_source.at(i); if ((orient == Qt::Vertical) ? !filterAcceptsRowInternal(source_item, source_parent) @@ -1319,11 +1416,20 @@ void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &sourc const QModelIndex &source_bottom_right = data_changed.bottomRight; const QModelIndex source_parent = source_top_left.parent(); + bool change_in_unmapped_parent = false; IndexMap::const_iterator it = source_index_mapping.constFind(source_parent); if (it == source_index_mapping.constEnd()) { - // Don't care, since we don't have mapping for this index - continue; + // We don't have mapping for this index, so we cannot know how things + // changed (in case the change affects filtering) in order to forward + // the change correctly. + // But we can at least forward the signal "as is", if the row isn't + // filtered out, this is better than nothing. + it = create_mapping_recursive(source_parent); + if (it == source_index_mapping.constEnd()) + continue; + change_in_unmapped_parent = true; } + Mapping *m = it.value(); // Figure out how the source changes affect us @@ -1331,9 +1437,9 @@ void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &sourc QList<int> source_rows_insert; QList<int> source_rows_change; QList<int> source_rows_resort; - int end = qMin(source_bottom_right.row(), m->proxy_rows.count() - 1); + int end = qMin(source_bottom_right.row(), m->proxy_rows.size() - 1); for (int source_row = source_top_left.row(); source_row <= end; ++source_row) { - if (dynamic_sortfilter) { + if (dynamic_sortfilter && !change_in_unmapped_parent) { if (m->proxy_rows.at(source_row) != -1) { if (!filterAcceptsRowInternal(source_row, source_parent)) { // This source row no longer satisfies the filter, so it must be removed @@ -1404,15 +1510,19 @@ void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &sourc while (source_left_column < source_bottom_right.column() && m->proxy_columns.at(source_left_column) == -1) ++source_left_column; - const QModelIndex proxy_top_left = create_index( - proxy_start_row, m->proxy_columns.at(source_left_column), it); - int source_right_column = source_bottom_right.column(); - while (source_right_column > source_top_left.column() - && m->proxy_columns.at(source_right_column) == -1) - --source_right_column; - const QModelIndex proxy_bottom_right = create_index( - proxy_end_row, m->proxy_columns.at(source_right_column), it); - emit q->dataChanged(proxy_top_left, proxy_bottom_right, roles); + if (m->proxy_columns.at(source_left_column) != -1) { + const QModelIndex proxy_top_left = create_index( + proxy_start_row, m->proxy_columns.at(source_left_column), it); + int source_right_column = source_bottom_right.column(); + while (source_right_column > source_top_left.column() + && m->proxy_columns.at(source_right_column) == -1) + --source_right_column; + if (m->proxy_columns.at(source_right_column) != -1) { + const QModelIndex proxy_bottom_right = create_index( + proxy_end_row, m->proxy_columns.at(source_right_column), it); + emit q->dataChanged(proxy_top_left, proxy_bottom_right, roles); + } + } } } @@ -1479,8 +1589,7 @@ void QSortFilterProxyModelPrivate::_q_sourceReset() _q_clearMapping(); // All internal structures are deleted in clear() q->endResetModel(); - update_source_sort_column(); - if (dynamic_sortfilter && update_source_sort_column()) + if (update_source_sort_column() && dynamic_sortfilter) sort(); } @@ -1544,8 +1653,8 @@ void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeInserted( const bool toplevel = !source_parent.isValid(); const bool recursive_accepted = filter_recursive && !toplevel && filterAcceptsRowInternal(source_parent.row(), source_parent.parent()); - //Force the creation of a mapping now, even if its empty. - //We need it because the proxy can be acessed at the moment it emits rowsAboutToBeInserted in insert_source_items + //Force the creation of a mapping now, even if it's empty. + //We need it because the proxy can be accessed at the moment it emits rowsAboutToBeInserted in insert_source_items if (!filter_recursive || toplevel || recursive_accepted) { if (can_create_mapping(source_parent)) create_mapping(source_parent); @@ -1664,8 +1773,8 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeInserted( { Q_UNUSED(start); Q_UNUSED(end); - //Force the creation of a mapping now, even if its empty. - //We need it because the proxy can be acessed at the moment it emits columnsAboutToBeInserted in insert_source_items + //Force the creation of a mapping now, even if it's empty. + //We need it because the proxy can be accessed at the moment it emits columnsAboutToBeInserted in insert_source_items if (can_create_mapping(source_parent)) create_mapping(source_parent); } @@ -1712,7 +1821,10 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsRemoved( source_sort_column = -1; } - proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column(); + if (source_sort_column >= 0) + proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column(); + else + proxy_sort_column = -1; } void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeMoved( @@ -1897,18 +2009,8 @@ QSortFilterProxyModel::QSortFilterProxyModel(QObject *parent) : QAbstractProxyModel(*new QSortFilterProxyModelPrivate, parent) { Q_D(QSortFilterProxyModel); - d->proxy_sort_column = d->source_sort_column = -1; - d->sort_order = Qt::AscendingOrder; - d->sort_casesensitivity = Qt::CaseSensitive; - d->sort_role = Qt::DisplayRole; - d->sort_localeaware = false; - d->filter_column = 0; - d->filter_role = Qt::DisplayRole; - d->filter_recursive = false; - d->accept_children = false; - d->dynamic_sortfilter = true; - d->complete_insert = false; - connect(this, SIGNAL(modelReset()), this, SLOT(_q_clearMapping())); + QObjectPrivate::connect(this, &QAbstractItemModel::modelReset, d, + &QSortFilterProxyModelPrivate::_q_clearMapping); } /*! @@ -1933,56 +2035,10 @@ void QSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel) beginResetModel(); - disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>)), - this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QList<int>))); - - disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), - this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int))); - - disconnect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); - - disconnect(d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int))); - - disconnect(d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); - - disconnect(d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int))); - - disconnect(d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); - - disconnect(d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); - - disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_sourceAboutToBeReset())); - disconnect(d->model, SIGNAL(modelReset()), this, SLOT(_q_sourceReset())); + if (d->model) { + for (const QMetaObject::Connection &connection : std::as_const(d->sourceConnections)) + disconnect(connection); + } // same as in _q_sourceReset() d->invalidatePersistentIndexes(); @@ -1990,57 +2046,61 @@ void QSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel) QAbstractProxyModel::setSourceModel(sourceModel); - connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QList<int>)), - this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QList<int>))); + d->sourceConnections = std::array<QMetaObject::Connection, 18>{ + QObjectPrivate::connect(d->model, &QAbstractItemModel::dataChanged, d, + &QSortFilterProxyModelPrivate::_q_sourceDataChanged), - connect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), - this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::headerDataChanged, d, + &QSortFilterProxyModelPrivate::_q_sourceHeaderDataChanged), - connect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsAboutToBeInserted, d, + &QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeInserted), - connect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsInserted, d, + &QSortFilterProxyModelPrivate::_q_sourceRowsInserted), - connect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsAboutToBeInserted, d, + &QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeInserted), - connect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsInserted, d, + &QSortFilterProxyModelPrivate::_q_sourceColumnsInserted), - connect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsAboutToBeRemoved, d, + &QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeRemoved), - connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsRemoved, d, + &QSortFilterProxyModelPrivate::_q_sourceRowsRemoved), - connect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsAboutToBeRemoved, d, + &QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeRemoved), - connect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)), - this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsRemoved, d, + &QSortFilterProxyModelPrivate::_q_sourceColumnsRemoved), - connect(d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsAboutToBeMoved, d, + &QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeMoved), - connect(d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::rowsMoved, d, + &QSortFilterProxyModelPrivate::_q_sourceRowsMoved), - connect(d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsAboutToBeMoved, d, + &QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeMoved), - connect(d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::columnsMoved, d, + &QSortFilterProxyModelPrivate::_q_sourceColumnsMoved), - connect(d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::layoutAboutToBeChanged, d, + &QSortFilterProxyModelPrivate::_q_sourceLayoutAboutToBeChanged), - connect(d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); + QObjectPrivate::connect(d->model, &QAbstractItemModel::layoutChanged, d, + &QSortFilterProxyModelPrivate::_q_sourceLayoutChanged), - connect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_sourceAboutToBeReset())); - connect(d->model, SIGNAL(modelReset()), this, SLOT(_q_sourceReset())); + QObjectPrivate::connect(d->model, &QAbstractItemModel::modelAboutToBeReset, d, + &QSortFilterProxyModelPrivate::_q_sourceAboutToBeReset), + QObjectPrivate::connect(d->model, &QAbstractItemModel::modelReset, d, + &QSortFilterProxyModelPrivate::_q_sourceReset) + }; endResetModel(); if (d->update_source_sort_column() && d->dynamic_sortfilter) d->sort(); @@ -2057,7 +2117,7 @@ QModelIndex QSortFilterProxyModel::index(int row, int column, const QModelIndex QModelIndex source_parent = mapToSource(parent); // parent is already mapped at this point IndexMap::const_iterator it = d->create_mapping(source_parent); // but make sure that the children are mapped - if (it.value()->source_rows.count() <= row || it.value()->source_columns.count() <= column) + if (it.value()->source_rows.size() <= row || it.value()->source_columns.size() <= column) return QModelIndex(); return d->create_index(row, column, it); @@ -2088,7 +2148,7 @@ QModelIndex QSortFilterProxyModel::sibling(int row, int column, const QModelInde return QModelIndex(); const IndexMap::const_iterator it = d->index_to_iterator(idx); - if (it.value()->source_rows.count() <= row || it.value()->source_columns.count() <= column) + if (it.value()->source_rows.size() <= row || it.value()->source_columns.size() <= column) return QModelIndex(); return d->create_index(row, column, it); @@ -2104,7 +2164,7 @@ int QSortFilterProxyModel::rowCount(const QModelIndex &parent) const if (parent.isValid() && !source_parent.isValid()) return 0; IndexMap::const_iterator it = d->create_mapping(source_parent); - return it.value()->source_rows.count(); + return it.value()->source_rows.size(); } /*! @@ -2117,7 +2177,7 @@ int QSortFilterProxyModel::columnCount(const QModelIndex &parent) const if (parent.isValid() && !source_parent.isValid()) return 0; IndexMap::const_iterator it = d->create_mapping(source_parent); - return it.value()->source_columns.count(); + return it.value()->source_columns.size(); } /*! @@ -2136,7 +2196,7 @@ bool QSortFilterProxyModel::hasChildren(const QModelIndex &parent) const return true; //we assume we might have children that can be fetched QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value(); - return m->source_rows.count() != 0 && m->source_columns.count() != 0; + return m->source_rows.size() != 0 && m->source_columns.size() != 0; } /*! @@ -2170,15 +2230,15 @@ QVariant QSortFilterProxyModel::headerData(int section, Qt::Orientation orientat { Q_D(const QSortFilterProxyModel); IndexMap::const_iterator it = d->create_mapping(QModelIndex()); - if (it.value()->source_rows.count() * it.value()->source_columns.count() > 0) + if (it.value()->source_rows.size() * it.value()->source_columns.size() > 0) return QAbstractProxyModel::headerData(section, orientation, role); int source_section; if (orientation == Qt::Vertical) { - if (section < 0 || section >= it.value()->source_rows.count()) + if (section < 0 || section >= it.value()->source_rows.size()) return QVariant(); source_section = it.value()->source_rows.at(section); } else { - if (section < 0 || section >= it.value()->source_columns.count()) + if (section < 0 || section >= it.value()->source_columns.size()) return QVariant(); source_section = it.value()->source_columns.at(section); } @@ -2193,15 +2253,15 @@ bool QSortFilterProxyModel::setHeaderData(int section, Qt::Orientation orientati { Q_D(QSortFilterProxyModel); IndexMap::const_iterator it = d->create_mapping(QModelIndex()); - if (it.value()->source_rows.count() * it.value()->source_columns.count() > 0) + if (it.value()->source_rows.size() * it.value()->source_columns.size() > 0) return QAbstractProxyModel::setHeaderData(section, orientation, value, role); int source_section; if (orientation == Qt::Vertical) { - if (section < 0 || section >= it.value()->source_rows.count()) + if (section < 0 || section >= it.value()->source_rows.size()) return false; source_section = it.value()->source_rows.at(section); } else { - if (section < 0 || section >= it.value()->source_columns.count()) + if (section < 0 || section >= it.value()->source_columns.size()) return false; source_section = it.value()->source_columns.at(section); } @@ -2215,7 +2275,7 @@ QMimeData *QSortFilterProxyModel::mimeData(const QModelIndexList &indexes) const { Q_D(const QSortFilterProxyModel); QModelIndexList source_indexes; - source_indexes.reserve(indexes.count()); + source_indexes.reserve(indexes.size()); for (const QModelIndex &idx : indexes) source_indexes << mapToSource(idx); return d->model->mimeData(source_indexes); @@ -2261,10 +2321,10 @@ bool QSortFilterProxyModel::insertRows(int row, int count, const QModelIndex &pa if (parent.isValid() && !source_parent.isValid()) return false; QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value(); - if (row > m->source_rows.count()) + if (row > m->source_rows.size()) return false; - int source_row = (row >= m->source_rows.count() - ? m->proxy_rows.count() + int source_row = (row >= m->source_rows.size() + ? m->proxy_rows.size() : m->source_rows.at(row)); return d->model->insertRows(source_row, count, source_parent); } @@ -2281,10 +2341,10 @@ bool QSortFilterProxyModel::insertColumns(int column, int count, const QModelInd if (parent.isValid() && !source_parent.isValid()) return false; QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value(); - if (column > m->source_columns.count()) + if (column > m->source_columns.size()) return false; - int source_column = (column >= m->source_columns.count() - ? m->proxy_columns.count() + int source_column = (column >= m->source_columns.size() + ? m->proxy_columns.size() : m->source_columns.at(column)); return d->model->insertColumns(source_column, count, source_parent); } @@ -2301,10 +2361,10 @@ bool QSortFilterProxyModel::removeRows(int row, int count, const QModelIndex &pa if (parent.isValid() && !source_parent.isValid()) return false; QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value(); - if (row + count > m->source_rows.count()) + if (row + count > m->source_rows.size()) return false; if ((count == 1) - || ((d->source_sort_column < 0) && (m->proxy_rows.count() == m->source_rows.count()))) { + || ((d->source_sort_column < 0) && (m->proxy_rows.size() == m->source_rows.size()))) { int source_row = m->source_rows.at(row); return d->model->removeRows(source_row, count, source_parent); } @@ -2316,7 +2376,7 @@ bool QSortFilterProxyModel::removeRows(int row, int count, const QModelIndex &pa rows.append(m->source_rows.at(i)); std::sort(rows.begin(), rows.end()); - int pos = rows.count() - 1; + int pos = rows.size() - 1; bool ok = true; while (pos >= 0) { const int source_end = rows.at(pos--); @@ -2343,9 +2403,9 @@ bool QSortFilterProxyModel::removeColumns(int column, int count, const QModelInd if (parent.isValid() && !source_parent.isValid()) return false; QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value(); - if (column + count > m->source_columns.count()) + if (column + count > m->source_columns.size()) return false; - if ((count == 1) || (m->proxy_columns.count() == m->source_columns.count())) { + if ((count == 1) || (m->proxy_columns.size() == m->source_columns.size())) { int source_column = m->source_columns.at(column); return d->model->removeColumns(source_column, count, source_parent); } @@ -2355,7 +2415,7 @@ bool QSortFilterProxyModel::removeColumns(int column, int count, const QModelInd for (int i = column; i < column + count; ++i) columns.append(m->source_columns.at(i)); - int pos = columns.count() - 1; + int pos = columns.size() - 1; bool ok = true; while (pos >= 0) { const int source_end = columns.at(pos--); @@ -2375,11 +2435,7 @@ bool QSortFilterProxyModel::removeColumns(int column, int count, const QModelInd */ void QSortFilterProxyModel::fetchMore(const QModelIndex &parent) { - Q_D(QSortFilterProxyModel); - QModelIndex source_parent; - if (d->indexValid(parent)) - source_parent = mapToSource(parent); - d->model->fetchMore(source_parent); + QAbstractProxyModel::fetchMore(parent); } /*! @@ -2387,11 +2443,7 @@ void QSortFilterProxyModel::fetchMore(const QModelIndex &parent) */ bool QSortFilterProxyModel::canFetchMore(const QModelIndex &parent) const { - Q_D(const QSortFilterProxyModel); - QModelIndex source_parent; - if (d->indexValid(parent)) - source_parent = mapToSource(parent); - return d->model->canFetchMore(source_parent); + return QAbstractProxyModel::canFetchMore(parent); } /*! @@ -2399,11 +2451,7 @@ bool QSortFilterProxyModel::canFetchMore(const QModelIndex &parent) const */ Qt::ItemFlags QSortFilterProxyModel::flags(const QModelIndex &index) const { - Q_D(const QSortFilterProxyModel); - QModelIndex source_index; - if (d->indexValid(index)) - source_index = mapToSource(index); - return d->model->flags(source_index); + return QAbstractProxyModel::flags(index); } /*! @@ -2444,7 +2492,10 @@ QSize QSortFilterProxyModel::span(const QModelIndex &index) const } /*! - \reimp + \reimp + Sorts the model by \a column in the given \a order. + If the sort \a column is less than zero, the model will be sorted by source model row + in the given \a order. */ void QSortFilterProxyModel::sort(int column, Qt::SortOrder order) { @@ -2459,9 +2510,12 @@ void QSortFilterProxyModel::sort(int column, Qt::SortOrder order) /*! \since 4.5 - \brief the column currently used for sorting + \return the column currently used for sorting - This returns the most recently used sort column. + This returns the most recently used sort column. The default value is -1, + which means that this proxy model does not sort. + + \sa sort() */ int QSortFilterProxyModel::sortColumn() const { @@ -2471,9 +2525,12 @@ int QSortFilterProxyModel::sortColumn() const /*! \since 4.5 - \brief the order currently used for sorting + \return the order currently used for sorting + + This returns the most recently used sort order. The default value is + Qt::AscendingOrder. - This returns the most recently used sort order. + \sa sort() */ Qt::SortOrder QSortFilterProxyModel::sortOrder() const { @@ -2481,35 +2538,62 @@ Qt::SortOrder QSortFilterProxyModel::sortOrder() const return d->sort_order; } -#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}. + Setting this property through the QRegularExpression overload 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. + \note Setting this property propagates the case sensitivity of the new + regular expression to the \l filterCaseSensitivity property, and so breaks + its binding. Likewise explicitly setting \l filterCaseSensitivity changes + the case sensitivity of the current regular expression, thereby breaking + its binding. + \sa filterCaseSensitivity, setFilterWildcard(), setFilterFixedString() */ QRegularExpression QSortFilterProxyModel::filterRegularExpression() const { Q_D(const QSortFilterProxyModel); - return d->filter_data; + return d->filter_regularexpression; +} + +QBindable<QRegularExpression> QSortFilterProxyModel::bindableFilterRegularExpression() +{ + Q_D(QSortFilterProxyModel); + return QBindable<QRegularExpression>(&d->filter_regularexpression); } void QSortFilterProxyModel::setFilterRegularExpression(const QRegularExpression ®ularExpression) { Q_D(QSortFilterProxyModel); + const QScopedPropertyUpdateGroup guard; + const bool regExpChanged = + regularExpression != d->filter_regularexpression.valueBypassingBindings(); + d->filter_regularexpression.removeBindingUnlessInWrapper(); + d->filter_casesensitive.removeBindingUnlessInWrapper(); + const Qt::CaseSensitivity cs = d->filter_casesensitive.valueBypassingBindings(); d->filter_about_to_be_changed(); - d->filter_data = regularExpression; + const Qt::CaseSensitivity updatedCs = + regularExpression.patternOptions() & QRegularExpression::CaseInsensitiveOption + ? Qt::CaseInsensitive : Qt::CaseSensitive; + d->filter_regularexpression.setValueBypassingBindings(regularExpression); + if (cs != updatedCs) + d->filter_casesensitive.setValueBypassingBindings(updatedCs); d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + // Do not change the evaluation logic, but notify only if the regular + // expression has actually changed. + if (regExpChanged) + d->filter_regularexpression.notify(); + if (cs != updatedCs) + d->filter_casesensitive.notify(); } -#endif /*! \property QSortFilterProxyModel::filterKeyColumn @@ -2527,10 +2611,25 @@ int QSortFilterProxyModel::filterKeyColumn() const void QSortFilterProxyModel::setFilterKeyColumn(int column) { + // While introducing new bindable properties, we still update the value + // unconditionally (even if it didn't really change), and call the + // filter_about_to_be_changed()/filter_changed() methods, so that we do + // not break any code. However we do notify the observing bindings only + // if the column has actually changed Q_D(QSortFilterProxyModel); + d->filter_column.removeBindingUnlessInWrapper(); d->filter_about_to_be_changed(); - d->filter_column = column; + const auto oldColumn = d->filter_column.valueBypassingBindings(); + d->filter_column.setValueBypassingBindings(column); d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + if (oldColumn != column) + d->filter_column.notify(); +} + +QBindable<int> QSortFilterProxyModel::bindableFilterKeyColumn() +{ + Q_D(QSortFilterProxyModel); + return QBindable<int>(&d->filter_column); } /*! @@ -2541,6 +2640,11 @@ void QSortFilterProxyModel::setFilterKeyColumn(int column) By default, the filter is case sensitive. + \note Setting this property propagates the new case sensitivity to the + \l filterRegularExpression property, and so breaks its binding. Likewise + explicitly setting \l filterRegularExpression changes the current case + sensitivity, thereby breaking its binding. + \sa filterRegularExpression, sortCaseSensitivity */ @@ -2553,22 +2657,36 @@ void QSortFilterProxyModel::setFilterKeyColumn(int column) Qt::CaseSensitivity QSortFilterProxyModel::filterCaseSensitivity() const { Q_D(const QSortFilterProxyModel); - return d->filter_data.patternOptions() & QRegularExpression::CaseInsensitiveOption ? - Qt::CaseInsensitive : Qt::CaseSensitive; + return d->filter_casesensitive; } void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs) { Q_D(QSortFilterProxyModel); - QRegularExpression::PatternOptions o = QRegularExpression::NoPatternOption; - if (cs == Qt::CaseInsensitive) - o = QRegularExpression::CaseInsensitiveOption; - if (o == d->filter_data.patternOptions()) + d->filter_casesensitive.removeBindingUnlessInWrapper(); + d->filter_regularexpression.removeBindingUnlessInWrapper(); + if (cs == d->filter_casesensitive) return; + + const QScopedPropertyUpdateGroup guard; + QRegularExpression::PatternOptions options = + d->filter_regularexpression.value().patternOptions(); + options.setFlag(QRegularExpression::CaseInsensitiveOption, cs == Qt::CaseInsensitive); + d->filter_casesensitive.setValueBypassingBindings(cs); + d->filter_about_to_be_changed(); - d->filter_data.setPatternOptions(o); + QRegularExpression re = d->filter_regularexpression; + re.setPatternOptions(options); + d->filter_regularexpression.setValueBypassingBindings(re); d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); - emit filterCaseSensitivityChanged(cs); + d->filter_regularexpression.notify(); + d->filter_casesensitive.notify(); +} + +QBindable<Qt::CaseSensitivity> QSortFilterProxyModel::bindableFilterCaseSensitivity() +{ + Q_D(QSortFilterProxyModel); + return QBindable<Qt::CaseSensitivity>(&d->filter_casesensitive); } /*! @@ -2596,12 +2714,19 @@ Qt::CaseSensitivity QSortFilterProxyModel::sortCaseSensitivity() const void QSortFilterProxyModel::setSortCaseSensitivity(Qt::CaseSensitivity cs) { Q_D(QSortFilterProxyModel); + d->sort_casesensitivity.removeBindingUnlessInWrapper(); if (d->sort_casesensitivity == cs) return; - d->sort_casesensitivity = cs; + d->sort_casesensitivity.setValueBypassingBindings(cs); d->sort(); - emit sortCaseSensitivityChanged(cs); + d->sort_casesensitivity.notify(); // also emits a signal +} + +QBindable<Qt::CaseSensitivity> QSortFilterProxyModel::bindableSortCaseSensitivity() +{ + Q_D(QSortFilterProxyModel); + return QBindable<Qt::CaseSensitivity>(&d->sort_casesensitivity); } /*! @@ -2629,15 +2754,21 @@ bool QSortFilterProxyModel::isSortLocaleAware() const void QSortFilterProxyModel::setSortLocaleAware(bool on) { Q_D(QSortFilterProxyModel); + d->sort_localeaware.removeBindingUnlessInWrapper(); if (d->sort_localeaware == on) return; - d->sort_localeaware = on; + d->sort_localeaware.setValueBypassingBindings(on); d->sort(); - emit sortLocaleAwareChanged(on); + d->sort_localeaware.notify(); // also emits a signal +} + +QBindable<bool> QSortFilterProxyModel::bindableIsSortLocaleAware() +{ + Q_D(QSortFilterProxyModel); + return QBindable<bool>(&d->sort_localeaware); } -#if QT_CONFIG(regularexpression) /*! \since 5.12 @@ -2647,45 +2778,70 @@ void QSortFilterProxyModel::setSortLocaleAware(bool on) This method should be preferred for new code as it will use QRegularExpression internally. + This method will reset the regular expression options + but respect case sensitivity. + + \note Calling this method updates the regular expression, thereby breaking + the binding for \l filterRegularExpression. However it has no effect on the + \l filterCaseSensitivity bindings. + \sa setFilterCaseSensitivity(), setFilterWildcard(), setFilterFixedString(), filterRegularExpression() */ void QSortFilterProxyModel::setFilterRegularExpression(const QString &pattern) { Q_D(QSortFilterProxyModel); + d->filter_regularexpression.removeBindingUnlessInWrapper(); d->filter_about_to_be_changed(); - QRegularExpression rx(pattern); - d->filter_data.setPattern(pattern); + d->set_filter_pattern(pattern); d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + d->filter_regularexpression.notify(); } -#endif /*! Sets the wildcard expression used to filter the contents of the source model to the given \a pattern. + This method will reset the regular expression options + but respect case sensitivity. + + \note Calling this method updates the regular expression, thereby breaking + the binding for \l filterRegularExpression. However it has no effect on the + \l filterCaseSensitivity bindings. + \sa setFilterCaseSensitivity(), setFilterRegularExpression(), setFilterFixedString(), filterRegularExpression() */ void QSortFilterProxyModel::setFilterWildcard(const QString &pattern) { Q_D(QSortFilterProxyModel); + d->filter_regularexpression.removeBindingUnlessInWrapper(); d->filter_about_to_be_changed(); - QString p = QRegularExpression::wildcardToRegularExpression(pattern, QRegularExpression::UnanchoredWildcardConversion); - d->filter_data.setPattern(p); + d->set_filter_pattern(QRegularExpression::wildcardToRegularExpression( + pattern, QRegularExpression::UnanchoredWildcardConversion)); d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + d->filter_regularexpression.notify(); } /*! Sets the fixed string used to filter the contents of the source model to the given \a pattern. + This method will reset the regular expression options + but respect case sensitivity. + + \note Calling this method updates the regular expression, thereby breaking + the binding for \l filterRegularExpression. However it has no effect on the + \l filterCaseSensitivity bindings. + \sa setFilterCaseSensitivity(), setFilterRegularExpression(), setFilterWildcard(), filterRegularExpression() */ void QSortFilterProxyModel::setFilterFixedString(const QString &pattern) { Q_D(QSortFilterProxyModel); + d->filter_regularexpression.removeBindingUnlessInWrapper(); d->filter_about_to_be_changed(); - d->filter_data.setPattern(QRegularExpression::escape(pattern)); + d->set_filter_pattern(QRegularExpression::escape(pattern)); d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); + d->filter_regularexpression.notify(); } /*! @@ -2703,6 +2859,8 @@ void QSortFilterProxyModel::setFilterFixedString(const QString &pattern) QComboBox. The default value is true. + + \sa sortColumn() */ bool QSortFilterProxyModel::dynamicSortFilter() const { @@ -2712,10 +2870,25 @@ bool QSortFilterProxyModel::dynamicSortFilter() const void QSortFilterProxyModel::setDynamicSortFilter(bool enable) { + // While introducing new bindable properties, we still update the value + // unconditionally (even if it didn't really change), and call the + // sort() method, so that we do not break any code. + // However we do notify the observing bindings only if the value has + // actually changed. Q_D(QSortFilterProxyModel); - d->dynamic_sortfilter = enable; + d->dynamic_sortfilter.removeBindingUnlessInWrapper(); + const bool valueChanged = d->dynamic_sortfilter.value() != enable; + d->dynamic_sortfilter.setValueBypassingBindings(enable); if (enable) d->sort(); + if (valueChanged) + d->dynamic_sortfilter.notify(); +} + +QBindable<bool> QSortFilterProxyModel::bindableDynamicSortFilter() +{ + Q_D(QSortFilterProxyModel); + return QBindable<bool>(&d->dynamic_sortfilter); } /*! @@ -2743,11 +2916,18 @@ int QSortFilterProxyModel::sortRole() const void QSortFilterProxyModel::setSortRole(int role) { Q_D(QSortFilterProxyModel); - if (d->sort_role == role) + d->sort_role.removeBindingUnlessInWrapper(); + if (d->sort_role.valueBypassingBindings() == role) return; - d->sort_role = role; + d->sort_role.setValueBypassingBindings(role); d->sort(); - emit sortRoleChanged(role); + d->sort_role.notify(); // also emits a signal +} + +QBindable<int> QSortFilterProxyModel::bindableSortRole() +{ + Q_D(QSortFilterProxyModel); + return QBindable<int>(&d->sort_role); } /*! @@ -2775,12 +2955,19 @@ int QSortFilterProxyModel::filterRole() const void QSortFilterProxyModel::setFilterRole(int role) { Q_D(QSortFilterProxyModel); - if (d->filter_role == role) + d->filter_role.removeBindingUnlessInWrapper(); + if (d->filter_role.valueBypassingBindings() == role) return; d->filter_about_to_be_changed(); - d->filter_role = role; + d->filter_role.setValueBypassingBindings(role); d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); - emit filterRoleChanged(role); + d->filter_role.notify(); // also emits a signal +} + +QBindable<int> QSortFilterProxyModel::bindableFilterRole() +{ + Q_D(QSortFilterProxyModel); + return QBindable<int>(&d->filter_role); } /*! @@ -2810,12 +2997,19 @@ bool QSortFilterProxyModel::isRecursiveFilteringEnabled() const void QSortFilterProxyModel::setRecursiveFilteringEnabled(bool recursive) { Q_D(QSortFilterProxyModel); + d->filter_recursive.removeBindingUnlessInWrapper(); if (d->filter_recursive == recursive) return; d->filter_about_to_be_changed(); - d->filter_recursive = recursive; + d->filter_recursive.setValueBypassingBindings(recursive); d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); - emit recursiveFilteringEnabledChanged(recursive); + d->filter_recursive.notify(); // also emits a signal +} + +QBindable<bool> QSortFilterProxyModel::bindableRecursiveFilteringEnabled() +{ + Q_D(QSortFilterProxyModel); + return QBindable<bool>(&d->filter_recursive); } /*! @@ -2832,7 +3026,8 @@ void QSortFilterProxyModel::setRecursiveFilteringEnabled(bool recursive) /*! \since 6.0 - \fn void QSortFilterProxyModel::showMatchesChildrenChanged(bool autoAcceptChildRows) + \fn void QSortFilterProxyModel::autoAcceptChildRowsChanged(bool autoAcceptChildRows) + \brief This signals is emitted when the value of the \a autoAcceptChildRows property is changed. \sa autoAcceptChildRows @@ -2846,13 +3041,20 @@ bool QSortFilterProxyModel::autoAcceptChildRows() const void QSortFilterProxyModel::setAutoAcceptChildRows(bool accept) { Q_D(QSortFilterProxyModel); + d->accept_children.removeBindingUnlessInWrapper(); if (d->accept_children == accept) return; d->filter_about_to_be_changed(); - d->accept_children = accept; + d->accept_children.setValueBypassingBindings(accept); d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); - emit autoAcceptChildRowsChanged(accept); + d->accept_children.notify(); // also emits a signal +} + +QBindable<bool> QSortFilterProxyModel::bindableAutoAcceptChildRows() +{ + Q_D(QSortFilterProxyModel); + return QBindable<bool>(&d->accept_children); } /*! @@ -2995,23 +3197,25 @@ bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex & { Q_D(const QSortFilterProxyModel); - if (d->filter_data.pattern().isEmpty()) + if (d->filter_regularexpression.value().pattern().isEmpty()) return true; + + int column_count = d->model->columnCount(source_parent); if (d->filter_column == -1) { - int column_count = d->model->columnCount(source_parent); for (int column = 0; column < column_count; ++column) { QModelIndex source_index = d->model->index(source_row, column, source_parent); QString key = d->model->data(source_index, d->filter_role).toString(); - if (d->filter_data.match(key).hasMatch()) + if (key.contains(d->filter_regularexpression.value())) return true; } return false; } - QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent); - if (!source_index.isValid()) // the column may not exist + + if (d->filter_column >= column_count) // the column may not exist return true; + QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent); QString key = d->model->data(source_index, d->filter_role).toString(); - return d->filter_data.match(key).hasMatch(); + return key.contains(d->filter_regularexpression.value()); } /*! |