📄 qsortfilterproxymodel.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.**** This file is part of the QtGui module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://www.trolltech.com/products/qt/opensource.html**** If you are unsure which license is appropriate for your use, please** review the following information:** http://www.trolltech.com/products/qt/licensing.html or contact the** sales department at sales@trolltech.com.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#include "qsortfilterproxymodel.h"#ifndef QT_NO_SORTFILTERPROXYMODEL#include "qitemselectionmodel.h"#include <qsize.h>#include <qdebug.h>#include <qdatetime.h>#include <private/qabstractitemmodel_p.h>#include <private/qabstractproxymodel_p.h>class QSortFilterProxyModelLessThan{public: inline QSortFilterProxyModelLessThan(int column, const QModelIndex &parent, const QAbstractItemModel *source, const QSortFilterProxyModel *proxy) : sort_column(column), source_parent(parent), source_model(source), proxy_model(proxy) {} inline bool operator()(int r1, int r2) const { QModelIndex i1 = source_model->index(r1, sort_column, source_parent); QModelIndex i2 = source_model->index(r2, sort_column, source_parent); return proxy_model->lessThan(i1, i2); }private: int sort_column; QModelIndex source_parent; const QAbstractItemModel *source_model; const QSortFilterProxyModel *proxy_model;};class QSortFilterProxyModelGreaterThan{public: inline QSortFilterProxyModelGreaterThan(int column, const QModelIndex &parent, const QAbstractItemModel *source, const QSortFilterProxyModel *proxy) : sort_column(column), source_parent(parent), source_model(source), proxy_model(proxy) {} inline bool operator()(int r1, int r2) const { QModelIndex i1 = source_model->index(r1, sort_column, source_parent); QModelIndex i2 = source_model->index(r2, sort_column, source_parent); return proxy_model->lessThan(i2, i1); }private: int sort_column; QModelIndex source_parent; const QAbstractItemModel *source_model; const QSortFilterProxyModel *proxy_model;};class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate{ Q_DECLARE_PUBLIC(QSortFilterProxyModel)public: struct Mapping { QVector<int> source_rows; QVector<int> source_columns; QVector<int> proxy_rows; QVector<int> proxy_columns; QVector<QModelIndex> mapped_children; }; mutable QMap<QModelIndex, Mapping*> source_index_mapping; int sort_column; Qt::SortOrder sort_order; int filter_column; QRegExp filter_regexp; QMap<QModelIndex, Mapping *>::const_iterator create_mapping( const QModelIndex &source_parent) const; QModelIndex proxy_to_source(const QModelIndex &proxyIndex) const; QModelIndex source_to_proxy(const QModelIndex &sourceIndex) const; void remove_from_mapping(const QModelIndex &source_parent); inline QMap<QModelIndex, Mapping *>::const_iterator index_to_iterator( const QModelIndex &proxy_index) const { Q_ASSERT(proxy_index.isValid()); const void *p = proxy_index.internalPointer(); Q_ASSERT(p); QMap<QModelIndex, Mapping *>::const_iterator it = reinterpret_cast<QMap<QModelIndex, Mapping *>::const_iterator & >(p); Q_ASSERT(it != source_index_mapping.end()); Q_ASSERT(it.value()); return it; } inline QModelIndex create_index(int row, int column, QMap<QModelIndex, Mapping*>::const_iterator it) const { const void *p = static_cast<const void *>(it); return q_func()->createIndex(row, column, const_cast<void *>(p)); } void _q_sourceDataChanged(const QModelIndex &source_top_left, const QModelIndex &source_bottom_right); void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int start, int end); void _q_sourceLayoutAboutToBeChanged(const QModelIndex &source_parent); void _q_sourceLayoutChanged(); void _q_sourceReset(); void clear_mapping();};typedef QMap<QModelIndex, QSortFilterProxyModelPrivate::Mapping *> IndexMap;void QSortFilterProxyModelPrivate::remove_from_mapping(const QModelIndex &source_parent){ if (Mapping *m = source_index_mapping.take(source_parent)) { for (int i = 0; i < m->mapped_children.size(); ++i) remove_from_mapping(m->mapped_children.at(i)); delete m; }}void QSortFilterProxyModelPrivate::clear_mapping(){ // store the persistent indexes QModelIndexList source_indexes; int persistent_count = persistent.indexes.count(); for (int i = 0; i < persistent_count; ++i) { QModelIndex proxy_index = persistent.indexes.at(i)->index; QModelIndex source_index = proxy_to_source(proxy_index); source_indexes.append(source_index); } qDeleteAll(source_index_mapping); source_index_mapping.clear(); // update the persistent indexes for (int i = 0; i < persistent_count; ++i) { QModelIndex source_index = source_indexes.at(i); create_mapping(source_index.parent()); QModelIndex proxy_index = source_to_proxy(source_index); persistent.indexes[i]->index = proxy_index; }}IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping( const QModelIndex &source_parent) const{ Q_Q(const QSortFilterProxyModel); IndexMap::const_iterator it = source_index_mapping.find(source_parent); if (it != source_index_mapping.end()) // was mapped already return it; Mapping *m = new Mapping; int source_rows = model->rowCount(source_parent); for (int i = 0; i < source_rows; ++i) { if (q->filterAcceptsRow(i, source_parent)) m->source_rows.append(i); } int source_cols = model->columnCount(source_parent); for (int i = 0; i < source_cols; ++i) { if (q->filterAcceptsColumn(i, source_parent)) m->source_columns.append(i); } if (sort_column >= 0) { // only sorts rows if (sort_order == Qt::AscendingOrder) { QSortFilterProxyModelLessThan lt(sort_column, source_parent, model, q); qStableSort(m->source_rows.begin(), m->source_rows.end(), lt); } else { QSortFilterProxyModelGreaterThan gt(sort_column, source_parent, model, q); qStableSort(m->source_rows.begin(), m->source_rows.end(), gt); } } m->proxy_rows.fill(-1, source_rows); for (int i = 0; i < m->source_rows.size(); ++i) m->proxy_rows[m->source_rows.at(i)] = i; m->proxy_columns.fill(-1, source_cols); for (int i = 0; i < m->source_columns.size(); ++i) m->proxy_columns[m->source_columns.at(i)] = i; it = source_index_mapping.insert(source_parent, m); if (source_parent.isValid()) { QModelIndex source_grand_parent = source_parent.parent(); IndexMap::const_iterator it2 = create_mapping(source_grand_parent); Q_ASSERT(it2 != source_index_mapping.end()); it2.value()->mapped_children.append(source_parent); } Q_ASSERT(it != source_index_mapping.end()); Q_ASSERT(it.value()); return it;}QModelIndex QSortFilterProxyModelPrivate::proxy_to_source(const QModelIndex &proxy_index) const{ if (!proxy_index.isValid()) return QModelIndex(); // for now; we may want to be able to set a root index later IndexMap::const_iterator it = index_to_iterator(proxy_index); Mapping *m = it.value(); if ((proxy_index.row() >= m->source_rows.size()) || (proxy_index.column() >= m->source_columns.size())) return QModelIndex(); int source_row = m->source_rows.at(proxy_index.row()); int source_col = m->source_columns.at(proxy_index.column()); return model->index(source_row, source_col, it.key());}QModelIndex QSortFilterProxyModelPrivate::source_to_proxy(const QModelIndex &source_index) const{ if (!source_index.isValid()) return QModelIndex(); // for now; we may want to be able to set a root index later QModelIndex source_parent = source_index.parent(); IndexMap::const_iterator it = create_mapping(source_parent); Mapping *m = it.value(); if ((source_index.row() >= m->proxy_rows.size()) || (source_index.column() >= m->proxy_columns.size())) return QModelIndex(); int proxy_row = m->proxy_rows.at(source_index.row()); int proxy_column = m->proxy_columns.at(source_index.column()); return create_index(proxy_row, proxy_column, it);}void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &source_top_left, const QModelIndex &source_bottom_right){ Q_Q(QSortFilterProxyModel); QModelIndex proxy_top_left = source_to_proxy(source_top_left); QModelIndex proxy_bottom_right = source_to_proxy(source_bottom_right); emit q->dataChanged(proxy_top_left, proxy_bottom_right);}void QSortFilterProxyModelPrivate::_q_sourceHeaderDataChanged(Qt::Orientation orientation, int start, int end){ Q_Q(QSortFilterProxyModel); Mapping *m = create_mapping(QModelIndex()).value(); int proxy_start = (orientation == Qt::Vertical ? m->proxy_rows.at(start) : m->proxy_columns.at(start)); int proxy_end = (orientation == Qt::Vertical ? m->proxy_rows.at(end) : m->proxy_columns.at(end)); emit q->headerDataChanged(orientation, proxy_start, proxy_end);}void QSortFilterProxyModelPrivate::_q_sourceLayoutAboutToBeChanged(const QModelIndex &source_parent){ Q_Q(QSortFilterProxyModel); const QModelIndex proxy_parent = source_to_proxy(source_parent);; int first = 0; int last = q->rowCount(proxy_parent) - 1; changes.push(QAbstractItemModelPrivate::Change(proxy_parent, first, last)); if (last >= first) rowsAboutToBeRemoved(proxy_parent, first, last); remove_from_mapping(source_parent);}void QSortFilterProxyModelPrivate::_q_sourceLayoutChanged(){ Q_Q(QSortFilterProxyModel); QAbstractItemModelPrivate::Change change = changes.pop(); if (change.last >= change.first) rowsRemoved(change.parent, change.first, change.last); emit q->layoutChanged();}void QSortFilterProxyModelPrivate::_q_sourceReset(){ Q_Q(QSortFilterProxyModel); // All internal structures are deleted in clear() q->reset();}/*! \since 4.1 \class QSortFilterProxyModel \brief The QSortFilterProxyModel class provides support for sorting and filtering data passed between another model and a view. \ingroup model-view QSortFilterProxyModel can be used for sorting items, filtering out items, or both. The model transforms the structure of a source model by mapping the model indexes it supplies to new indexes, corresponding to different locations, for views to use. This approach allows a given source model to be restructured as far as views are concerned without requiring any transformations on the underlying data, and without duplicating the data in memory. Let's assume that we want to sort and filter the items provided by a custom model. The code to set up the model and the view, \e without sorting and filtering, would look like this: \code QTreeView *treeView = new QTreeView; MyItemModel *model = new MyItemModel(this); treeView->setModel(model); \endcode To add sorting and filtering support to \c MyItemModel, we need to create a QSortFilterProxyModel, call setSourceModel() with the \c MyItemModel as argument, and install the QSortFilterProxyModel on the view: \code QTreeView *treeView = new QTreeView; MyItemModel *sourceModel = new MyItemModel(this); QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this); proxyModel->setSourceModel(sourceModel); treeView->setModel(proxyModel); \endcode At this point, neither sorting nor filtering is enabled; the original data is displayed in the view. Any changes made through the QSortFilterProxyModel are applied to the original model. The QSortFilterProxyModel acts as a wrapper for the original model. If you need to convert source \l{QModelIndex}es to sorted/filtered model indexes or vice versa, use mapToSource(), mapFromSource(), mapSelectionToSource(), and mapSelectionFromSource(). By default, sorting and filtering isn't dynamically reapplied whenever the data of the original model changes, as an optimization. To enable dynamic sorting and filtering, call setDynamicSortFilter(true). At any time, you can call clear() to resort/refilter the data. \section1 Sorting QHeaderView has a \l {QHeaderVIew::showSortIndicator()}{showSortIndicator()} property and a \l {QHeaderView::setClickable()}{setClickable()} function that together control whether the user can sort the view by clicking the view's horizontal header. For example: \code treeView->header()->setClickable(true); treeView->header()->setSortIndicatorShown(true); \endcode When this feature is on, clicking on a header section sorts the items according to that column. By clicking repeatedly, the user can alternate between ascending and descending order. \image qsortfilterproxymodel-sorting.png A sorted QTreeView Behind the scene, the view calls the sort() virtual function on
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -