Merge lp:~saviq/ubuntu/saucy/qtbase-opensource-src/add-proxymodel-patch into lp:ubuntu/saucy/qtbase-opensource-src

Proposed by Michał Sawicz
Status: Merged
Merge reported by: Sebastien Bacher
Merged at revision: not available
Proposed branch: lp:~saviq/ubuntu/saucy/qtbase-opensource-src/add-proxymodel-patch
Merge into: lp:ubuntu/saucy/qtbase-opensource-src
Diff against target: 6723 lines (+6654/-2)
7 files modified
.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/src/corelib/itemmodels/qsortfilterproxymodel.cpp (+2767/-0)
.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp (+3640/-0)
.pc/applied-patches (+1/-0)
debian/patches/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch (+145/-0)
debian/patches/series (+1/-0)
src/corelib/itemmodels/qsortfilterproxymodel.cpp (+5/-2)
tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp (+95/-0)
To merge this branch: bzr merge lp:~saviq/ubuntu/saucy/qtbase-opensource-src/add-proxymodel-patch
Reviewer Review Type Date Requested Status
Ubuntu branches Pending
Review via email: mp+189028@code.launchpad.net

Commit message

Add QSortFilterProxyModel patch.

Qt code review at https://codereview.qt-project.org/#change,67214

Description of the change

A fix from:

https://codereview.qt-project.org/#change,67214

Problem: reordering categories in dash breaks.

To post a comment you must log in.
Revision history for this message
Sebastien Bacher (seb128) wrote :

the update with the patch has been uploaded

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch'
2=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/src'
3=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/src/corelib'
4=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/src/corelib/itemmodels'
5=== added file '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/src/corelib/itemmodels/qsortfilterproxymodel.cpp'
6--- .pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/src/corelib/itemmodels/qsortfilterproxymodel.cpp 1970-01-01 00:00:00 +0000
7+++ .pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/src/corelib/itemmodels/qsortfilterproxymodel.cpp 2013-10-03 10:06:38 +0000
8@@ -0,0 +1,2767 @@
9+/****************************************************************************
10+**
11+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
12+** Contact: http://www.qt-project.org/legal
13+**
14+** This file is part of the QtGui module of the Qt Toolkit.
15+**
16+** $QT_BEGIN_LICENSE:LGPL$
17+** Commercial License Usage
18+** Licensees holding valid commercial Qt licenses may use this file in
19+** accordance with the commercial license agreement provided with the
20+** Software or, alternatively, in accordance with the terms contained in
21+** a written agreement between you and Digia. For licensing terms and
22+** conditions see http://qt.digia.com/licensing. For further information
23+** use the contact form at http://qt.digia.com/contact-us.
24+**
25+** GNU Lesser General Public License Usage
26+** Alternatively, this file may be used under the terms of the GNU Lesser
27+** General Public License version 2.1 as published by the Free Software
28+** Foundation and appearing in the file LICENSE.LGPL included in the
29+** packaging of this file. Please review the following information to
30+** ensure the GNU Lesser General Public License version 2.1 requirements
31+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
32+**
33+** In addition, as a special exception, Digia gives you certain additional
34+** rights. These rights are described in the Digia Qt LGPL Exception
35+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
36+**
37+** GNU General Public License Usage
38+** Alternatively, this file may be used under the terms of the GNU
39+** General Public License version 3.0 as published by the Free Software
40+** Foundation and appearing in the file LICENSE.GPL included in the
41+** packaging of this file. Please review the following information to
42+** ensure the GNU General Public License version 3.0 requirements will be
43+** met: http://www.gnu.org/copyleft/gpl.html.
44+**
45+**
46+** $QT_END_LICENSE$
47+**
48+****************************************************************************/
49+
50+#include "qsortfilterproxymodel.h"
51+
52+#ifndef QT_NO_SORTFILTERPROXYMODEL
53+
54+#include "qitemselectionmodel.h"
55+#include <qsize.h>
56+#include <qdebug.h>
57+#include <qdatetime.h>
58+#include <qpair.h>
59+#include <qstringlist.h>
60+#include <private/qabstractitemmodel_p.h>
61+#include <private/qabstractproxymodel_p.h>
62+
63+#include <algorithm>
64+
65+QT_BEGIN_NAMESPACE
66+
67+typedef QList<QPair<QModelIndex, QPersistentModelIndex> > QModelIndexPairList;
68+
69+static inline QSet<int> qVectorToSet(const QVector<int> &vector)
70+{
71+ QSet<int> set;
72+ set.reserve(vector.size());
73+ for(int i=0; i < vector.size(); ++i)
74+ set << vector.at(i);
75+ return set;
76+}
77+
78+class QSortFilterProxyModelLessThan
79+{
80+public:
81+ inline QSortFilterProxyModelLessThan(int column, const QModelIndex &parent,
82+ const QAbstractItemModel *source,
83+ const QSortFilterProxyModel *proxy)
84+ : sort_column(column), source_parent(parent), source_model(source), proxy_model(proxy) {}
85+
86+ inline bool operator()(int r1, int r2) const
87+ {
88+ QModelIndex i1 = source_model->index(r1, sort_column, source_parent);
89+ QModelIndex i2 = source_model->index(r2, sort_column, source_parent);
90+ return proxy_model->lessThan(i1, i2);
91+ }
92+
93+private:
94+ int sort_column;
95+ QModelIndex source_parent;
96+ const QAbstractItemModel *source_model;
97+ const QSortFilterProxyModel *proxy_model;
98+};
99+
100+class QSortFilterProxyModelGreaterThan
101+{
102+public:
103+ inline QSortFilterProxyModelGreaterThan(int column, const QModelIndex &parent,
104+ const QAbstractItemModel *source,
105+ const QSortFilterProxyModel *proxy)
106+ : sort_column(column), source_parent(parent),
107+ source_model(source), proxy_model(proxy) {}
108+
109+ inline bool operator()(int r1, int r2) const
110+ {
111+ QModelIndex i1 = source_model->index(r1, sort_column, source_parent);
112+ QModelIndex i2 = source_model->index(r2, sort_column, source_parent);
113+ return proxy_model->lessThan(i2, i1);
114+ }
115+
116+private:
117+ int sort_column;
118+ QModelIndex source_parent;
119+ const QAbstractItemModel *source_model;
120+ const QSortFilterProxyModel *proxy_model;
121+};
122+
123+
124+//this struct is used to store what are the rows that are removed
125+//between a call to rowsAboutToBeRemoved and rowsRemoved
126+//it avoids readding rows to the mapping that are currently being removed
127+struct QRowsRemoval
128+{
129+ QRowsRemoval(const QModelIndex &parent_source, int start, int end) : parent_source(parent_source), start(start), end(end)
130+ {
131+ }
132+
133+ QRowsRemoval() : start(-1), end(-1)
134+ {
135+ }
136+
137+ bool contains(QModelIndex parent, int row)
138+ {
139+ do {
140+ if (parent == parent_source)
141+ return row >= start && row <= end;
142+ row = parent.row();
143+ parent = parent.parent();
144+ } while (row >= 0);
145+ return false;
146+ }
147+private:
148+ QModelIndex parent_source;
149+ int start;
150+ int end;
151+};
152+
153+class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate
154+{
155+ Q_DECLARE_PUBLIC(QSortFilterProxyModel)
156+
157+public:
158+ struct Mapping {
159+ QVector<int> source_rows;
160+ QVector<int> source_columns;
161+ QVector<int> proxy_rows;
162+ QVector<int> proxy_columns;
163+ QVector<QModelIndex> mapped_children;
164+ QHash<QModelIndex, Mapping *>::const_iterator map_iter;
165+ };
166+
167+ mutable QHash<QModelIndex, Mapping*> source_index_mapping;
168+
169+ int source_sort_column;
170+ int proxy_sort_column;
171+ Qt::SortOrder sort_order;
172+ Qt::CaseSensitivity sort_casesensitivity;
173+ int sort_role;
174+ bool sort_localeaware;
175+
176+ int filter_column;
177+ QRegExp filter_regexp;
178+ int filter_role;
179+
180+ bool dynamic_sortfilter;
181+ QRowsRemoval itemsBeingRemoved;
182+
183+ QModelIndexPairList saved_persistent_indexes;
184+
185+ QHash<QModelIndex, Mapping *>::const_iterator create_mapping(
186+ const QModelIndex &source_parent) const;
187+ QModelIndex proxy_to_source(const QModelIndex &proxyIndex) const;
188+ QModelIndex source_to_proxy(const QModelIndex &sourceIndex) const;
189+ bool can_create_mapping(const QModelIndex &source_parent) const;
190+
191+ void remove_from_mapping(const QModelIndex &source_parent);
192+
193+ inline QHash<QModelIndex, Mapping *>::const_iterator index_to_iterator(
194+ const QModelIndex &proxy_index) const
195+ {
196+ Q_ASSERT(proxy_index.isValid());
197+ Q_ASSERT(proxy_index.model() == q_func());
198+ const void *p = proxy_index.internalPointer();
199+ Q_ASSERT(p);
200+ QHash<QModelIndex, Mapping *>::const_iterator it =
201+ static_cast<const Mapping*>(p)->map_iter;
202+ Q_ASSERT(it != source_index_mapping.constEnd());
203+ Q_ASSERT(it.value());
204+ return it;
205+ }
206+
207+ inline QModelIndex create_index(int row, int column,
208+ QHash<QModelIndex, Mapping*>::const_iterator it) const
209+ {
210+ return q_func()->createIndex(row, column, *it);
211+ }
212+
213+ void _q_sourceDataChanged(const QModelIndex &source_top_left,
214+ const QModelIndex &source_bottom_right);
215+ void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int start, int end);
216+
217+ void _q_sourceAboutToBeReset();
218+ void _q_sourceReset();
219+
220+ void _q_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint);
221+ void _q_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint);
222+
223+ void _q_sourceRowsAboutToBeInserted(const QModelIndex &source_parent,
224+ int start, int end);
225+ void _q_sourceRowsInserted(const QModelIndex &source_parent,
226+ int start, int end);
227+ void _q_sourceRowsAboutToBeRemoved(const QModelIndex &source_parent,
228+ int start, int end);
229+ void _q_sourceRowsRemoved(const QModelIndex &source_parent,
230+ int start, int end);
231+ void _q_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent,
232+ int sourceStart, int sourceEnd,
233+ const QModelIndex &destParent, int dest);
234+ void _q_sourceRowsMoved(const QModelIndex &sourceParent,
235+ int sourceStart, int sourceEnd,
236+ const QModelIndex &destParent, int dest);
237+ void _q_sourceColumnsAboutToBeInserted(const QModelIndex &source_parent,
238+ int start, int end);
239+ void _q_sourceColumnsInserted(const QModelIndex &source_parent,
240+ int start, int end);
241+ void _q_sourceColumnsAboutToBeRemoved(const QModelIndex &source_parent,
242+ int start, int end);
243+ void _q_sourceColumnsRemoved(const QModelIndex &source_parent,
244+ int start, int end);
245+ void _q_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent,
246+ int sourceStart, int sourceEnd,
247+ const QModelIndex &destParent, int dest);
248+ void _q_sourceColumnsMoved(const QModelIndex &sourceParent,
249+ int sourceStart, int sourceEnd,
250+ const QModelIndex &destParent, int dest);
251+
252+ void _q_clearMapping();
253+
254+ void sort();
255+ bool update_source_sort_column();
256+ void sort_source_rows(QVector<int> &source_rows,
257+ const QModelIndex &source_parent) const;
258+ QVector<QPair<int, QVector<int > > > proxy_intervals_for_source_items_to_add(
259+ const QVector<int> &proxy_to_source, const QVector<int> &source_items,
260+ const QModelIndex &source_parent, Qt::Orientation orient) const;
261+ QVector<QPair<int, int > > proxy_intervals_for_source_items(
262+ const QVector<int> &source_to_proxy, const QVector<int> &source_items) const;
263+ void insert_source_items(
264+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
265+ const QVector<int> &source_items, const QModelIndex &source_parent,
266+ Qt::Orientation orient, bool emit_signal = true);
267+ void remove_source_items(
268+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
269+ const QVector<int> &source_items, const QModelIndex &source_parent,
270+ Qt::Orientation orient, bool emit_signal = true);
271+ void remove_proxy_interval(
272+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
273+ int proxy_start, int proxy_end, const QModelIndex &proxy_parent,
274+ Qt::Orientation orient, bool emit_signal = true);
275+ void build_source_to_proxy_mapping(
276+ const QVector<int> &proxy_to_source, QVector<int> &source_to_proxy) const;
277+ void source_items_inserted(const QModelIndex &source_parent,
278+ int start, int end, Qt::Orientation orient);
279+ void source_items_about_to_be_removed(const QModelIndex &source_parent,
280+ int start, int end, Qt::Orientation orient);
281+ void source_items_removed(const QModelIndex &source_parent,
282+ int start, int end, Qt::Orientation orient);
283+ void proxy_item_range(
284+ const QVector<int> &source_to_proxy, const QVector<int> &source_items,
285+ int &proxy_low, int &proxy_high) const;
286+
287+ QModelIndexPairList store_persistent_indexes();
288+ void update_persistent_indexes(const QModelIndexPairList &source_indexes);
289+
290+ void filter_changed(const QModelIndex &source_parent = QModelIndex());
291+ QSet<int> handle_filter_changed(
292+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
293+ const QModelIndex &source_parent, Qt::Orientation orient);
294+
295+ void updateChildrenMapping(const QModelIndex &source_parent, Mapping *parent_mapping,
296+ Qt::Orientation orient, int start, int end, int delta_item_count, bool remove);
297+
298+ virtual void _q_sourceModelDestroyed();
299+};
300+
301+typedef QHash<QModelIndex, QSortFilterProxyModelPrivate::Mapping *> IndexMap;
302+
303+void QSortFilterProxyModelPrivate::_q_sourceModelDestroyed()
304+{
305+ QAbstractProxyModelPrivate::_q_sourceModelDestroyed();
306+ _q_clearMapping();
307+}
308+
309+void QSortFilterProxyModelPrivate::remove_from_mapping(const QModelIndex &source_parent)
310+{
311+ if (Mapping *m = source_index_mapping.take(source_parent)) {
312+ for (int i = 0; i < m->mapped_children.size(); ++i)
313+ remove_from_mapping(m->mapped_children.at(i));
314+ delete m;
315+ }
316+}
317+
318+void QSortFilterProxyModelPrivate::_q_clearMapping()
319+{
320+ // store the persistent indexes
321+ QModelIndexPairList source_indexes = store_persistent_indexes();
322+
323+ qDeleteAll(source_index_mapping);
324+ source_index_mapping.clear();
325+ if (dynamic_sortfilter && update_source_sort_column()) {
326+ //update_source_sort_column might have created wrong mapping so we have to clear it again
327+ qDeleteAll(source_index_mapping);
328+ source_index_mapping.clear();
329+ }
330+
331+ // update the persistent indexes
332+ update_persistent_indexes(source_indexes);
333+}
334+
335+IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping(
336+ const QModelIndex &source_parent) const
337+{
338+ Q_Q(const QSortFilterProxyModel);
339+
340+ IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
341+ if (it != source_index_mapping.constEnd()) // was mapped already
342+ return it;
343+
344+ Mapping *m = new Mapping;
345+
346+ int source_rows = model->rowCount(source_parent);
347+ m->source_rows.reserve(source_rows);
348+ for (int i = 0; i < source_rows; ++i) {
349+ if (q->filterAcceptsRow(i, source_parent))
350+ m->source_rows.append(i);
351+ }
352+ int source_cols = model->columnCount(source_parent);
353+ m->source_columns.reserve(source_cols);
354+ for (int i = 0; i < source_cols; ++i) {
355+ if (q->filterAcceptsColumn(i, source_parent))
356+ m->source_columns.append(i);
357+ }
358+
359+ sort_source_rows(m->source_rows, source_parent);
360+ m->proxy_rows.resize(source_rows);
361+ build_source_to_proxy_mapping(m->source_rows, m->proxy_rows);
362+ m->proxy_columns.resize(source_cols);
363+ build_source_to_proxy_mapping(m->source_columns, m->proxy_columns);
364+
365+ it = IndexMap::const_iterator(source_index_mapping.insert(source_parent, m));
366+ m->map_iter = it;
367+
368+ if (source_parent.isValid()) {
369+ QModelIndex source_grand_parent = source_parent.parent();
370+ IndexMap::const_iterator it2 = create_mapping(source_grand_parent);
371+ Q_ASSERT(it2 != source_index_mapping.constEnd());
372+ it2.value()->mapped_children.append(source_parent);
373+ }
374+
375+ Q_ASSERT(it != source_index_mapping.constEnd());
376+ Q_ASSERT(it.value());
377+
378+ return it;
379+}
380+
381+QModelIndex QSortFilterProxyModelPrivate::proxy_to_source(const QModelIndex &proxy_index) const
382+{
383+ if (!proxy_index.isValid())
384+ return QModelIndex(); // for now; we may want to be able to set a root index later
385+ if (proxy_index.model() != q_func()) {
386+ qWarning() << "QSortFilterProxyModel: index from wrong model passed to mapToSource";
387+ Q_ASSERT(!"QSortFilterProxyModel: index from wrong model passed to mapToSource");
388+ return QModelIndex();
389+ }
390+ IndexMap::const_iterator it = index_to_iterator(proxy_index);
391+ Mapping *m = it.value();
392+ if ((proxy_index.row() >= m->source_rows.size()) || (proxy_index.column() >= m->source_columns.size()))
393+ return QModelIndex();
394+ int source_row = m->source_rows.at(proxy_index.row());
395+ int source_col = m->source_columns.at(proxy_index.column());
396+ return model->index(source_row, source_col, it.key());
397+}
398+
399+QModelIndex QSortFilterProxyModelPrivate::source_to_proxy(const QModelIndex &source_index) const
400+{
401+ if (!source_index.isValid())
402+ return QModelIndex(); // for now; we may want to be able to set a root index later
403+ if (source_index.model() != model) {
404+ qWarning() << "QSortFilterProxyModel: index from wrong model passed to mapFromSource";
405+ Q_ASSERT(!"QSortFilterProxyModel: index from wrong model passed to mapFromSource");
406+ return QModelIndex();
407+ }
408+ QModelIndex source_parent = source_index.parent();
409+ IndexMap::const_iterator it = create_mapping(source_parent);
410+ Mapping *m = it.value();
411+ if ((source_index.row() >= m->proxy_rows.size()) || (source_index.column() >= m->proxy_columns.size()))
412+ return QModelIndex();
413+ int proxy_row = m->proxy_rows.at(source_index.row());
414+ int proxy_column = m->proxy_columns.at(source_index.column());
415+ if (proxy_row == -1 || proxy_column == -1)
416+ return QModelIndex();
417+ return create_index(proxy_row, proxy_column, it);
418+}
419+
420+bool QSortFilterProxyModelPrivate::can_create_mapping(const QModelIndex &source_parent) const
421+{
422+ if (source_parent.isValid()) {
423+ QModelIndex source_grand_parent = source_parent.parent();
424+ IndexMap::const_iterator it = source_index_mapping.constFind(source_grand_parent);
425+ if (it == source_index_mapping.constEnd()) {
426+ // Don't care, since we don't have mapping for the grand parent
427+ return false;
428+ }
429+ Mapping *gm = it.value();
430+ if (gm->proxy_rows.at(source_parent.row()) == -1 ||
431+ gm->proxy_columns.at(source_parent.column()) == -1) {
432+ // Don't care, since parent is filtered
433+ return false;
434+ }
435+ }
436+ return true;
437+}
438+
439+/*!
440+ \internal
441+
442+ Sorts the existing mappings.
443+*/
444+void QSortFilterProxyModelPrivate::sort()
445+{
446+ Q_Q(QSortFilterProxyModel);
447+ emit q->layoutAboutToBeChanged(QList<QPersistentModelIndex>(), QAbstractItemModel::VerticalSortHint);
448+ QModelIndexPairList source_indexes = store_persistent_indexes();
449+ IndexMap::const_iterator it = source_index_mapping.constBegin();
450+ for (; it != source_index_mapping.constEnd(); ++it) {
451+ QModelIndex source_parent = it.key();
452+ Mapping *m = it.value();
453+ sort_source_rows(m->source_rows, source_parent);
454+ build_source_to_proxy_mapping(m->source_rows, m->proxy_rows);
455+ }
456+ update_persistent_indexes(source_indexes);
457+ emit q->layoutChanged(QList<QPersistentModelIndex>(), QAbstractItemModel::VerticalSortHint);
458+}
459+
460+/*!
461+ \internal
462+
463+ update the source_sort_column according to the proxy_sort_column
464+ return true if the column was changed
465+*/
466+bool QSortFilterProxyModelPrivate::update_source_sort_column()
467+{
468+ Q_Q(QSortFilterProxyModel);
469+ QModelIndex proxy_index = q->index(0, proxy_sort_column, QModelIndex());
470+ int old_source_sort_column = source_sort_column;
471+ source_sort_column = q->mapToSource(proxy_index).column();
472+ return old_source_sort_column != source_sort_column;
473+}
474+
475+
476+/*!
477+ \internal
478+
479+ Sorts the given \a source_rows according to current sort column and order.
480+*/
481+void QSortFilterProxyModelPrivate::sort_source_rows(
482+ QVector<int> &source_rows, const QModelIndex &source_parent) const
483+{
484+ Q_Q(const QSortFilterProxyModel);
485+ if (source_sort_column >= 0) {
486+ if (sort_order == Qt::AscendingOrder) {
487+ QSortFilterProxyModelLessThan lt(source_sort_column, source_parent, model, q);
488+ std::stable_sort(source_rows.begin(), source_rows.end(), lt);
489+ } else {
490+ QSortFilterProxyModelGreaterThan gt(source_sort_column, source_parent, model, q);
491+ std::stable_sort(source_rows.begin(), source_rows.end(), gt);
492+ }
493+ } else { // restore the source model order
494+ std::stable_sort(source_rows.begin(), source_rows.end());
495+ }
496+}
497+
498+/*!
499+ \internal
500+
501+ Given source-to-proxy mapping \a source_to_proxy and the set of
502+ source items \a source_items (which are part of that mapping),
503+ determines the corresponding proxy item intervals that should
504+ be removed from the proxy model.
505+
506+ The result is a vector of pairs, where each pair represents a
507+ (start, end) tuple, sorted in ascending order.
508+*/
509+QVector<QPair<int, int > > QSortFilterProxyModelPrivate::proxy_intervals_for_source_items(
510+ const QVector<int> &source_to_proxy, const QVector<int> &source_items) const
511+{
512+ QVector<QPair<int, int> > proxy_intervals;
513+ if (source_items.isEmpty())
514+ return proxy_intervals;
515+
516+ int source_items_index = 0;
517+ while (source_items_index < source_items.size()) {
518+ int first_proxy_item = source_to_proxy.at(source_items.at(source_items_index));
519+ Q_ASSERT(first_proxy_item != -1);
520+ int last_proxy_item = first_proxy_item;
521+ ++source_items_index;
522+ // Find end of interval
523+ while ((source_items_index < source_items.size())
524+ && (source_to_proxy.at(source_items.at(source_items_index)) == last_proxy_item + 1)) {
525+ ++last_proxy_item;
526+ ++source_items_index;
527+ }
528+ // Add interval to result
529+ proxy_intervals.append(QPair<int, int>(first_proxy_item, last_proxy_item));
530+ }
531+ std::stable_sort(proxy_intervals.begin(), proxy_intervals.end());
532+ return proxy_intervals;
533+}
534+
535+/*!
536+ \internal
537+
538+ Given source-to-proxy mapping \a src_to_proxy and proxy-to-source mapping
539+ \a proxy_to_source, removes \a source_items from this proxy model.
540+ The corresponding proxy items are removed in intervals, so that the proper
541+ rows/columnsRemoved(start, end) signals will be generated.
542+*/
543+void QSortFilterProxyModelPrivate::remove_source_items(
544+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
545+ const QVector<int> &source_items, const QModelIndex &source_parent,
546+ Qt::Orientation orient, bool emit_signal)
547+{
548+ Q_Q(QSortFilterProxyModel);
549+ QModelIndex proxy_parent = q->mapFromSource(source_parent);
550+ if (!proxy_parent.isValid() && source_parent.isValid())
551+ return; // nothing to do (already removed)
552+
553+ QVector<QPair<int, int> > proxy_intervals;
554+ proxy_intervals = proxy_intervals_for_source_items(source_to_proxy, source_items);
555+
556+ for (int i = proxy_intervals.size()-1; i >= 0; --i) {
557+ QPair<int, int> interval = proxy_intervals.at(i);
558+ int proxy_start = interval.first;
559+ int proxy_end = interval.second;
560+ remove_proxy_interval(source_to_proxy, proxy_to_source, proxy_start, proxy_end,
561+ proxy_parent, orient, emit_signal);
562+ }
563+}
564+
565+/*!
566+ \internal
567+
568+ Given source-to-proxy mapping \a source_to_proxy and proxy-to-source mapping
569+ \a proxy_to_source, removes items from \a proxy_start to \a proxy_end
570+ (inclusive) from this proxy model.
571+*/
572+void QSortFilterProxyModelPrivate::remove_proxy_interval(
573+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source, int proxy_start, int proxy_end,
574+ const QModelIndex &proxy_parent, Qt::Orientation orient, bool emit_signal)
575+{
576+ Q_Q(QSortFilterProxyModel);
577+ if (emit_signal) {
578+ if (orient == Qt::Vertical)
579+ q->beginRemoveRows(proxy_parent, proxy_start, proxy_end);
580+ else
581+ q->beginRemoveColumns(proxy_parent, proxy_start, proxy_end);
582+ }
583+
584+ // Remove items from proxy-to-source mapping
585+ proxy_to_source.remove(proxy_start, proxy_end - proxy_start + 1);
586+
587+ build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
588+
589+ if (emit_signal) {
590+ if (orient == Qt::Vertical)
591+ q->endRemoveRows();
592+ else
593+ q->endRemoveColumns();
594+ }
595+}
596+
597+/*!
598+ \internal
599+
600+ Given proxy-to-source mapping \a proxy_to_source and a set of
601+ unmapped source items \a source_items, determines the proxy item
602+ intervals at which the subsets of source items should be inserted
603+ (but does not actually add them to the mapping).
604+
605+ The result is a vector of pairs, each pair representing a tuple (start,
606+ items), where items is a vector containing the (sorted) source items that
607+ should be inserted at that proxy model location.
608+*/
609+QVector<QPair<int, QVector<int > > > QSortFilterProxyModelPrivate::proxy_intervals_for_source_items_to_add(
610+ const QVector<int> &proxy_to_source, const QVector<int> &source_items,
611+ const QModelIndex &source_parent, Qt::Orientation orient) const
612+{
613+ Q_Q(const QSortFilterProxyModel);
614+ QVector<QPair<int, QVector<int> > > proxy_intervals;
615+ if (source_items.isEmpty())
616+ return proxy_intervals;
617+
618+ int proxy_low = 0;
619+ int proxy_item = 0;
620+ int source_items_index = 0;
621+ QVector<int> source_items_in_interval;
622+ bool compare = (orient == Qt::Vertical && source_sort_column >= 0 && dynamic_sortfilter);
623+ while (source_items_index < source_items.size()) {
624+ source_items_in_interval.clear();
625+ int first_new_source_item = source_items.at(source_items_index);
626+ source_items_in_interval.append(first_new_source_item);
627+ ++source_items_index;
628+
629+ // Find proxy item at which insertion should be started
630+ int proxy_high = proxy_to_source.size() - 1;
631+ QModelIndex i1 = compare ? model->index(first_new_source_item, source_sort_column, source_parent) : QModelIndex();
632+ while (proxy_low <= proxy_high) {
633+ proxy_item = (proxy_low + proxy_high) / 2;
634+ if (compare) {
635+ QModelIndex i2 = model->index(proxy_to_source.at(proxy_item), source_sort_column, source_parent);
636+ if ((sort_order == Qt::AscendingOrder) ? q->lessThan(i1, i2) : q->lessThan(i2, i1))
637+ proxy_high = proxy_item - 1;
638+ else
639+ proxy_low = proxy_item + 1;
640+ } else {
641+ if (first_new_source_item < proxy_to_source.at(proxy_item))
642+ proxy_high = proxy_item - 1;
643+ else
644+ proxy_low = proxy_item + 1;
645+ }
646+ }
647+ proxy_item = proxy_low;
648+
649+ // Find the sequence of new source items that should be inserted here
650+ if (proxy_item >= proxy_to_source.size()) {
651+ for ( ; source_items_index < source_items.size(); ++source_items_index)
652+ source_items_in_interval.append(source_items.at(source_items_index));
653+ } else {
654+ i1 = compare ? model->index(proxy_to_source.at(proxy_item), source_sort_column, source_parent) : QModelIndex();
655+ for ( ; source_items_index < source_items.size(); ++source_items_index) {
656+ int new_source_item = source_items.at(source_items_index);
657+ if (compare) {
658+ QModelIndex i2 = model->index(new_source_item, source_sort_column, source_parent);
659+ if ((sort_order == Qt::AscendingOrder) ? q->lessThan(i1, i2) : q->lessThan(i2, i1))
660+ break;
661+ } else {
662+ if (proxy_to_source.at(proxy_item) < new_source_item)
663+ break;
664+ }
665+ source_items_in_interval.append(new_source_item);
666+ }
667+ }
668+
669+ // Add interval to result
670+ proxy_intervals.append(QPair<int, QVector<int> >(proxy_item, source_items_in_interval));
671+ }
672+ return proxy_intervals;
673+}
674+
675+/*!
676+ \internal
677+
678+ Given source-to-proxy mapping \a source_to_proxy and proxy-to-source mapping
679+ \a proxy_to_source, inserts the given \a source_items into this proxy model.
680+ The source items are inserted in intervals (based on some sorted order), so
681+ that the proper rows/columnsInserted(start, end) signals will be generated.
682+*/
683+void QSortFilterProxyModelPrivate::insert_source_items(
684+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
685+ const QVector<int> &source_items, const QModelIndex &source_parent,
686+ Qt::Orientation orient, bool emit_signal)
687+{
688+ Q_Q(QSortFilterProxyModel);
689+ QModelIndex proxy_parent = q->mapFromSource(source_parent);
690+ if (!proxy_parent.isValid() && source_parent.isValid())
691+ return; // nothing to do (source_parent is not mapped)
692+
693+ QVector<QPair<int, QVector<int> > > proxy_intervals;
694+ proxy_intervals = proxy_intervals_for_source_items_to_add(
695+ proxy_to_source, source_items, source_parent, orient);
696+
697+ for (int i = proxy_intervals.size()-1; i >= 0; --i) {
698+ QPair<int, QVector<int> > interval = proxy_intervals.at(i);
699+ int proxy_start = interval.first;
700+ QVector<int> source_items = interval.second;
701+ int proxy_end = proxy_start + source_items.size() - 1;
702+
703+ if (emit_signal) {
704+ if (orient == Qt::Vertical)
705+ q->beginInsertRows(proxy_parent, proxy_start, proxy_end);
706+ else
707+ q->beginInsertColumns(proxy_parent, proxy_start, proxy_end);
708+ }
709+
710+ for (int i = 0; i < source_items.size(); ++i)
711+ proxy_to_source.insert(proxy_start + i, source_items.at(i));
712+
713+ build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
714+
715+ if (emit_signal) {
716+ if (orient == Qt::Vertical)
717+ q->endInsertRows();
718+ else
719+ q->endInsertColumns();
720+ }
721+ }
722+}
723+
724+/*!
725+ \internal
726+
727+ Handles source model items insertion (columnsInserted(), rowsInserted()).
728+ Determines
729+ 1) which of the inserted items to also insert into proxy model (filtering),
730+ 2) where to insert the items into the proxy model (sorting),
731+ then inserts those items.
732+ The items are inserted into the proxy model in intervals (based on
733+ sorted order), so that the proper rows/columnsInserted(start, end)
734+ signals will be generated.
735+*/
736+void QSortFilterProxyModelPrivate::source_items_inserted(
737+ const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
738+{
739+ Q_Q(QSortFilterProxyModel);
740+ if ((start < 0) || (end < 0))
741+ return;
742+ IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
743+ if (it == source_index_mapping.constEnd()) {
744+ if (!can_create_mapping(source_parent))
745+ return;
746+ it = create_mapping(source_parent);
747+ Mapping *m = it.value();
748+ QModelIndex proxy_parent = q->mapFromSource(source_parent);
749+ if (m->source_rows.count() > 0) {
750+ q->beginInsertRows(proxy_parent, 0, m->source_rows.count() - 1);
751+ q->endInsertRows();
752+ }
753+ if (m->source_columns.count() > 0) {
754+ q->beginInsertColumns(proxy_parent, 0, m->source_columns.count() - 1);
755+ q->endInsertColumns();
756+ }
757+ return;
758+ }
759+
760+ Mapping *m = it.value();
761+ QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
762+ QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
763+
764+ int delta_item_count = end - start + 1;
765+ int old_item_count = source_to_proxy.size();
766+
767+ updateChildrenMapping(source_parent, m, orient, start, end, delta_item_count, false);
768+
769+ // Expand source-to-proxy mapping to account for new items
770+ if (start < 0 || start > source_to_proxy.size()) {
771+ qWarning("QSortFilterProxyModel: invalid inserted rows reported by source model");
772+ remove_from_mapping(source_parent);
773+ return;
774+ }
775+ source_to_proxy.insert(start, delta_item_count, -1);
776+
777+ if (start < old_item_count) {
778+ // Adjust existing "stale" indexes in proxy-to-source mapping
779+ int proxy_count = proxy_to_source.size();
780+ for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
781+ int source_item = proxy_to_source.at(proxy_item);
782+ if (source_item >= start)
783+ proxy_to_source.replace(proxy_item, source_item + delta_item_count);
784+ }
785+ build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
786+ }
787+
788+ // Figure out which items to add to mapping based on filter
789+ QVector<int> source_items;
790+ for (int i = start; i <= end; ++i) {
791+ if ((orient == Qt::Vertical)
792+ ? q->filterAcceptsRow(i, source_parent)
793+ : q->filterAcceptsColumn(i, source_parent)) {
794+ source_items.append(i);
795+ }
796+ }
797+
798+ if (model->rowCount(source_parent) == delta_item_count) {
799+ // Items were inserted where there were none before.
800+ // If it was new rows make sure to create mappings for columns so that a
801+ // valid mapping can be retrieved later and vice-versa.
802+
803+ QVector<int> &orthogonal_proxy_to_source = (orient == Qt::Horizontal) ? m->source_rows : m->source_columns;
804+ QVector<int> &orthogonal_source_to_proxy = (orient == Qt::Horizontal) ? m->proxy_rows : m->proxy_columns;
805+
806+ if (orthogonal_source_to_proxy.isEmpty()) {
807+ const int ortho_end = (orient == Qt::Horizontal) ? model->rowCount(source_parent) : model->columnCount(source_parent);
808+
809+ orthogonal_source_to_proxy.resize(ortho_end);
810+
811+ for (int ortho_item = 0; ortho_item < ortho_end; ++ortho_item) {
812+ if ((orient == Qt::Horizontal) ? q->filterAcceptsRow(ortho_item, source_parent)
813+ : q->filterAcceptsColumn(ortho_item, source_parent)) {
814+ orthogonal_proxy_to_source.append(ortho_item);
815+ }
816+ }
817+ if (orient == Qt::Horizontal) {
818+ // We're reacting to columnsInserted, but we've just inserted new rows. Sort them.
819+ sort_source_rows(orthogonal_proxy_to_source, source_parent);
820+ }
821+ build_source_to_proxy_mapping(orthogonal_proxy_to_source, orthogonal_source_to_proxy);
822+ }
823+ }
824+
825+ // Sort and insert the items
826+ if (orient == Qt::Vertical) // Only sort rows
827+ sort_source_rows(source_items, source_parent);
828+ insert_source_items(source_to_proxy, proxy_to_source, source_items, source_parent, orient);
829+}
830+
831+/*!
832+ \internal
833+
834+ Handles source model items removal
835+ (columnsAboutToBeRemoved(), rowsAboutToBeRemoved()).
836+*/
837+void QSortFilterProxyModelPrivate::source_items_about_to_be_removed(
838+ const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
839+{
840+ if ((start < 0) || (end < 0))
841+ return;
842+ IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
843+ if (it == source_index_mapping.constEnd()) {
844+ // Don't care, since we don't have mapping for this index
845+ return;
846+ }
847+
848+ Mapping *m = it.value();
849+ QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
850+ QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
851+
852+ // figure out which items to remove
853+ QVector<int> source_items_to_remove;
854+ int proxy_count = proxy_to_source.size();
855+ for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
856+ int source_item = proxy_to_source.at(proxy_item);
857+ if ((source_item >= start) && (source_item <= end))
858+ source_items_to_remove.append(source_item);
859+ }
860+
861+ remove_source_items(source_to_proxy, proxy_to_source, source_items_to_remove,
862+ source_parent, orient);
863+}
864+
865+/*!
866+ \internal
867+
868+ Handles source model items removal (columnsRemoved(), rowsRemoved()).
869+*/
870+void QSortFilterProxyModelPrivate::source_items_removed(
871+ const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
872+{
873+ if ((start < 0) || (end < 0))
874+ return;
875+ IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
876+ if (it == source_index_mapping.constEnd()) {
877+ // Don't care, since we don't have mapping for this index
878+ return;
879+ }
880+
881+ Mapping *m = it.value();
882+ QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
883+ QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
884+
885+ if (end >= source_to_proxy.size())
886+ end = source_to_proxy.size() - 1;
887+
888+ // Shrink the source-to-proxy mapping to reflect the new item count
889+ int delta_item_count = end - start + 1;
890+ source_to_proxy.remove(start, delta_item_count);
891+
892+ int proxy_count = proxy_to_source.size();
893+ if (proxy_count > source_to_proxy.size()) {
894+ // mapping is in an inconsistent state -- redo the whole mapping
895+ qWarning("QSortFilterProxyModel: inconsistent changes reported by source model");
896+ Q_Q(QSortFilterProxyModel);
897+ q->beginResetModel();
898+ remove_from_mapping(source_parent);
899+ q->endResetModel();
900+ return;
901+ }
902+
903+ // Adjust "stale" indexes in proxy-to-source mapping
904+ for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
905+ int source_item = proxy_to_source.at(proxy_item);
906+ if (source_item >= start) {
907+ Q_ASSERT(source_item - delta_item_count >= 0);
908+ proxy_to_source.replace(proxy_item, source_item - delta_item_count);
909+ }
910+ }
911+ build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
912+
913+ updateChildrenMapping(source_parent, m, orient, start, end, delta_item_count, true);
914+
915+}
916+
917+
918+/*!
919+ \internal
920+ updates the mapping of the children when inserting or removing items
921+*/
922+void QSortFilterProxyModelPrivate::updateChildrenMapping(const QModelIndex &source_parent, Mapping *parent_mapping,
923+ Qt::Orientation orient, int start, int end, int delta_item_count, bool remove)
924+{
925+ // see if any mapped children should be (re)moved
926+ QVector<QPair<QModelIndex, Mapping*> > moved_source_index_mappings;
927+ QVector<QModelIndex>::iterator it2 = parent_mapping->mapped_children.begin();
928+ for ( ; it2 != parent_mapping->mapped_children.end();) {
929+ const QModelIndex source_child_index = *it2;
930+ const int pos = (orient == Qt::Vertical)
931+ ? source_child_index.row()
932+ : source_child_index.column();
933+ if (pos < start) {
934+ // not affected
935+ ++it2;
936+ } else if (remove && pos <= end) {
937+ // in the removed interval
938+ it2 = parent_mapping->mapped_children.erase(it2);
939+ remove_from_mapping(source_child_index);
940+ } else {
941+ // below the removed items -- recompute the index
942+ QModelIndex new_index;
943+ const int newpos = remove ? pos - delta_item_count : pos + delta_item_count;
944+ if (orient == Qt::Vertical) {
945+ new_index = model->index(newpos,
946+ source_child_index.column(),
947+ source_parent);
948+ } else {
949+ new_index = model->index(source_child_index.row(),
950+ newpos,
951+ source_parent);
952+ }
953+ *it2 = new_index;
954+ ++it2;
955+
956+ // update mapping
957+ Mapping *cm = source_index_mapping.take(source_child_index);
958+ Q_ASSERT(cm);
959+ // we do not reinsert right away, because the new index might be identical with another, old index
960+ moved_source_index_mappings.append(QPair<QModelIndex, Mapping*>(new_index, cm));
961+ }
962+ }
963+
964+ // reinsert moved, mapped indexes
965+ QVector<QPair<QModelIndex, Mapping*> >::iterator it = moved_source_index_mappings.begin();
966+ for (; it != moved_source_index_mappings.end(); ++it) {
967+#ifdef QT_STRICT_ITERATORS
968+ source_index_mapping.insert((*it).first, (*it).second);
969+ (*it).second->map_iter = source_index_mapping.constFind((*it).first);
970+#else
971+ (*it).second->map_iter = source_index_mapping.insert((*it).first, (*it).second);
972+#endif
973+ }
974+}
975+
976+/*!
977+ \internal
978+*/
979+void QSortFilterProxyModelPrivate::proxy_item_range(
980+ const QVector<int> &source_to_proxy, const QVector<int> &source_items,
981+ int &proxy_low, int &proxy_high) const
982+{
983+ proxy_low = INT_MAX;
984+ proxy_high = INT_MIN;
985+ for (int i = 0; i < source_items.count(); ++i) {
986+ int proxy_item = source_to_proxy.at(source_items.at(i));
987+ Q_ASSERT(proxy_item != -1);
988+ if (proxy_item < proxy_low)
989+ proxy_low = proxy_item;
990+ if (proxy_item > proxy_high)
991+ proxy_high = proxy_item;
992+ }
993+}
994+
995+/*!
996+ \internal
997+*/
998+void QSortFilterProxyModelPrivate::build_source_to_proxy_mapping(
999+ const QVector<int> &proxy_to_source, QVector<int> &source_to_proxy) const
1000+{
1001+ source_to_proxy.fill(-1);
1002+ int proxy_count = proxy_to_source.size();
1003+ for (int i = 0; i < proxy_count; ++i)
1004+ source_to_proxy[proxy_to_source.at(i)] = i;
1005+}
1006+
1007+/*!
1008+ \internal
1009+
1010+ Maps the persistent proxy indexes to source indexes and
1011+ returns the list of source indexes.
1012+*/
1013+QModelIndexPairList QSortFilterProxyModelPrivate::store_persistent_indexes()
1014+{
1015+ Q_Q(QSortFilterProxyModel);
1016+ QModelIndexPairList source_indexes;
1017+ foreach (QPersistentModelIndexData *data, persistent.indexes) {
1018+ QModelIndex proxy_index = data->index;
1019+ QModelIndex source_index = q->mapToSource(proxy_index);
1020+ source_indexes.append(qMakePair(proxy_index, QPersistentModelIndex(source_index)));
1021+ }
1022+ return source_indexes;
1023+}
1024+
1025+/*!
1026+ \internal
1027+
1028+ Maps \a source_indexes to proxy indexes and stores those
1029+ as persistent indexes.
1030+*/
1031+void QSortFilterProxyModelPrivate::update_persistent_indexes(
1032+ const QModelIndexPairList &source_indexes)
1033+{
1034+ Q_Q(QSortFilterProxyModel);
1035+ QModelIndexList from, to;
1036+ for (int i = 0; i < source_indexes.count(); ++i) {
1037+ QModelIndex source_index = source_indexes.at(i).second;
1038+ QModelIndex old_proxy_index = source_indexes.at(i).first;
1039+ create_mapping(source_index.parent());
1040+ QModelIndex proxy_index = q->mapFromSource(source_index);
1041+ from << old_proxy_index;
1042+ to << proxy_index;
1043+ }
1044+ q->changePersistentIndexList(from, to);
1045+}
1046+
1047+
1048+/*!
1049+ \internal
1050+
1051+ Updates the proxy model (adds/removes rows) based on the
1052+ new filter.
1053+*/
1054+void QSortFilterProxyModelPrivate::filter_changed(const QModelIndex &source_parent)
1055+{
1056+ IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
1057+ if (it == source_index_mapping.constEnd())
1058+ return;
1059+ Mapping *m = it.value();
1060+ QSet<int> rows_removed = handle_filter_changed(m->proxy_rows, m->source_rows, source_parent, Qt::Vertical);
1061+ QSet<int> columns_removed = handle_filter_changed(m->proxy_columns, m->source_columns, source_parent, Qt::Horizontal);
1062+
1063+ // We need to iterate over a copy of m->mapped_children because otherwise it may be changed by other code, invalidating
1064+ // the iterator it2.
1065+ // The m->mapped_children vector can be appended to with indexes which are no longer filtered
1066+ // out (in create_mapping) when this function recurses for child indexes.
1067+ const QVector<QModelIndex> mappedChildren = m->mapped_children;
1068+ QVector<int> indexesToRemove;
1069+ for (int i = 0; i < mappedChildren.size(); ++i) {
1070+ const QModelIndex source_child_index = mappedChildren.at(i);
1071+ if (rows_removed.contains(source_child_index.row()) || columns_removed.contains(source_child_index.column())) {
1072+ indexesToRemove.push_back(i);
1073+ remove_from_mapping(source_child_index);
1074+ } else {
1075+ filter_changed(source_child_index);
1076+ }
1077+ }
1078+ QVector<int>::const_iterator removeIt = indexesToRemove.constEnd();
1079+ const QVector<int>::const_iterator removeBegin = indexesToRemove.constBegin();
1080+
1081+ // We can't just remove these items from mappedChildren while iterating above and then
1082+ // do something like m->mapped_children = mappedChildren, because mapped_children might
1083+ // be appended to in create_mapping, and we would lose those new items.
1084+ // Because they are always appended in create_mapping, we can still remove them by
1085+ // position here.
1086+ while (removeIt != removeBegin) {
1087+ --removeIt;
1088+ m->mapped_children.remove(*removeIt);
1089+ }
1090+}
1091+
1092+/*!
1093+ \internal
1094+ returns the removed items indexes
1095+*/
1096+QSet<int> QSortFilterProxyModelPrivate::handle_filter_changed(
1097+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
1098+ const QModelIndex &source_parent, Qt::Orientation orient)
1099+{
1100+ Q_Q(QSortFilterProxyModel);
1101+ // Figure out which mapped items to remove
1102+ QVector<int> source_items_remove;
1103+ for (int i = 0; i < proxy_to_source.count(); ++i) {
1104+ const int source_item = proxy_to_source.at(i);
1105+ if ((orient == Qt::Vertical)
1106+ ? !q->filterAcceptsRow(source_item, source_parent)
1107+ : !q->filterAcceptsColumn(source_item, source_parent)) {
1108+ // This source item does not satisfy the filter, so it must be removed
1109+ source_items_remove.append(source_item);
1110+ }
1111+ }
1112+ // Figure out which non-mapped items to insert
1113+ QVector<int> source_items_insert;
1114+ int source_count = source_to_proxy.size();
1115+ for (int source_item = 0; source_item < source_count; ++source_item) {
1116+ if (source_to_proxy.at(source_item) == -1) {
1117+ if ((orient == Qt::Vertical)
1118+ ? q->filterAcceptsRow(source_item, source_parent)
1119+ : q->filterAcceptsColumn(source_item, source_parent)) {
1120+ // This source item satisfies the filter, so it must be added
1121+ source_items_insert.append(source_item);
1122+ }
1123+ }
1124+ }
1125+ if (!source_items_remove.isEmpty() || !source_items_insert.isEmpty()) {
1126+ // Do item removal and insertion
1127+ remove_source_items(source_to_proxy, proxy_to_source,
1128+ source_items_remove, source_parent, orient);
1129+ if (orient == Qt::Vertical)
1130+ sort_source_rows(source_items_insert, source_parent);
1131+ insert_source_items(source_to_proxy, proxy_to_source,
1132+ source_items_insert, source_parent, orient);
1133+ }
1134+ return qVectorToSet(source_items_remove);
1135+}
1136+
1137+void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &source_top_left,
1138+ const QModelIndex &source_bottom_right)
1139+{
1140+ Q_Q(QSortFilterProxyModel);
1141+ if (!source_top_left.isValid() || !source_bottom_right.isValid())
1142+ return;
1143+ QModelIndex source_parent = source_top_left.parent();
1144+ IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
1145+ if (it == source_index_mapping.constEnd()) {
1146+ // Don't care, since we don't have mapping for this index
1147+ return;
1148+ }
1149+ Mapping *m = it.value();
1150+
1151+ // Figure out how the source changes affect us
1152+ QVector<int> source_rows_remove;
1153+ QVector<int> source_rows_insert;
1154+ QVector<int> source_rows_change;
1155+ QVector<int> source_rows_resort;
1156+ int end = qMin(source_bottom_right.row(), m->proxy_rows.count() - 1);
1157+ for (int source_row = source_top_left.row(); source_row <= end; ++source_row) {
1158+ if (dynamic_sortfilter) {
1159+ if (m->proxy_rows.at(source_row) != -1) {
1160+ if (!q->filterAcceptsRow(source_row, source_parent)) {
1161+ // This source row no longer satisfies the filter, so it must be removed
1162+ source_rows_remove.append(source_row);
1163+ } else if (source_sort_column >= source_top_left.column() && source_sort_column <= source_bottom_right.column()) {
1164+ // This source row has changed in a way that may affect sorted order
1165+ source_rows_resort.append(source_row);
1166+ } else {
1167+ // This row has simply changed, without affecting filtering nor sorting
1168+ source_rows_change.append(source_row);
1169+ }
1170+ } else {
1171+ if (!itemsBeingRemoved.contains(source_parent, source_row) && q->filterAcceptsRow(source_row, source_parent)) {
1172+ // This source row now satisfies the filter, so it must be added
1173+ source_rows_insert.append(source_row);
1174+ }
1175+ }
1176+ } else {
1177+ if (m->proxy_rows.at(source_row) != -1)
1178+ source_rows_change.append(source_row);
1179+ }
1180+ }
1181+
1182+ if (!source_rows_remove.isEmpty()) {
1183+ remove_source_items(m->proxy_rows, m->source_rows,
1184+ source_rows_remove, source_parent, Qt::Vertical);
1185+ QSet<int> source_rows_remove_set = qVectorToSet(source_rows_remove);
1186+ QVector<QModelIndex>::iterator childIt = m->mapped_children.end();
1187+ while (childIt != m->mapped_children.begin()) {
1188+ --childIt;
1189+ const QModelIndex source_child_index = *childIt;
1190+ if (source_rows_remove_set.contains(source_child_index.row())) {
1191+ childIt = m->mapped_children.erase(childIt);
1192+ remove_from_mapping(source_child_index);
1193+ }
1194+ }
1195+ }
1196+
1197+ if (!source_rows_resort.isEmpty()) {
1198+ // Re-sort the rows of this level
1199+ QList<QPersistentModelIndex> parents;
1200+ parents << q->mapFromSource(source_parent);
1201+ emit q->layoutAboutToBeChanged(parents, QAbstractItemModel::VerticalSortHint);
1202+ QModelIndexPairList source_indexes = store_persistent_indexes();
1203+ remove_source_items(m->proxy_rows, m->source_rows, source_rows_resort,
1204+ source_parent, Qt::Vertical, false);
1205+ sort_source_rows(source_rows_resort, source_parent);
1206+ insert_source_items(m->proxy_rows, m->source_rows, source_rows_resort,
1207+ source_parent, Qt::Vertical, false);
1208+ update_persistent_indexes(source_indexes);
1209+ emit q->layoutChanged(parents, QAbstractItemModel::VerticalSortHint);
1210+ // Make sure we also emit dataChanged for the rows
1211+ source_rows_change += source_rows_resort;
1212+ }
1213+
1214+ if (!source_rows_change.isEmpty()) {
1215+ // Find the proxy row range
1216+ int proxy_start_row;
1217+ int proxy_end_row;
1218+ proxy_item_range(m->proxy_rows, source_rows_change,
1219+ proxy_start_row, proxy_end_row);
1220+ // ### Find the proxy column range also
1221+ if (proxy_end_row >= 0) {
1222+ // the row was accepted, but some columns might still be filtered out
1223+ int source_left_column = source_top_left.column();
1224+ while (source_left_column < source_bottom_right.column()
1225+ && m->proxy_columns.at(source_left_column) == -1)
1226+ ++source_left_column;
1227+ const QModelIndex proxy_top_left = create_index(
1228+ proxy_start_row, m->proxy_columns.at(source_left_column), it);
1229+ int source_right_column = source_bottom_right.column();
1230+ while (source_right_column > source_top_left.column()
1231+ && m->proxy_columns.at(source_right_column) == -1)
1232+ --source_right_column;
1233+ const QModelIndex proxy_bottom_right = create_index(
1234+ proxy_end_row, m->proxy_columns.at(source_right_column), it);
1235+ emit q->dataChanged(proxy_top_left, proxy_bottom_right);
1236+ }
1237+ }
1238+
1239+ if (!source_rows_insert.isEmpty()) {
1240+ sort_source_rows(source_rows_insert, source_parent);
1241+ insert_source_items(m->proxy_rows, m->source_rows,
1242+ source_rows_insert, source_parent, Qt::Vertical);
1243+ }
1244+}
1245+
1246+void QSortFilterProxyModelPrivate::_q_sourceHeaderDataChanged(Qt::Orientation orientation,
1247+ int start, int end)
1248+{
1249+ Q_ASSERT(start <= end);
1250+
1251+ Q_Q(QSortFilterProxyModel);
1252+ Mapping *m = create_mapping(QModelIndex()).value();
1253+
1254+ const QVector<int> &source_to_proxy = (orientation == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
1255+
1256+ QVector<int> proxy_positions;
1257+ proxy_positions.reserve(end - start + 1);
1258+ {
1259+ Q_ASSERT(source_to_proxy.size() > end);
1260+ QVector<int>::const_iterator it = source_to_proxy.constBegin() + start;
1261+ const QVector<int>::const_iterator endIt = source_to_proxy.constBegin() + end + 1;
1262+ for ( ; it != endIt; ++it) {
1263+ if (*it != -1)
1264+ proxy_positions.push_back(*it);
1265+ }
1266+ }
1267+
1268+ std::sort(proxy_positions.begin(), proxy_positions.end());
1269+
1270+ int last_index = 0;
1271+ const int numItems = proxy_positions.size();
1272+ while (last_index < numItems) {
1273+ const int proxyStart = proxy_positions.at(last_index);
1274+ int proxyEnd = proxyStart;
1275+ ++last_index;
1276+ for (int i = last_index; i < numItems; ++i) {
1277+ if (proxy_positions.at(i) == proxyEnd + 1) {
1278+ ++last_index;
1279+ ++proxyEnd;
1280+ } else {
1281+ break;
1282+ }
1283+ }
1284+ emit q->headerDataChanged(orientation, proxyStart, proxyEnd);
1285+ }
1286+}
1287+
1288+void QSortFilterProxyModelPrivate::_q_sourceAboutToBeReset()
1289+{
1290+ Q_Q(QSortFilterProxyModel);
1291+ q->beginResetModel();
1292+}
1293+
1294+void QSortFilterProxyModelPrivate::_q_sourceReset()
1295+{
1296+ Q_Q(QSortFilterProxyModel);
1297+ invalidatePersistentIndexes();
1298+ _q_clearMapping();
1299+ // All internal structures are deleted in clear()
1300+ q->endResetModel();
1301+ update_source_sort_column();
1302+ if (dynamic_sortfilter)
1303+ sort();
1304+}
1305+
1306+void QSortFilterProxyModelPrivate::_q_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
1307+{
1308+ Q_Q(QSortFilterProxyModel);
1309+ saved_persistent_indexes.clear();
1310+
1311+ QList<QPersistentModelIndex> parents;
1312+ foreach (const QPersistentModelIndex &parent, sourceParents) {
1313+ if (!parent.isValid()) {
1314+ parents << QPersistentModelIndex();
1315+ continue;
1316+ }
1317+ const QModelIndex mappedParent = q->mapFromSource(parent);
1318+ // Might be filtered out.
1319+ if (mappedParent.isValid())
1320+ parents << mappedParent;
1321+ }
1322+
1323+ // All parents filtered out.
1324+ if (!sourceParents.isEmpty() && parents.isEmpty())
1325+ return;
1326+
1327+ emit q->layoutAboutToBeChanged(parents, hint);
1328+ if (persistent.indexes.isEmpty())
1329+ return;
1330+
1331+ saved_persistent_indexes = store_persistent_indexes();
1332+}
1333+
1334+void QSortFilterProxyModelPrivate::_q_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
1335+{
1336+ Q_Q(QSortFilterProxyModel);
1337+
1338+ // Optimize: We only actually have to clear the mapping related to the contents of
1339+ // sourceParents, not everything.
1340+ qDeleteAll(source_index_mapping);
1341+ source_index_mapping.clear();
1342+
1343+ update_persistent_indexes(saved_persistent_indexes);
1344+ saved_persistent_indexes.clear();
1345+
1346+ if (dynamic_sortfilter && update_source_sort_column()) {
1347+ //update_source_sort_column might have created wrong mapping so we have to clear it again
1348+ qDeleteAll(source_index_mapping);
1349+ source_index_mapping.clear();
1350+ }
1351+
1352+ QList<QPersistentModelIndex> parents;
1353+ foreach (const QPersistentModelIndex &parent, sourceParents) {
1354+ if (!parent.isValid()) {
1355+ parents << QPersistentModelIndex();
1356+ continue;
1357+ }
1358+ const QModelIndex mappedParent = q->mapFromSource(parent);
1359+ if (mappedParent.isValid())
1360+ parents << mappedParent;
1361+ }
1362+
1363+ if (!sourceParents.isEmpty() && parents.isEmpty())
1364+ return;
1365+
1366+ emit q->layoutChanged(parents, hint);
1367+}
1368+
1369+void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeInserted(
1370+ const QModelIndex &source_parent, int start, int end)
1371+{
1372+ Q_UNUSED(start);
1373+ Q_UNUSED(end);
1374+ //Force the creation of a mapping now, even if its empty.
1375+ //We need it because the proxy can be acessed at the moment it emits rowsAboutToBeInserted in insert_source_items
1376+ if (can_create_mapping(source_parent))
1377+ create_mapping(source_parent);
1378+}
1379+
1380+void QSortFilterProxyModelPrivate::_q_sourceRowsInserted(
1381+ const QModelIndex &source_parent, int start, int end)
1382+{
1383+ source_items_inserted(source_parent, start, end, Qt::Vertical);
1384+ if (update_source_sort_column() && dynamic_sortfilter) //previous call to update_source_sort_column may fail if the model has no column.
1385+ sort(); // now it should succeed so we need to make sure to sort again
1386+}
1387+
1388+void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeRemoved(
1389+ const QModelIndex &source_parent, int start, int end)
1390+{
1391+ itemsBeingRemoved = QRowsRemoval(source_parent, start, end);
1392+ source_items_about_to_be_removed(source_parent, start, end,
1393+ Qt::Vertical);
1394+}
1395+
1396+void QSortFilterProxyModelPrivate::_q_sourceRowsRemoved(
1397+ const QModelIndex &source_parent, int start, int end)
1398+{
1399+ itemsBeingRemoved = QRowsRemoval();
1400+ source_items_removed(source_parent, start, end, Qt::Vertical);
1401+}
1402+
1403+void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeMoved(
1404+ const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1405+{
1406+ Q_Q(QSortFilterProxyModel);
1407+ // Because rows which are contiguous in the source model might not be contiguous
1408+ // in the proxy due to sorting, the best thing we can do here is be specific about what
1409+ // parents are having their children changed.
1410+ // Optimize: Emit move signals if the proxy is not sorted. Will need to account for rows
1411+ // being filtered out though.
1412+
1413+ saved_persistent_indexes.clear();
1414+
1415+ QList<QPersistentModelIndex> parents;
1416+ parents << q->mapFromSource(sourceParent);
1417+ if (sourceParent != destParent)
1418+ parents << q->mapFromSource(destParent);
1419+ emit q->layoutAboutToBeChanged(parents);
1420+ if (persistent.indexes.isEmpty())
1421+ return;
1422+ saved_persistent_indexes = store_persistent_indexes();
1423+}
1424+
1425+void QSortFilterProxyModelPrivate::_q_sourceRowsMoved(
1426+ const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1427+{
1428+ Q_Q(QSortFilterProxyModel);
1429+
1430+ // Optimize: We only need to clear and update the persistent indexes which are children of
1431+ // sourceParent or destParent
1432+ qDeleteAll(source_index_mapping);
1433+ source_index_mapping.clear();
1434+
1435+ update_persistent_indexes(saved_persistent_indexes);
1436+ saved_persistent_indexes.clear();
1437+
1438+ if (dynamic_sortfilter && update_source_sort_column()) {
1439+ //update_source_sort_column might have created wrong mapping so we have to clear it again
1440+ qDeleteAll(source_index_mapping);
1441+ source_index_mapping.clear();
1442+ }
1443+
1444+ QList<QPersistentModelIndex> parents;
1445+ parents << q->mapFromSource(sourceParent);
1446+ if (sourceParent != destParent)
1447+ parents << q->mapFromSource(destParent);
1448+ emit q->layoutChanged(parents);
1449+}
1450+
1451+void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeInserted(
1452+ const QModelIndex &source_parent, int start, int end)
1453+{
1454+ Q_UNUSED(start);
1455+ Q_UNUSED(end);
1456+ //Force the creation of a mapping now, even if its empty.
1457+ //We need it because the proxy can be acessed at the moment it emits columnsAboutToBeInserted in insert_source_items
1458+ if (can_create_mapping(source_parent))
1459+ create_mapping(source_parent);
1460+}
1461+
1462+void QSortFilterProxyModelPrivate::_q_sourceColumnsInserted(
1463+ const QModelIndex &source_parent, int start, int end)
1464+{
1465+ Q_Q(const QSortFilterProxyModel);
1466+ source_items_inserted(source_parent, start, end, Qt::Horizontal);
1467+
1468+ if (source_parent.isValid())
1469+ return; //we sort according to the root column only
1470+ if (source_sort_column == -1) {
1471+ //we update the source_sort_column depending on the proxy_sort_column
1472+ if (update_source_sort_column() && dynamic_sortfilter)
1473+ sort();
1474+ } else {
1475+ if (start <= source_sort_column)
1476+ source_sort_column += end - start + 1;
1477+
1478+ proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column();
1479+ }
1480+}
1481+
1482+void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeRemoved(
1483+ const QModelIndex &source_parent, int start, int end)
1484+{
1485+ source_items_about_to_be_removed(source_parent, start, end,
1486+ Qt::Horizontal);
1487+}
1488+
1489+void QSortFilterProxyModelPrivate::_q_sourceColumnsRemoved(
1490+ const QModelIndex &source_parent, int start, int end)
1491+{
1492+ Q_Q(const QSortFilterProxyModel);
1493+ source_items_removed(source_parent, start, end, Qt::Horizontal);
1494+
1495+ if (source_parent.isValid())
1496+ return; //we sort according to the root column only
1497+ if (start <= source_sort_column) {
1498+ if (end < source_sort_column)
1499+ source_sort_column -= end - start + 1;
1500+ else
1501+ source_sort_column = -1;
1502+ }
1503+
1504+ proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column();
1505+}
1506+
1507+void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeMoved(
1508+ const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1509+{
1510+ Q_Q(QSortFilterProxyModel);
1511+
1512+ saved_persistent_indexes.clear();
1513+
1514+ QList<QPersistentModelIndex> parents;
1515+ parents << q->mapFromSource(sourceParent);
1516+ if (sourceParent != destParent)
1517+ parents << q->mapFromSource(destParent);
1518+ emit q->layoutAboutToBeChanged(parents);
1519+
1520+ if (persistent.indexes.isEmpty())
1521+ return;
1522+ saved_persistent_indexes = store_persistent_indexes();
1523+}
1524+
1525+void QSortFilterProxyModelPrivate::_q_sourceColumnsMoved(
1526+ const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1527+{
1528+ Q_Q(QSortFilterProxyModel);
1529+
1530+ qDeleteAll(source_index_mapping);
1531+ source_index_mapping.clear();
1532+
1533+ update_persistent_indexes(saved_persistent_indexes);
1534+ saved_persistent_indexes.clear();
1535+
1536+ if (dynamic_sortfilter && update_source_sort_column()) {
1537+ qDeleteAll(source_index_mapping);
1538+ source_index_mapping.clear();
1539+ }
1540+
1541+ QList<QPersistentModelIndex> parents;
1542+ parents << q->mapFromSource(sourceParent);
1543+ if (sourceParent != destParent)
1544+ parents << q->mapFromSource(destParent);
1545+ emit q->layoutChanged(parents);
1546+}
1547+
1548+/*!
1549+ \since 4.1
1550+ \class QSortFilterProxyModel
1551+ \inmodule QtCore
1552+ \brief The QSortFilterProxyModel class provides support for sorting and
1553+ filtering data passed between another model and a view.
1554+
1555+ \ingroup model-view
1556+
1557+ QSortFilterProxyModel can be used for sorting items, filtering out items,
1558+ or both. The model transforms the structure of a source model by mapping
1559+ the model indexes it supplies to new indexes, corresponding to different
1560+ locations, for views to use. This approach allows a given source model to
1561+ be restructured as far as views are concerned without requiring any
1562+ transformations on the underlying data, and without duplicating the data in
1563+ memory.
1564+
1565+ Let's assume that we want to sort and filter the items provided by a custom
1566+ model. The code to set up the model and the view, \e without sorting and
1567+ filtering, would look like this:
1568+
1569+ \snippet qsortfilterproxymodel-details/main.cpp 1
1570+
1571+ To add sorting and filtering support to \c MyItemModel, we need to create
1572+ a QSortFilterProxyModel, call setSourceModel() with the \c MyItemModel as
1573+ argument, and install the QSortFilterProxyModel on the view:
1574+
1575+ \snippet qsortfilterproxymodel-details/main.cpp 0
1576+ \snippet qsortfilterproxymodel-details/main.cpp 2
1577+
1578+ At this point, neither sorting nor filtering is enabled; the original data
1579+ is displayed in the view. Any changes made through the
1580+ QSortFilterProxyModel are applied to the original model.
1581+
1582+ The QSortFilterProxyModel acts as a wrapper for the original model. If you
1583+ need to convert source \l{QModelIndex}es to sorted/filtered model indexes
1584+ or vice versa, use mapToSource(), mapFromSource(), mapSelectionToSource(),
1585+ and mapSelectionFromSource().
1586+
1587+ \note By default, the model dynamically re-sorts and re-filters data
1588+ whenever the original model changes. This behavior can be changed by
1589+ setting the \l{QSortFilterProxyModel::dynamicSortFilter}{dynamicSortFilter}
1590+ property.
1591+
1592+ The \l{itemviews/basicsortfiltermodel}{Basic Sort/Filter Model} and
1593+ \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model} examples
1594+ illustrate how to use QSortFilterProxyModel to perform basic sorting and
1595+ filtering and how to subclass it to implement custom behavior.
1596+
1597+ \section1 Sorting
1598+
1599+ QTableView and QTreeView have a
1600+ \l{QTreeView::sortingEnabled}{sortingEnabled} property that controls
1601+ whether the user can sort the view by clicking the view's horizontal
1602+ header. For example:
1603+
1604+ \snippet qsortfilterproxymodel-details/main.cpp 3
1605+
1606+ When this feature is on (the default is off), clicking on a header section
1607+ sorts the items according to that column. By clicking repeatedly, the user
1608+ can alternate between ascending and descending order.
1609+
1610+ \image qsortfilterproxymodel-sorting.png A sorted QTreeView
1611+
1612+ Behind the scene, the view calls the sort() virtual function on the model
1613+ to reorder the data in the model. To make your data sortable, you can
1614+ either implement sort() in your model, or use a QSortFilterProxyModel to
1615+ wrap your model -- QSortFilterProxyModel provides a generic sort()
1616+ reimplementation that operates on the sortRole() (Qt::DisplayRole by
1617+ default) of the items and that understands several data types, including
1618+ \c int, QString, and QDateTime. For hierarchical models, sorting is applied
1619+ recursively to all child items. String comparisons are case sensitive by
1620+ default; this can be changed by setting the \l{QSortFilterProxyModel::}
1621+ {sortCaseSensitivity} property.
1622+
1623+ Custom sorting behavior is achieved by subclassing
1624+ QSortFilterProxyModel and reimplementing lessThan(), which is
1625+ used to compare items. For example:
1626+
1627+ \snippet ../widgets/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 5
1628+
1629+ (This code snippet comes from the
1630+ \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model}
1631+ example.)
1632+
1633+ An alternative approach to sorting is to disable sorting on the view and to
1634+ impose a certain order to the user. This is done by explicitly calling
1635+ sort() with the desired column and order as arguments on the
1636+ QSortFilterProxyModel (or on the original model if it implements sort()).
1637+ For example:
1638+
1639+ \snippet qsortfilterproxymodel-details/main.cpp 4
1640+
1641+ QSortFilterProxyModel can be sorted by column -1, in which case it returns
1642+ to the sort order of the underlying source model.
1643+
1644+ \section1 Filtering
1645+
1646+ In addition to sorting, QSortFilterProxyModel can be used to hide items
1647+ that do not match a certain filter. The filter is specified using a QRegExp
1648+ object and is applied to the filterRole() (Qt::DisplayRole by default) of
1649+ each item, for a given column. The QRegExp object can be used to match a
1650+ regular expression, a wildcard pattern, or a fixed string. For example:
1651+
1652+ \snippet qsortfilterproxymodel-details/main.cpp 5
1653+
1654+ For hierarchical models, the filter is applied recursively to all children.
1655+ If a parent item doesn't match the filter, none of its children will be
1656+ shown.
1657+
1658+ A common use case is to let the user specify the filter regexp, wildcard
1659+ pattern, or fixed string in a QLineEdit and to connect the
1660+ \l{QLineEdit::textChanged()}{textChanged()} signal to setFilterRegExp(),
1661+ setFilterWildcard(), or setFilterFixedString() to reapply the filter.
1662+
1663+ Custom filtering behavior can be achieved by reimplementing the
1664+ filterAcceptsRow() and filterAcceptsColumn() functions. For
1665+ example (from the \l{itemviews/customsortfiltermodel}
1666+ {Custom Sort/Filter Model} example), the following implementation ignores
1667+ the \l{QSortFilterProxyModel::filterKeyColumn}{filterKeyColumn} property
1668+ and performs filtering on columns 0, 1, and 2:
1669+
1670+ \snippet ../widgets/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 3
1671+
1672+ (This code snippet comes from the
1673+ \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model}
1674+ example.)
1675+
1676+ If you are working with large amounts of filtering and have to invoke
1677+ invalidateFilter() repeatedly, using reset() may be more efficient,
1678+ depending on the implementation of your model. However, reset() returns the
1679+ proxy model to its original state, losing selection information, and will
1680+ cause the proxy model to be repopulated.
1681+
1682+ \section1 Subclassing
1683+
1684+ Since QAbstractProxyModel and its subclasses are derived from
1685+ QAbstractItemModel, much of the same advice about subclassing normal models
1686+ also applies to proxy models. In addition, it is worth noting that many of
1687+ the default implementations of functions in this class are written so that
1688+ they call the equivalent functions in the relevant source model. This
1689+ simple proxying mechanism may need to be overridden for source models with
1690+ more complex behavior; for example, if the source model provides a custom
1691+ hasChildren() implementation, you should also provide one in the proxy
1692+ model.
1693+
1694+ \note Some general guidelines for subclassing models are available in the
1695+ \l{Model Subclassing Reference}.
1696+
1697+ \sa QAbstractProxyModel, QAbstractItemModel, {Model/View Programming},
1698+ {Basic Sort/Filter Model Example}, {Custom Sort/Filter Model Example}, QIdentityProxyModel
1699+*/
1700+
1701+/*!
1702+ Constructs a sorting filter model with the given \a parent.
1703+*/
1704+
1705+QSortFilterProxyModel::QSortFilterProxyModel(QObject *parent)
1706+ : QAbstractProxyModel(*new QSortFilterProxyModelPrivate, parent)
1707+{
1708+ Q_D(QSortFilterProxyModel);
1709+ d->proxy_sort_column = d->source_sort_column = -1;
1710+ d->sort_order = Qt::AscendingOrder;
1711+ d->sort_casesensitivity = Qt::CaseSensitive;
1712+ d->sort_role = Qt::DisplayRole;
1713+ d->sort_localeaware = false;
1714+ d->filter_column = 0;
1715+ d->filter_role = Qt::DisplayRole;
1716+ d->dynamic_sortfilter = true;
1717+ connect(this, SIGNAL(modelReset()), this, SLOT(_q_clearMapping()));
1718+}
1719+
1720+/*!
1721+ Destroys this sorting filter model.
1722+*/
1723+QSortFilterProxyModel::~QSortFilterProxyModel()
1724+{
1725+ Q_D(QSortFilterProxyModel);
1726+ qDeleteAll(d->source_index_mapping);
1727+ d->source_index_mapping.clear();
1728+}
1729+
1730+/*!
1731+ \reimp
1732+*/
1733+void QSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
1734+{
1735+ Q_D(QSortFilterProxyModel);
1736+
1737+ beginResetModel();
1738+
1739+ disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
1740+ this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex)));
1741+
1742+ disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
1743+ this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int)));
1744+
1745+ disconnect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
1746+ this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int)));
1747+
1748+ disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
1749+ this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int)));
1750+
1751+ disconnect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
1752+ this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int)));
1753+
1754+ disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
1755+ this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int)));
1756+
1757+ disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
1758+ this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
1759+
1760+ disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
1761+ this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int)));
1762+
1763+ disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
1764+ this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int)));
1765+
1766+ disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
1767+ this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int)));
1768+
1769+ disconnect(d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
1770+ this, SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
1771+
1772+ disconnect(d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
1773+ this, SLOT(_q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)));
1774+
1775+ disconnect(d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
1776+ this, SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
1777+
1778+ disconnect(d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
1779+ this, SLOT(_q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int)));
1780+
1781+ disconnect(d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
1782+ this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
1783+
1784+ disconnect(d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
1785+ this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
1786+
1787+ disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_sourceAboutToBeReset()));
1788+ disconnect(d->model, SIGNAL(modelReset()), this, SLOT(_q_sourceReset()));
1789+
1790+ QAbstractProxyModel::setSourceModel(sourceModel);
1791+
1792+ connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
1793+ this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex)));
1794+
1795+ connect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
1796+ this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int)));
1797+
1798+ connect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
1799+ this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int)));
1800+
1801+ connect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
1802+ this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int)));
1803+
1804+ connect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
1805+ this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int)));
1806+
1807+ connect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
1808+ this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int)));
1809+
1810+ connect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
1811+ this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
1812+
1813+ connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
1814+ this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int)));
1815+
1816+ connect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
1817+ this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int)));
1818+
1819+ connect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
1820+ this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int)));
1821+
1822+ connect(d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
1823+ this, SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
1824+
1825+ connect(d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
1826+ this, SLOT(_q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)));
1827+
1828+ connect(d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
1829+ this, SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
1830+
1831+ connect(d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
1832+ this, SLOT(_q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int)));
1833+
1834+ connect(d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
1835+ this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
1836+
1837+ connect(d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
1838+ this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
1839+
1840+ connect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_sourceAboutToBeReset()));
1841+ connect(d->model, SIGNAL(modelReset()), this, SLOT(_q_sourceReset()));
1842+
1843+ d->_q_clearMapping();
1844+ endResetModel();
1845+ if (d->update_source_sort_column() && d->dynamic_sortfilter)
1846+ d->sort();
1847+}
1848+
1849+/*!
1850+ \reimp
1851+*/
1852+QModelIndex QSortFilterProxyModel::index(int row, int column, const QModelIndex &parent) const
1853+{
1854+ Q_D(const QSortFilterProxyModel);
1855+ if (row < 0 || column < 0)
1856+ return QModelIndex();
1857+
1858+ QModelIndex source_parent = mapToSource(parent); // parent is already mapped at this point
1859+ IndexMap::const_iterator it = d->create_mapping(source_parent); // but make sure that the children are mapped
1860+ if (it.value()->source_rows.count() <= row || it.value()->source_columns.count() <= column)
1861+ return QModelIndex();
1862+
1863+ return d->create_index(row, column, it);
1864+}
1865+
1866+/*!
1867+ \reimp
1868+*/
1869+QModelIndex QSortFilterProxyModel::parent(const QModelIndex &child) const
1870+{
1871+ Q_D(const QSortFilterProxyModel);
1872+ if (!d->indexValid(child))
1873+ return QModelIndex();
1874+ IndexMap::const_iterator it = d->index_to_iterator(child);
1875+ Q_ASSERT(it != d->source_index_mapping.constEnd());
1876+ QModelIndex source_parent = it.key();
1877+ QModelIndex proxy_parent = mapFromSource(source_parent);
1878+ return proxy_parent;
1879+}
1880+
1881+/*!
1882+ \reimp
1883+*/
1884+QModelIndex QSortFilterProxyModel::sibling(int row, int column, const QModelIndex &idx) const
1885+{
1886+ Q_D(const QSortFilterProxyModel);
1887+ if (!d->indexValid(idx))
1888+ return QModelIndex();
1889+
1890+ const IndexMap::const_iterator it = d->index_to_iterator(idx);
1891+ if (it.value()->source_rows.count() <= row || it.value()->source_columns.count() <= column)
1892+ return QModelIndex();
1893+
1894+ return d->create_index(row, column, it);
1895+}
1896+
1897+/*!
1898+ \reimp
1899+*/
1900+int QSortFilterProxyModel::rowCount(const QModelIndex &parent) const
1901+{
1902+ Q_D(const QSortFilterProxyModel);
1903+ QModelIndex source_parent = mapToSource(parent);
1904+ if (parent.isValid() && !source_parent.isValid())
1905+ return 0;
1906+ IndexMap::const_iterator it = d->create_mapping(source_parent);
1907+ return it.value()->source_rows.count();
1908+}
1909+
1910+/*!
1911+ \reimp
1912+*/
1913+int QSortFilterProxyModel::columnCount(const QModelIndex &parent) const
1914+{
1915+ Q_D(const QSortFilterProxyModel);
1916+ QModelIndex source_parent = mapToSource(parent);
1917+ if (parent.isValid() && !source_parent.isValid())
1918+ return 0;
1919+ IndexMap::const_iterator it = d->create_mapping(source_parent);
1920+ return it.value()->source_columns.count();
1921+}
1922+
1923+/*!
1924+ \reimp
1925+*/
1926+bool QSortFilterProxyModel::hasChildren(const QModelIndex &parent) const
1927+{
1928+ Q_D(const QSortFilterProxyModel);
1929+ QModelIndex source_parent = mapToSource(parent);
1930+ if (parent.isValid() && !source_parent.isValid())
1931+ return false;
1932+ if (!d->model->hasChildren(source_parent))
1933+ return false;
1934+
1935+ if (d->model->canFetchMore(source_parent))
1936+ return true; //we assume we might have children that can be fetched
1937+
1938+ QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
1939+ return m->source_rows.count() != 0 && m->source_columns.count() != 0;
1940+}
1941+
1942+/*!
1943+ \reimp
1944+*/
1945+QVariant QSortFilterProxyModel::data(const QModelIndex &index, int role) const
1946+{
1947+ Q_D(const QSortFilterProxyModel);
1948+ QModelIndex source_index = mapToSource(index);
1949+ if (index.isValid() && !source_index.isValid())
1950+ return QVariant();
1951+ return d->model->data(source_index, role);
1952+}
1953+
1954+/*!
1955+ \reimp
1956+*/
1957+bool QSortFilterProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
1958+{
1959+ Q_D(QSortFilterProxyModel);
1960+ QModelIndex source_index = mapToSource(index);
1961+ if (index.isValid() && !source_index.isValid())
1962+ return false;
1963+ return d->model->setData(source_index, value, role);
1964+}
1965+
1966+/*!
1967+ \reimp
1968+*/
1969+QVariant QSortFilterProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
1970+{
1971+ Q_D(const QSortFilterProxyModel);
1972+ IndexMap::const_iterator it = d->create_mapping(QModelIndex());
1973+ if (it.value()->source_rows.count() * it.value()->source_columns.count() > 0)
1974+ return QAbstractProxyModel::headerData(section, orientation, role);
1975+ int source_section;
1976+ if (orientation == Qt::Vertical) {
1977+ if (section < 0 || section >= it.value()->source_rows.count())
1978+ return QVariant();
1979+ source_section = it.value()->source_rows.at(section);
1980+ } else {
1981+ if (section < 0 || section >= it.value()->source_columns.count())
1982+ return QVariant();
1983+ source_section = it.value()->source_columns.at(section);
1984+ }
1985+ return d->model->headerData(source_section, orientation, role);
1986+}
1987+
1988+/*!
1989+ \reimp
1990+*/
1991+bool QSortFilterProxyModel::setHeaderData(int section, Qt::Orientation orientation,
1992+ const QVariant &value, int role)
1993+{
1994+ Q_D(QSortFilterProxyModel);
1995+ IndexMap::const_iterator it = d->create_mapping(QModelIndex());
1996+ if (it.value()->source_rows.count() * it.value()->source_columns.count() > 0)
1997+ return QAbstractProxyModel::setHeaderData(section, orientation, value, role);
1998+ int source_section;
1999+ if (orientation == Qt::Vertical) {
2000+ if (section < 0 || section >= it.value()->source_rows.count())
2001+ return false;
2002+ source_section = it.value()->source_rows.at(section);
2003+ } else {
2004+ if (section < 0 || section >= it.value()->source_columns.count())
2005+ return false;
2006+ source_section = it.value()->source_columns.at(section);
2007+ }
2008+ return d->model->setHeaderData(source_section, orientation, value, role);
2009+}
2010+
2011+/*!
2012+ \reimp
2013+*/
2014+QMimeData *QSortFilterProxyModel::mimeData(const QModelIndexList &indexes) const
2015+{
2016+ Q_D(const QSortFilterProxyModel);
2017+ QModelIndexList source_indexes;
2018+ for (int i = 0; i < indexes.count(); ++i)
2019+ source_indexes << mapToSource(indexes.at(i));
2020+ return d->model->mimeData(source_indexes);
2021+}
2022+
2023+/*!
2024+ \reimp
2025+*/
2026+QStringList QSortFilterProxyModel::mimeTypes() const
2027+{
2028+ Q_D(const QSortFilterProxyModel);
2029+ return d->model->mimeTypes();
2030+}
2031+
2032+/*!
2033+ \reimp
2034+*/
2035+Qt::DropActions QSortFilterProxyModel::supportedDropActions() const
2036+{
2037+ Q_D(const QSortFilterProxyModel);
2038+ return d->model->supportedDropActions();
2039+}
2040+
2041+/*!
2042+ \reimp
2043+*/
2044+bool QSortFilterProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
2045+ int row, int column, const QModelIndex &parent)
2046+{
2047+ Q_D(QSortFilterProxyModel);
2048+ if ((row == -1) && (column == -1))
2049+ return d->model->dropMimeData(data, action, -1, -1, mapToSource(parent));
2050+ int source_destination_row = -1;
2051+ int source_destination_column = -1;
2052+ QModelIndex source_parent;
2053+ if (row == rowCount(parent)) {
2054+ source_parent = mapToSource(parent);
2055+ source_destination_row = d->model->rowCount(source_parent);
2056+ } else {
2057+ QModelIndex proxy_index = index(row, column, parent);
2058+ QModelIndex source_index = mapToSource(proxy_index);
2059+ source_destination_row = source_index.row();
2060+ source_destination_column = source_index.column();
2061+ source_parent = source_index.parent();
2062+ }
2063+ return d->model->dropMimeData(data, action, source_destination_row,
2064+ source_destination_column, source_parent);
2065+}
2066+
2067+/*!
2068+ \reimp
2069+*/
2070+bool QSortFilterProxyModel::insertRows(int row, int count, const QModelIndex &parent)
2071+{
2072+ Q_D(QSortFilterProxyModel);
2073+ if (row < 0 || count <= 0)
2074+ return false;
2075+ QModelIndex source_parent = mapToSource(parent);
2076+ if (parent.isValid() && !source_parent.isValid())
2077+ return false;
2078+ QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2079+ if (row > m->source_rows.count())
2080+ return false;
2081+ int source_row = (row >= m->source_rows.count()
2082+ ? m->source_rows.count()
2083+ : m->source_rows.at(row));
2084+ return d->model->insertRows(source_row, count, source_parent);
2085+}
2086+
2087+/*!
2088+ \reimp
2089+*/
2090+bool QSortFilterProxyModel::insertColumns(int column, int count, const QModelIndex &parent)
2091+{
2092+ Q_D(QSortFilterProxyModel);
2093+ if (column < 0|| count <= 0)
2094+ return false;
2095+ QModelIndex source_parent = mapToSource(parent);
2096+ if (parent.isValid() && !source_parent.isValid())
2097+ return false;
2098+ QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2099+ if (column > m->source_columns.count())
2100+ return false;
2101+ int source_column = (column >= m->source_columns.count()
2102+ ? m->source_columns.count()
2103+ : m->source_columns.at(column));
2104+ return d->model->insertColumns(source_column, count, source_parent);
2105+}
2106+
2107+/*!
2108+ \reimp
2109+*/
2110+bool QSortFilterProxyModel::removeRows(int row, int count, const QModelIndex &parent)
2111+{
2112+ Q_D(QSortFilterProxyModel);
2113+ if (row < 0 || count <= 0)
2114+ return false;
2115+ QModelIndex source_parent = mapToSource(parent);
2116+ if (parent.isValid() && !source_parent.isValid())
2117+ return false;
2118+ QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2119+ if (row + count > m->source_rows.count())
2120+ return false;
2121+ if ((count == 1)
2122+ || ((d->source_sort_column < 0) && (m->proxy_rows.count() == m->source_rows.count()))) {
2123+ int source_row = m->source_rows.at(row);
2124+ return d->model->removeRows(source_row, count, source_parent);
2125+ }
2126+ // remove corresponding source intervals
2127+ // ### if this proves to be slow, we can switch to single-row removal
2128+ QVector<int> rows;
2129+ for (int i = row; i < row + count; ++i)
2130+ rows.append(m->source_rows.at(i));
2131+ std::sort(rows.begin(), rows.end());
2132+
2133+ int pos = rows.count() - 1;
2134+ bool ok = true;
2135+ while (pos >= 0) {
2136+ const int source_end = rows.at(pos--);
2137+ int source_start = source_end;
2138+ while ((pos >= 0) && (rows.at(pos) == (source_start - 1))) {
2139+ --source_start;
2140+ --pos;
2141+ }
2142+ ok = ok && d->model->removeRows(source_start, source_end - source_start + 1,
2143+ source_parent);
2144+ }
2145+ return ok;
2146+}
2147+
2148+/*!
2149+ \reimp
2150+*/
2151+bool QSortFilterProxyModel::removeColumns(int column, int count, const QModelIndex &parent)
2152+{
2153+ Q_D(QSortFilterProxyModel);
2154+ if (column < 0 || count <= 0)
2155+ return false;
2156+ QModelIndex source_parent = mapToSource(parent);
2157+ if (parent.isValid() && !source_parent.isValid())
2158+ return false;
2159+ QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2160+ if (column + count > m->source_columns.count())
2161+ return false;
2162+ if ((count == 1) || (m->proxy_columns.count() == m->source_columns.count())) {
2163+ int source_column = m->source_columns.at(column);
2164+ return d->model->removeColumns(source_column, count, source_parent);
2165+ }
2166+ // remove corresponding source intervals
2167+ QVector<int> columns;
2168+ for (int i = column; i < column + count; ++i)
2169+ columns.append(m->source_columns.at(i));
2170+
2171+ int pos = columns.count() - 1;
2172+ bool ok = true;
2173+ while (pos >= 0) {
2174+ const int source_end = columns.at(pos--);
2175+ int source_start = source_end;
2176+ while ((pos >= 0) && (columns.at(pos) == (source_start - 1))) {
2177+ --source_start;
2178+ --pos;
2179+ }
2180+ ok = ok && d->model->removeColumns(source_start, source_end - source_start + 1,
2181+ source_parent);
2182+ }
2183+ return ok;
2184+}
2185+
2186+/*!
2187+ \reimp
2188+*/
2189+void QSortFilterProxyModel::fetchMore(const QModelIndex &parent)
2190+{
2191+ Q_D(QSortFilterProxyModel);
2192+ QModelIndex source_parent;
2193+ if (d->indexValid(parent))
2194+ source_parent = mapToSource(parent);
2195+ d->model->fetchMore(source_parent);
2196+}
2197+
2198+/*!
2199+ \reimp
2200+*/
2201+bool QSortFilterProxyModel::canFetchMore(const QModelIndex &parent) const
2202+{
2203+ Q_D(const QSortFilterProxyModel);
2204+ QModelIndex source_parent;
2205+ if (d->indexValid(parent))
2206+ source_parent = mapToSource(parent);
2207+ return d->model->canFetchMore(source_parent);
2208+}
2209+
2210+/*!
2211+ \reimp
2212+*/
2213+Qt::ItemFlags QSortFilterProxyModel::flags(const QModelIndex &index) const
2214+{
2215+ Q_D(const QSortFilterProxyModel);
2216+ QModelIndex source_index;
2217+ if (d->indexValid(index))
2218+ source_index = mapToSource(index);
2219+ return d->model->flags(source_index);
2220+}
2221+
2222+/*!
2223+ \reimp
2224+*/
2225+QModelIndex QSortFilterProxyModel::buddy(const QModelIndex &index) const
2226+{
2227+ Q_D(const QSortFilterProxyModel);
2228+ if (!d->indexValid(index))
2229+ return QModelIndex();
2230+ QModelIndex source_index = mapToSource(index);
2231+ QModelIndex source_buddy = d->model->buddy(source_index);
2232+ if (source_index == source_buddy)
2233+ return index;
2234+ return mapFromSource(source_buddy);
2235+}
2236+
2237+/*!
2238+ \reimp
2239+*/
2240+QModelIndexList QSortFilterProxyModel::match(const QModelIndex &start, int role,
2241+ const QVariant &value, int hits,
2242+ Qt::MatchFlags flags) const
2243+{
2244+ return QAbstractProxyModel::match(start, role, value, hits, flags);
2245+}
2246+
2247+/*!
2248+ \reimp
2249+*/
2250+QSize QSortFilterProxyModel::span(const QModelIndex &index) const
2251+{
2252+ Q_D(const QSortFilterProxyModel);
2253+ QModelIndex source_index = mapToSource(index);
2254+ if (index.isValid() && !source_index.isValid())
2255+ return QSize();
2256+ return d->model->span(source_index);
2257+}
2258+
2259+/*!
2260+ \reimp
2261+*/
2262+void QSortFilterProxyModel::sort(int column, Qt::SortOrder order)
2263+{
2264+ Q_D(QSortFilterProxyModel);
2265+ if (d->dynamic_sortfilter && d->proxy_sort_column == column && d->sort_order == order)
2266+ return;
2267+ d->sort_order = order;
2268+ d->proxy_sort_column = column;
2269+ d->update_source_sort_column();
2270+ d->sort();
2271+}
2272+
2273+/*!
2274+ \since 4.5
2275+ \brief the column currently used for sorting
2276+
2277+ This returns the most recently used sort column.
2278+*/
2279+int QSortFilterProxyModel::sortColumn() const
2280+{
2281+ Q_D(const QSortFilterProxyModel);
2282+ return d->proxy_sort_column;
2283+}
2284+
2285+/*!
2286+ \since 4.5
2287+ \brief the order currently used for sorting
2288+
2289+ This returns the most recently used sort order.
2290+*/
2291+Qt::SortOrder QSortFilterProxyModel::sortOrder() const
2292+{
2293+ Q_D(const QSortFilterProxyModel);
2294+ return d->sort_order;
2295+}
2296+
2297+/*!
2298+ \property QSortFilterProxyModel::filterRegExp
2299+ \brief the QRegExp used to filter the contents of the source model
2300+
2301+ Setting this property overwrites the current
2302+ \l{QSortFilterProxyModel::filterCaseSensitivity}{filterCaseSensitivity}.
2303+ By default, the QRegExp is an empty string matching all contents.
2304+
2305+ If no QRegExp or an empty string is set, everything in the source model
2306+ will be accepted.
2307+
2308+ \sa filterCaseSensitivity, setFilterWildcard(), setFilterFixedString()
2309+*/
2310+QRegExp QSortFilterProxyModel::filterRegExp() const
2311+{
2312+ Q_D(const QSortFilterProxyModel);
2313+ return d->filter_regexp;
2314+}
2315+
2316+void QSortFilterProxyModel::setFilterRegExp(const QRegExp &regExp)
2317+{
2318+ Q_D(QSortFilterProxyModel);
2319+ d->filter_regexp = regExp;
2320+ d->filter_changed();
2321+}
2322+
2323+/*!
2324+ \property QSortFilterProxyModel::filterKeyColumn
2325+ \brief the column where the key used to filter the contents of the
2326+ source model is read from.
2327+
2328+ The default value is 0. If the value is -1, the keys will be read
2329+ from all columns.
2330+*/
2331+int QSortFilterProxyModel::filterKeyColumn() const
2332+{
2333+ Q_D(const QSortFilterProxyModel);
2334+ return d->filter_column;
2335+}
2336+
2337+void QSortFilterProxyModel::setFilterKeyColumn(int column)
2338+{
2339+ Q_D(QSortFilterProxyModel);
2340+ d->filter_column = column;
2341+ d->filter_changed();
2342+}
2343+
2344+/*!
2345+ \property QSortFilterProxyModel::filterCaseSensitivity
2346+
2347+ \brief the case sensitivity of the QRegExp pattern used to filter the
2348+ contents of the source model
2349+
2350+ By default, the filter is case sensitive.
2351+
2352+ \sa filterRegExp, sortCaseSensitivity
2353+*/
2354+Qt::CaseSensitivity QSortFilterProxyModel::filterCaseSensitivity() const
2355+{
2356+ Q_D(const QSortFilterProxyModel);
2357+ return d->filter_regexp.caseSensitivity();
2358+}
2359+
2360+void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs)
2361+{
2362+ Q_D(QSortFilterProxyModel);
2363+ if (cs == d->filter_regexp.caseSensitivity())
2364+ return;
2365+ d->filter_regexp.setCaseSensitivity(cs);
2366+ d->filter_changed();
2367+}
2368+
2369+/*!
2370+ \since 4.2
2371+ \property QSortFilterProxyModel::sortCaseSensitivity
2372+ \brief the case sensitivity setting used for comparing strings when sorting
2373+
2374+ By default, sorting is case sensitive.
2375+
2376+ \sa filterCaseSensitivity, lessThan()
2377+*/
2378+Qt::CaseSensitivity QSortFilterProxyModel::sortCaseSensitivity() const
2379+{
2380+ Q_D(const QSortFilterProxyModel);
2381+ return d->sort_casesensitivity;
2382+}
2383+
2384+void QSortFilterProxyModel::setSortCaseSensitivity(Qt::CaseSensitivity cs)
2385+{
2386+ Q_D(QSortFilterProxyModel);
2387+ if (d->sort_casesensitivity == cs)
2388+ return;
2389+
2390+ d->sort_casesensitivity = cs;
2391+ d->sort();
2392+}
2393+
2394+/*!
2395+ \since 4.3
2396+ \property QSortFilterProxyModel::isSortLocaleAware
2397+ \brief the local aware setting used for comparing strings when sorting
2398+
2399+ By default, sorting is not local aware.
2400+
2401+ \sa sortCaseSensitivity, lessThan()
2402+*/
2403+bool QSortFilterProxyModel::isSortLocaleAware() const
2404+{
2405+ Q_D(const QSortFilterProxyModel);
2406+ return d->sort_localeaware;
2407+}
2408+
2409+void QSortFilterProxyModel::setSortLocaleAware(bool on)
2410+{
2411+ Q_D(QSortFilterProxyModel);
2412+ if (d->sort_localeaware == on)
2413+ return;
2414+
2415+ d->sort_localeaware = on;
2416+ d->sort();
2417+}
2418+
2419+/*!
2420+ \overload
2421+
2422+ Sets the regular expression used to filter the contents
2423+ of the source model to \a pattern.
2424+
2425+ \sa setFilterCaseSensitivity(), setFilterWildcard(), setFilterFixedString(), filterRegExp()
2426+*/
2427+void QSortFilterProxyModel::setFilterRegExp(const QString &pattern)
2428+{
2429+ Q_D(QSortFilterProxyModel);
2430+ d->filter_regexp.setPatternSyntax(QRegExp::RegExp);
2431+ d->filter_regexp.setPattern(pattern);
2432+ d->filter_changed();
2433+}
2434+
2435+/*!
2436+ Sets the wildcard expression used to filter the contents
2437+ of the source model to the given \a pattern.
2438+
2439+ \sa setFilterCaseSensitivity(), setFilterRegExp(), setFilterFixedString(), filterRegExp()
2440+*/
2441+void QSortFilterProxyModel::setFilterWildcard(const QString &pattern)
2442+{
2443+ Q_D(QSortFilterProxyModel);
2444+ d->filter_regexp.setPatternSyntax(QRegExp::Wildcard);
2445+ d->filter_regexp.setPattern(pattern);
2446+ d->filter_changed();
2447+}
2448+
2449+/*!
2450+ Sets the fixed string used to filter the contents
2451+ of the source model to the given \a pattern.
2452+
2453+ \sa setFilterCaseSensitivity(), setFilterRegExp(), setFilterWildcard(), filterRegExp()
2454+*/
2455+void QSortFilterProxyModel::setFilterFixedString(const QString &pattern)
2456+{
2457+ Q_D(QSortFilterProxyModel);
2458+ d->filter_regexp.setPatternSyntax(QRegExp::FixedString);
2459+ d->filter_regexp.setPattern(pattern);
2460+ d->filter_changed();
2461+}
2462+
2463+/*!
2464+ \since 4.2
2465+ \property QSortFilterProxyModel::dynamicSortFilter
2466+ \brief whether the proxy model is dynamically sorted and filtered
2467+ whenever the contents of the source model change
2468+
2469+ Note that you should not update the source model through the proxy
2470+ model when dynamicSortFilter is true. For instance, if you set the
2471+ proxy model on a QComboBox, then using functions that update the
2472+ model, e.g., \l{QComboBox::}{addItem()}, will not work as
2473+ expected. An alternative is to set dynamicSortFilter to false and
2474+ call \l{QSortFilterProxyModel::}{sort()} after adding items to the
2475+ QComboBox.
2476+
2477+ The default value is true.
2478+*/
2479+bool QSortFilterProxyModel::dynamicSortFilter() const
2480+{
2481+ Q_D(const QSortFilterProxyModel);
2482+ return d->dynamic_sortfilter;
2483+}
2484+
2485+void QSortFilterProxyModel::setDynamicSortFilter(bool enable)
2486+{
2487+ Q_D(QSortFilterProxyModel);
2488+ d->dynamic_sortfilter = enable;
2489+ if (enable)
2490+ d->sort();
2491+}
2492+
2493+/*!
2494+ \since 4.2
2495+ \property QSortFilterProxyModel::sortRole
2496+ \brief the item role that is used to query the source model's data when sorting items
2497+
2498+ The default value is Qt::DisplayRole.
2499+
2500+ \sa lessThan()
2501+*/
2502+int QSortFilterProxyModel::sortRole() const
2503+{
2504+ Q_D(const QSortFilterProxyModel);
2505+ return d->sort_role;
2506+}
2507+
2508+void QSortFilterProxyModel::setSortRole(int role)
2509+{
2510+ Q_D(QSortFilterProxyModel);
2511+ if (d->sort_role == role)
2512+ return;
2513+ d->sort_role = role;
2514+ d->sort();
2515+}
2516+
2517+/*!
2518+ \since 4.2
2519+ \property QSortFilterProxyModel::filterRole
2520+ \brief the item role that is used to query the source model's data when filtering items
2521+
2522+ The default value is Qt::DisplayRole.
2523+
2524+ \sa filterAcceptsRow()
2525+*/
2526+int QSortFilterProxyModel::filterRole() const
2527+{
2528+ Q_D(const QSortFilterProxyModel);
2529+ return d->filter_role;
2530+}
2531+
2532+void QSortFilterProxyModel::setFilterRole(int role)
2533+{
2534+ Q_D(QSortFilterProxyModel);
2535+ if (d->filter_role == role)
2536+ return;
2537+ d->filter_role = role;
2538+ d->filter_changed();
2539+}
2540+
2541+/*!
2542+ \obsolete
2543+
2544+ This function is obsolete. Use invalidate() instead.
2545+*/
2546+void QSortFilterProxyModel::clear()
2547+{
2548+ Q_D(QSortFilterProxyModel);
2549+ emit layoutAboutToBeChanged();
2550+ d->_q_clearMapping();
2551+ emit layoutChanged();
2552+}
2553+
2554+/*!
2555+ \since 4.3
2556+
2557+ Invalidates the current sorting and filtering.
2558+
2559+ \sa invalidateFilter()
2560+*/
2561+void QSortFilterProxyModel::invalidate()
2562+{
2563+ Q_D(QSortFilterProxyModel);
2564+ emit layoutAboutToBeChanged();
2565+ d->_q_clearMapping();
2566+ emit layoutChanged();
2567+}
2568+
2569+/*!
2570+ \obsolete
2571+
2572+ This function is obsolete. Use invalidateFilter() instead.
2573+*/
2574+void QSortFilterProxyModel::filterChanged()
2575+{
2576+ Q_D(QSortFilterProxyModel);
2577+ d->filter_changed();
2578+}
2579+
2580+/*!
2581+ \since 4.3
2582+
2583+ Invalidates the current filtering.
2584+
2585+ This function should be called if you are implementing custom filtering
2586+ (e.g. filterAcceptsRow()), and your filter parameters have changed.
2587+
2588+ \sa invalidate()
2589+*/
2590+void QSortFilterProxyModel::invalidateFilter()
2591+{
2592+ Q_D(QSortFilterProxyModel);
2593+ d->filter_changed();
2594+}
2595+
2596+/*!
2597+ Returns true if the value of the item referred to by the given
2598+ index \a left is less than the value of the item referred to by
2599+ the given index \a right, otherwise returns false.
2600+
2601+ This function is used as the < operator when sorting, and handles
2602+ the following QVariant types:
2603+
2604+ \list
2605+ \li QVariant::Int
2606+ \li QVariant::UInt
2607+ \li QVariant::LongLong
2608+ \li QVariant::ULongLong
2609+ \li QVariant::Double
2610+ \li QVariant::Char
2611+ \li QVariant::Date
2612+ \li QVariant::Time
2613+ \li QVariant::DateTime
2614+ \li QVariant::String
2615+ \endlist
2616+
2617+ Any other type will be converted to a QString using
2618+ QVariant::toString().
2619+
2620+ Comparison of \l{QString}s is case sensitive by default; this can
2621+ be changed using the \l {QSortFilterProxyModel::sortCaseSensitivity}
2622+ {sortCaseSensitivity} property.
2623+
2624+ By default, the Qt::DisplayRole associated with the
2625+ \l{QModelIndex}es is used for comparisons. This can be changed by
2626+ setting the \l {QSortFilterProxyModel::sortRole} {sortRole} property.
2627+
2628+ \note The indices passed in correspond to the source model.
2629+
2630+ \sa sortRole, sortCaseSensitivity, dynamicSortFilter
2631+*/
2632+bool QSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
2633+{
2634+ Q_D(const QSortFilterProxyModel);
2635+ QVariant l = (left.model() ? left.model()->data(left, d->sort_role) : QVariant());
2636+ QVariant r = (right.model() ? right.model()->data(right, d->sort_role) : QVariant());
2637+ switch (l.userType()) {
2638+ case QVariant::Invalid:
2639+ return (r.type() != QVariant::Invalid);
2640+ case QVariant::Int:
2641+ return l.toInt() < r.toInt();
2642+ case QVariant::UInt:
2643+ return l.toUInt() < r.toUInt();
2644+ case QVariant::LongLong:
2645+ return l.toLongLong() < r.toLongLong();
2646+ case QVariant::ULongLong:
2647+ return l.toULongLong() < r.toULongLong();
2648+ case QMetaType::Float:
2649+ return l.toFloat() < r.toFloat();
2650+ case QVariant::Double:
2651+ return l.toDouble() < r.toDouble();
2652+ case QVariant::Char:
2653+ return l.toChar() < r.toChar();
2654+ case QVariant::Date:
2655+ return l.toDate() < r.toDate();
2656+ case QVariant::Time:
2657+ return l.toTime() < r.toTime();
2658+ case QVariant::DateTime:
2659+ return l.toDateTime() < r.toDateTime();
2660+ case QVariant::String:
2661+ default:
2662+ if (d->sort_localeaware)
2663+ return l.toString().localeAwareCompare(r.toString()) < 0;
2664+ else
2665+ return l.toString().compare(r.toString(), d->sort_casesensitivity) < 0;
2666+ }
2667+ return false;
2668+}
2669+
2670+/*!
2671+ Returns true if the item in the row indicated by the given \a source_row
2672+ and \a source_parent should be included in the model; otherwise returns
2673+ false.
2674+
2675+ The default implementation returns true if the value held by the relevant item
2676+ matches the filter string, wildcard string or regular expression.
2677+
2678+ \note By default, the Qt::DisplayRole is used to determine if the row
2679+ should be accepted or not. This can be changed by setting the
2680+ \l{QSortFilterProxyModel::filterRole}{filterRole} property.
2681+
2682+ \sa filterAcceptsColumn(), setFilterFixedString(), setFilterRegExp(), setFilterWildcard()
2683+*/
2684+bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
2685+{
2686+ Q_D(const QSortFilterProxyModel);
2687+ if (d->filter_regexp.isEmpty())
2688+ return true;
2689+ if (d->filter_column == -1) {
2690+ int column_count = d->model->columnCount(source_parent);
2691+ for (int column = 0; column < column_count; ++column) {
2692+ QModelIndex source_index = d->model->index(source_row, column, source_parent);
2693+ QString key = d->model->data(source_index, d->filter_role).toString();
2694+ if (key.contains(d->filter_regexp))
2695+ return true;
2696+ }
2697+ return false;
2698+ }
2699+ QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent);
2700+ if (!source_index.isValid()) // the column may not exist
2701+ return true;
2702+ QString key = d->model->data(source_index, d->filter_role).toString();
2703+ return key.contains(d->filter_regexp);
2704+}
2705+
2706+/*!
2707+ Returns true if the item in the column indicated by the given \a source_column
2708+ and \a source_parent should be included in the model; otherwise returns false.
2709+
2710+ The default implementation returns true if the value held by the relevant item
2711+ matches the filter string, wildcard string or regular expression.
2712+
2713+ \note By default, the Qt::DisplayRole is used to determine if the row
2714+ should be accepted or not. This can be changed by setting the \l
2715+ filterRole property.
2716+
2717+ \sa filterAcceptsRow(), setFilterFixedString(), setFilterRegExp(), setFilterWildcard()
2718+*/
2719+bool QSortFilterProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
2720+{
2721+ Q_UNUSED(source_column);
2722+ Q_UNUSED(source_parent);
2723+ return true;
2724+}
2725+
2726+/*!
2727+ Returns the source model index corresponding to the given \a
2728+ proxyIndex from the sorting filter model.
2729+
2730+ \sa mapFromSource()
2731+*/
2732+QModelIndex QSortFilterProxyModel::mapToSource(const QModelIndex &proxyIndex) const
2733+{
2734+ Q_D(const QSortFilterProxyModel);
2735+ return d->proxy_to_source(proxyIndex);
2736+}
2737+
2738+/*!
2739+ Returns the model index in the QSortFilterProxyModel given the \a
2740+ sourceIndex from the source model.
2741+
2742+ \sa mapToSource()
2743+*/
2744+QModelIndex QSortFilterProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
2745+{
2746+ Q_D(const QSortFilterProxyModel);
2747+ return d->source_to_proxy(sourceIndex);
2748+}
2749+
2750+/*!
2751+ \reimp
2752+*/
2753+QItemSelection QSortFilterProxyModel::mapSelectionToSource(const QItemSelection &proxySelection) const
2754+{
2755+ return QAbstractProxyModel::mapSelectionToSource(proxySelection);
2756+}
2757+
2758+/*!
2759+ \reimp
2760+*/
2761+QItemSelection QSortFilterProxyModel::mapSelectionFromSource(const QItemSelection &sourceSelection) const
2762+{
2763+ return QAbstractProxyModel::mapSelectionFromSource(sourceSelection);
2764+}
2765+
2766+/*!
2767+ \fn QObject *QSortFilterProxyModel::parent() const
2768+ \internal
2769+*/
2770+
2771+QT_END_NAMESPACE
2772+
2773+#include "moc_qsortfilterproxymodel.cpp"
2774+
2775+#endif // QT_NO_SORTFILTERPROXYMODEL
2776
2777=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests'
2778=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests/auto'
2779=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests/auto/corelib'
2780=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests/auto/corelib/itemmodels'
2781=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests/auto/corelib/itemmodels/qsortfilterproxymodel'
2782=== added file '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp'
2783--- .pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp 1970-01-01 00:00:00 +0000
2784+++ .pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp 2013-10-03 10:06:38 +0000
2785@@ -0,0 +1,3640 @@
2786+/****************************************************************************
2787+**
2788+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
2789+** Contact: http://www.qt-project.org/legal
2790+**
2791+** This file is part of the test suite of the Qt Toolkit.
2792+**
2793+** $QT_BEGIN_LICENSE:LGPL$
2794+** Commercial License Usage
2795+** Licensees holding valid commercial Qt licenses may use this file in
2796+** accordance with the commercial license agreement provided with the
2797+** Software or, alternatively, in accordance with the terms contained in
2798+** a written agreement between you and Digia. For licensing terms and
2799+** conditions see http://qt.digia.com/licensing. For further information
2800+** use the contact form at http://qt.digia.com/contact-us.
2801+**
2802+** GNU Lesser General Public License Usage
2803+** Alternatively, this file may be used under the terms of the GNU Lesser
2804+** General Public License version 2.1 as published by the Free Software
2805+** Foundation and appearing in the file LICENSE.LGPL included in the
2806+** packaging of this file. Please review the following information to
2807+** ensure the GNU Lesser General Public License version 2.1 requirements
2808+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
2809+**
2810+** In addition, as a special exception, Digia gives you certain additional
2811+** rights. These rights are described in the Digia Qt LGPL Exception
2812+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
2813+**
2814+** GNU General Public License Usage
2815+** Alternatively, this file may be used under the terms of the GNU
2816+** General Public License version 3.0 as published by the Free Software
2817+** Foundation and appearing in the file LICENSE.GPL included in the
2818+** packaging of this file. Please review the following information to
2819+** ensure the GNU General Public License version 3.0 requirements will be
2820+** met: http://www.gnu.org/copyleft/gpl.html.
2821+**
2822+**
2823+** $QT_END_LICENSE$
2824+**
2825+****************************************************************************/
2826+
2827+#include <QtTest/QtTest>
2828+#include "dynamictreemodel.h"
2829+#include "modeltest.h"
2830+
2831+#include <QtCore/QCoreApplication>
2832+#include <QtGui/QStandardItem>
2833+#include <QtWidgets/QTreeView>
2834+#include <QtWidgets/QTableView>
2835+
2836+#include <qdebug.h>
2837+
2838+typedef QList<int> IntList;
2839+typedef QPair<int, int> IntPair;
2840+typedef QList<IntPair> IntPairList;
2841+
2842+class tst_QSortFilterProxyModel : public QObject
2843+{
2844+ Q_OBJECT
2845+public:
2846+ tst_QSortFilterProxyModel();
2847+
2848+public slots:
2849+ void initTestCase();
2850+ void cleanupTestCase();
2851+ void cleanup();
2852+
2853+private slots:
2854+ void getSetCheck();
2855+ void sort_data();
2856+ void sort();
2857+ void sortHierarchy_data();
2858+ void sortHierarchy();
2859+
2860+ void insertRows_data();
2861+ void insertRows();
2862+ void prependRow();
2863+ void removeRows_data();
2864+ void removeRows();
2865+ void removeColumns_data();
2866+ void removeColumns();
2867+ void insertAfterSelect();
2868+ void removeAfterSelect();
2869+ void filter_data();
2870+ void filter();
2871+ void filterHierarchy_data();
2872+ void filterHierarchy();
2873+ void filterColumns_data();
2874+ void filterColumns();
2875+
2876+ void filterTable();
2877+ void filterCurrent();
2878+
2879+ void changeSourceLayout();
2880+ void removeSourceRows_data();
2881+ void removeSourceRows();
2882+ void insertSourceRows_data();
2883+ void insertSourceRows();
2884+ void changeFilter_data();
2885+ void changeFilter();
2886+ void changeSourceData_data();
2887+ void changeSourceData();
2888+ void sortFilterRole();
2889+ void selectionFilteredOut();
2890+ void match_data();
2891+ void match();
2892+ void insertIntoChildrenlessItem();
2893+ void invalidateMappedChildren();
2894+ void insertRowIntoFilteredParent();
2895+ void filterOutParentAndFilterInChild();
2896+
2897+ void sourceInsertRows();
2898+ void sourceModelDeletion();
2899+
2900+ void sortColumnTracking1();
2901+ void sortColumnTracking2();
2902+
2903+ void sortStable();
2904+
2905+ void hiddenColumns();
2906+ void insertRowsSort();
2907+ void staticSorting();
2908+ void dynamicSorting();
2909+ void fetchMore();
2910+ void hiddenChildren();
2911+ void mapFromToSource();
2912+ void removeRowsRecursive();
2913+ void doubleProxySelectionSetSourceModel();
2914+ void appearsAndSort();
2915+ void unnecessaryDynamicSorting();
2916+ void unnecessaryMapCreation();
2917+ void resetInvalidate_data();
2918+ void resetInvalidate();
2919+
2920+ void testMultipleProxiesWithSelection();
2921+ void mapSelectionFromSource();
2922+ void filteredColumns();
2923+ void headerDataChanged();
2924+
2925+ void testParentLayoutChanged();
2926+ void moveSourceRows();
2927+
2928+ void hierarchyFilterInvalidation();
2929+ void simpleFilterInvalidation();
2930+
2931+protected:
2932+ void buildHierarchy(const QStringList &data, QAbstractItemModel *model);
2933+ void checkHierarchy(const QStringList &data, const QAbstractItemModel *model);
2934+
2935+private:
2936+ QStandardItemModel *m_model;
2937+ QSortFilterProxyModel *m_proxy;
2938+};
2939+
2940+// Testing get/set functions
2941+void tst_QSortFilterProxyModel::getSetCheck()
2942+{
2943+ QSortFilterProxyModel obj1;
2944+ QCOMPARE(obj1.sourceModel(), (QAbstractItemModel *)0);
2945+ // int QSortFilterProxyModel::filterKeyColumn()
2946+ // void QSortFilterProxyModel::setFilterKeyColumn(int)
2947+ obj1.setFilterKeyColumn(0);
2948+ QCOMPARE(0, obj1.filterKeyColumn());
2949+ obj1.setFilterKeyColumn(INT_MIN);
2950+ QCOMPARE(INT_MIN, obj1.filterKeyColumn());
2951+ obj1.setFilterKeyColumn(INT_MAX);
2952+ QCOMPARE(INT_MAX, obj1.filterKeyColumn());
2953+}
2954+
2955+tst_QSortFilterProxyModel::tst_QSortFilterProxyModel()
2956+ : m_model(0), m_proxy(0)
2957+{
2958+}
2959+
2960+void tst_QSortFilterProxyModel::initTestCase()
2961+{
2962+ qRegisterMetaType<IntList>("IntList");
2963+ qRegisterMetaType<IntPair>("IntPair");
2964+ qRegisterMetaType<IntPairList>("IntPairList");
2965+ m_model = new QStandardItemModel(0, 1);
2966+ m_proxy = new QSortFilterProxyModel();
2967+ m_proxy->setSourceModel(m_model);
2968+}
2969+
2970+void tst_QSortFilterProxyModel::cleanupTestCase()
2971+{
2972+ delete m_proxy;
2973+ delete m_model;
2974+}
2975+
2976+void tst_QSortFilterProxyModel::cleanup()
2977+{
2978+ m_proxy->setFilterRegExp(QRegExp());
2979+ m_proxy->sort(-1, Qt::AscendingOrder);
2980+ m_model->clear();
2981+ m_model->insertColumns(0, 1);
2982+}
2983+
2984+/*
2985+ tests
2986+*/
2987+
2988+void tst_QSortFilterProxyModel::sort_data()
2989+{
2990+ QTest::addColumn<int>("sortOrder");
2991+ QTest::addColumn<int>("sortCaseSensitivity");
2992+ QTest::addColumn<QStringList>("initial");
2993+ QTest::addColumn<QStringList>("expected");
2994+
2995+ QTest::newRow("flat descending") << static_cast<int>(Qt::DescendingOrder)
2996+ << int(Qt::CaseSensitive)
2997+ << (QStringList()
2998+ << "delta"
2999+ << "yankee"
3000+ << "bravo"
3001+ << "lima"
3002+ << "charlie"
3003+ << "juliet"
3004+ << "tango"
3005+ << "hotel"
3006+ << "uniform"
3007+ << "alpha"
3008+ << "echo"
3009+ << "golf"
3010+ << "quebec"
3011+ << "foxtrot"
3012+ << "india"
3013+ << "romeo"
3014+ << "november"
3015+ << "oskar"
3016+ << "zulu"
3017+ << "kilo"
3018+ << "whiskey"
3019+ << "mike"
3020+ << "papa"
3021+ << "sierra"
3022+ << "xray"
3023+ << "viktor")
3024+ << (QStringList()
3025+ << "zulu"
3026+ << "yankee"
3027+ << "xray"
3028+ << "whiskey"
3029+ << "viktor"
3030+ << "uniform"
3031+ << "tango"
3032+ << "sierra"
3033+ << "romeo"
3034+ << "quebec"
3035+ << "papa"
3036+ << "oskar"
3037+ << "november"
3038+ << "mike"
3039+ << "lima"
3040+ << "kilo"
3041+ << "juliet"
3042+ << "india"
3043+ << "hotel"
3044+ << "golf"
3045+ << "foxtrot"
3046+ << "echo"
3047+ << "delta"
3048+ << "charlie"
3049+ << "bravo"
3050+ << "alpha");
3051+ QTest::newRow("flat ascending") << static_cast<int>(Qt::AscendingOrder)
3052+ << int(Qt::CaseSensitive)
3053+ << (QStringList()
3054+ << "delta"
3055+ << "yankee"
3056+ << "bravo"
3057+ << "lima"
3058+ << "charlie"
3059+ << "juliet"
3060+ << "tango"
3061+ << "hotel"
3062+ << "uniform"
3063+ << "alpha"
3064+ << "echo"
3065+ << "golf"
3066+ << "quebec"
3067+ << "foxtrot"
3068+ << "india"
3069+ << "romeo"
3070+ << "november"
3071+ << "oskar"
3072+ << "zulu"
3073+ << "kilo"
3074+ << "whiskey"
3075+ << "mike"
3076+ << "papa"
3077+ << "sierra"
3078+ << "xray"
3079+ << "viktor")
3080+ << (QStringList()
3081+ << "alpha"
3082+ << "bravo"
3083+ << "charlie"
3084+ << "delta"
3085+ << "echo"
3086+ << "foxtrot"
3087+ << "golf"
3088+ << "hotel"
3089+ << "india"
3090+ << "juliet"
3091+ << "kilo"
3092+ << "lima"
3093+ << "mike"
3094+ << "november"
3095+ << "oskar"
3096+ << "papa"
3097+ << "quebec"
3098+ << "romeo"
3099+ << "sierra"
3100+ << "tango"
3101+ << "uniform"
3102+ << "viktor"
3103+ << "whiskey"
3104+ << "xray"
3105+ << "yankee"
3106+ << "zulu");
3107+ QTest::newRow("case insensitive") << static_cast<int>(Qt::AscendingOrder)
3108+ << int(Qt::CaseInsensitive)
3109+ << (QStringList()
3110+ << "alpha" << "BETA" << "Gamma" << "delta")
3111+ << (QStringList()
3112+ << "alpha" << "BETA" << "delta" << "Gamma");
3113+ QTest::newRow("case sensitive") << static_cast<int>(Qt::AscendingOrder)
3114+ << int(Qt::CaseSensitive)
3115+ << (QStringList()
3116+ << "alpha" << "BETA" << "Gamma" << "delta")
3117+ << (QStringList()
3118+ << "BETA" << "Gamma" << "alpha" << "delta");
3119+
3120+ QStringList list;
3121+ for (int i = 10000; i < 20000; ++i)
3122+ list.append(QString("Number: %1").arg(i));
3123+ QTest::newRow("large set ascending") << static_cast<int>(Qt::AscendingOrder) << int(Qt::CaseSensitive) << list << list;
3124+}
3125+
3126+void tst_QSortFilterProxyModel::sort()
3127+{
3128+ QFETCH(int, sortOrder);
3129+ QFETCH(int, sortCaseSensitivity);
3130+ QFETCH(QStringList, initial);
3131+ QFETCH(QStringList, expected);
3132+
3133+ // prepare model
3134+ QStandardItem *root = m_model->invisibleRootItem ();
3135+ QList<QStandardItem *> items;
3136+ for (int i = 0; i < initial.count(); ++i) {
3137+ items.append(new QStandardItem(initial.at(i)));
3138+ }
3139+ root->insertRows(0, items);
3140+ QCOMPARE(m_model->rowCount(QModelIndex()), initial.count());
3141+ QCOMPARE(m_model->columnCount(QModelIndex()), 1);
3142+
3143+ // make sure the proxy is unsorted
3144+ QCOMPARE(m_proxy->columnCount(QModelIndex()), 1);
3145+ QCOMPARE(m_proxy->rowCount(QModelIndex()), initial.count());
3146+ for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
3147+ QModelIndex index = m_proxy->index(row, 0, QModelIndex());
3148+ QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(row));
3149+ }
3150+
3151+ // sort
3152+ m_proxy->sort(0, static_cast<Qt::SortOrder>(sortOrder));
3153+ m_proxy->setSortCaseSensitivity(static_cast<Qt::CaseSensitivity>(sortCaseSensitivity));
3154+
3155+ // make sure the model is unchanged
3156+ for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
3157+ QModelIndex index = m_model->index(row, 0, QModelIndex());
3158+ QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(row));
3159+ }
3160+ // make sure the proxy is sorted
3161+ for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
3162+ QModelIndex index = m_proxy->index(row, 0, QModelIndex());
3163+ QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), expected.at(row));
3164+ }
3165+
3166+ // restore the unsorted order
3167+ m_proxy->sort(-1);
3168+
3169+ // make sure the proxy is unsorted again
3170+ for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
3171+ QModelIndex index = m_proxy->index(row, 0, QModelIndex());
3172+ QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(row));
3173+ }
3174+}
3175+
3176+void tst_QSortFilterProxyModel::sortHierarchy_data()
3177+{
3178+ QTest::addColumn<int>("sortOrder");
3179+ QTest::addColumn<QStringList>("initial");
3180+ QTest::addColumn<QStringList>("expected");
3181+
3182+ QTest::newRow("flat ascending")
3183+ << static_cast<int>(Qt::AscendingOrder)
3184+ << (QStringList()
3185+ << "c" << "f" << "d" << "e" << "a" << "b")
3186+ << (QStringList()
3187+ << "a" << "b" << "c" << "d" << "e" << "f");
3188+
3189+ QTest::newRow("simple hierarchy")
3190+ << static_cast<int>(Qt::AscendingOrder)
3191+ << (QStringList() << "a" << "<" << "b" << "<" << "c" << ">" << ">")
3192+ << (QStringList() << "a" << "<" << "b" << "<" << "c" << ">" << ">");
3193+
3194+ QTest::newRow("hierarchical ascending")
3195+ << static_cast<int>(Qt::AscendingOrder)
3196+ << (QStringList()
3197+ << "c"
3198+ << "<"
3199+ << "h"
3200+ << "<"
3201+ << "2"
3202+ << "0"
3203+ << "1"
3204+ << ">"
3205+ << "g"
3206+ << "i"
3207+ << ">"
3208+ << "b"
3209+ << "<"
3210+ << "l"
3211+ << "k"
3212+ << "<"
3213+ << "8"
3214+ << "7"
3215+ << "9"
3216+ << ">"
3217+ << "m"
3218+ << ">"
3219+ << "a"
3220+ << "<"
3221+ << "z"
3222+ << "y"
3223+ << "x"
3224+ << ">")
3225+ << (QStringList()
3226+ << "a"
3227+ << "<"
3228+ << "x"
3229+ << "y"
3230+ << "z"
3231+ << ">"
3232+ << "b"
3233+ << "<"
3234+ << "k"
3235+ << "<"
3236+ << "7"
3237+ << "8"
3238+ << "9"
3239+ << ">"
3240+ << "l"
3241+ << "m"
3242+ << ">"
3243+ << "c"
3244+ << "<"
3245+ << "g"
3246+ << "h"
3247+ << "<"
3248+ << "0"
3249+ << "1"
3250+ << "2"
3251+ << ">"
3252+ << "i"
3253+ << ">");
3254+}
3255+
3256+void tst_QSortFilterProxyModel::sortHierarchy()
3257+{
3258+ QFETCH(int, sortOrder);
3259+ QFETCH(QStringList, initial);
3260+ QFETCH(QStringList, expected);
3261+
3262+ buildHierarchy(initial, m_model);
3263+ checkHierarchy(initial, m_model);
3264+ checkHierarchy(initial, m_proxy);
3265+ m_proxy->sort(0, static_cast<Qt::SortOrder>(sortOrder));
3266+ checkHierarchy(initial, m_model);
3267+ checkHierarchy(expected, m_proxy);
3268+}
3269+
3270+void tst_QSortFilterProxyModel::insertRows_data()
3271+{
3272+ QTest::addColumn<QStringList>("initial");
3273+ QTest::addColumn<QStringList>("expected");
3274+ QTest::addColumn<QStringList>("insert");
3275+ QTest::addColumn<int>("position");
3276+
3277+ QTest::newRow("insert one row in the middle")
3278+ << (QStringList()
3279+ << "One"
3280+ << "Two"
3281+ << "Four"
3282+ << "Five")
3283+ << (QStringList()
3284+ << "One"
3285+ << "Two"
3286+ << "Three"
3287+ << "Four"
3288+ << "Five")
3289+ << (QStringList()
3290+ << "Three")
3291+ << 2;
3292+
3293+ QTest::newRow("insert one row in the beginning")
3294+ << (QStringList()
3295+ << "Two"
3296+ << "Three"
3297+ << "Four"
3298+ << "Five")
3299+ << (QStringList()
3300+ << "One"
3301+ << "Two"
3302+ << "Three"
3303+ << "Four"
3304+ << "Five")
3305+ << (QStringList()
3306+ << "One")
3307+ << 0;
3308+
3309+ QTest::newRow("insert one row in the end")
3310+ << (QStringList()
3311+ << "One"
3312+ << "Two"
3313+ << "Three"
3314+ << "Four")
3315+ << (QStringList()
3316+ << "One"
3317+ << "Two"
3318+ << "Three"
3319+ << "Four"
3320+ << "Five")
3321+ << (QStringList()
3322+ <<"Five")
3323+ << 4;
3324+}
3325+
3326+void tst_QSortFilterProxyModel::insertRows()
3327+{
3328+ QFETCH(QStringList, initial);
3329+ QFETCH(QStringList, expected);
3330+ QFETCH(QStringList, insert);
3331+ QFETCH(int, position);
3332+ // prepare model
3333+ m_model->insertRows(0, initial.count(), QModelIndex());
3334+ //m_model->insertColumns(0, 1, QModelIndex());
3335+ QCOMPARE(m_model->columnCount(QModelIndex()), 1);
3336+ QCOMPARE(m_model->rowCount(QModelIndex()), initial.count());
3337+ for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
3338+ QModelIndex index = m_model->index(row, 0, QModelIndex());
3339+ m_model->setData(index, initial.at(row), Qt::DisplayRole);
3340+ }
3341+ // make sure the model correct before insert
3342+ for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
3343+ QModelIndex index = m_model->index(row, 0, QModelIndex());
3344+ QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(row));
3345+ }
3346+ // make sure the proxy is correct before insert
3347+ for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
3348+ QModelIndex index = m_proxy->index(row, 0, QModelIndex());
3349+ QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(row));
3350+ }
3351+
3352+ // insert the row
3353+ m_proxy->insertRows(position, insert.count(), QModelIndex());
3354+ QCOMPARE(m_model->rowCount(QModelIndex()), expected.count());
3355+ QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.count());
3356+
3357+ // set the data for the inserted row
3358+ for (int i = 0; i < insert.count(); ++i) {
3359+ QModelIndex index = m_proxy->index(position + i, 0, QModelIndex());
3360+ m_proxy->setData(index, insert.at(i), Qt::DisplayRole);
3361+ }
3362+
3363+ // make sure the model correct after insert
3364+ for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
3365+ QModelIndex index = m_model->index(row, 0, QModelIndex());
3366+ QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), expected.at(row));
3367+ }
3368+
3369+ // make sure the proxy is correct after insert
3370+ for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
3371+ QModelIndex index = m_proxy->index(row, 0, QModelIndex());
3372+ QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), expected.at(row));
3373+ }
3374+}
3375+
3376+void tst_QSortFilterProxyModel::prependRow()
3377+{
3378+ //this tests that data is correctly handled by the sort filter when prepending a row
3379+ QStandardItemModel model;
3380+ QSortFilterProxyModel proxy;
3381+ proxy.setSourceModel(&model);
3382+
3383+ QStandardItem item("root");
3384+ model.appendRow(&item);
3385+
3386+ QStandardItem sub("sub");
3387+ item.appendRow(&sub);
3388+
3389+ sub.appendRow(new QStandardItem("test1"));
3390+ sub.appendRow(new QStandardItem("test2"));
3391+
3392+ QStandardItem sub2("sub2");
3393+ sub2.appendRow(new QStandardItem("sub3"));
3394+ item.insertRow(0, &sub2);
3395+
3396+ QModelIndex index_sub2 = proxy.mapFromSource(model.indexFromItem(&sub2));
3397+
3398+ QCOMPARE(sub2.rowCount(), proxy.rowCount(index_sub2));
3399+ QCOMPARE(proxy.rowCount(QModelIndex()), 1); //only the "root" item is there
3400+}
3401+
3402+void tst_QSortFilterProxyModel::removeRows_data()
3403+{
3404+ QTest::addColumn<QStringList>("initial");
3405+ QTest::addColumn<int>("sortOrder");
3406+ QTest::addColumn<QString>("filter");
3407+ QTest::addColumn<int>("position");
3408+ QTest::addColumn<int>("count");
3409+ QTest::addColumn<bool>("success");
3410+ QTest::addColumn<QStringList>("expectedProxy");
3411+ QTest::addColumn<QStringList>("expectedSource");
3412+
3413+ QTest::newRow("remove one row in the middle [no sorting/filter]")
3414+ << (QStringList()
3415+ << "One"
3416+ << "Two"
3417+ << "Three"
3418+ << "Four"
3419+ << "Five")
3420+ << -1 // no sorting
3421+ << QString() // no filter
3422+ << 2 // position
3423+ << 1 // count
3424+ << true // success
3425+ << (QStringList() // expectedProxy
3426+ << "One"
3427+ << "Two"
3428+ << "Four"
3429+ << "Five")
3430+ << (QStringList() // expectedSource
3431+ << "One"
3432+ << "Two"
3433+ << "Four"
3434+ << "Five");
3435+
3436+ QTest::newRow("remove one row in the beginning [no sorting/filter]")
3437+ << (QStringList()
3438+ << "One"
3439+ << "Two"
3440+ << "Three"
3441+ << "Four"
3442+ << "Five")
3443+ << -1 // no sorting
3444+ << QString() // no filter
3445+ << 0 // position
3446+ << 1 // count
3447+ << true // success
3448+ << (QStringList() // expectedProxy
3449+ << "Two"
3450+ << "Three"
3451+ << "Four"
3452+ << "Five")
3453+ << (QStringList() // expectedSource
3454+ << "Two"
3455+ << "Three"
3456+ << "Four"
3457+ << "Five");
3458+
3459+ QTest::newRow("remove one row in the end [no sorting/filter]")
3460+ << (QStringList()
3461+ << "One"
3462+ << "Two"
3463+ << "Three"
3464+ << "Four"
3465+ << "Five")
3466+ << -1 // no sorting
3467+ << QString() // no filter
3468+ << 4 // position
3469+ << 1 // count
3470+ << true // success
3471+ << (QStringList() // expectedProxy
3472+ << "One"
3473+ << "Two"
3474+ << "Three"
3475+ << "Four")
3476+ << (QStringList() // expectedSource
3477+ << "One"
3478+ << "Two"
3479+ << "Three"
3480+ << "Four");
3481+
3482+ QTest::newRow("remove all [no sorting/filter]")
3483+ << (QStringList()
3484+ << "One"
3485+ << "Two"
3486+ << "Three"
3487+ << "Four"
3488+ << "Five")
3489+ << -1 // no sorting
3490+ << QString() // no filter
3491+ << 0 // position
3492+ << 5 // count
3493+ << true // success
3494+ << QStringList() // expectedProxy
3495+ << QStringList(); // expectedSource
3496+
3497+ QTest::newRow("remove one row past the end [no sorting/filter]")
3498+ << (QStringList()
3499+ << "One"
3500+ << "Two"
3501+ << "Three"
3502+ << "Four"
3503+ << "Five")
3504+ << -1 // no sorting
3505+ << QString() // no filter
3506+ << 5 // position
3507+ << 1 // count
3508+ << false // success
3509+ << (QStringList() // expectedProxy
3510+ << "One"
3511+ << "Two"
3512+ << "Three"
3513+ << "Four"
3514+ << "Five")
3515+ << (QStringList() // expectedSource
3516+ << "One"
3517+ << "Two"
3518+ << "Three"
3519+ << "Four"
3520+ << "Five");
3521+
3522+ QTest::newRow("remove row -1 [no sorting/filter]")
3523+ << (QStringList()
3524+ << "One"
3525+ << "Two"
3526+ << "Three"
3527+ << "Four"
3528+ << "Five")
3529+ << -1 // no sorting
3530+ << QString() // no filter
3531+ << -1 // position
3532+ << 1 // count
3533+ << false // success
3534+ << (QStringList() // expectedProxy
3535+ << "One"
3536+ << "Two"
3537+ << "Three"
3538+ << "Four"
3539+ << "Five")
3540+ << (QStringList() // expectedSource
3541+ << "One"
3542+ << "Two"
3543+ << "Three"
3544+ << "Four"
3545+ << "Five");
3546+
3547+ QTest::newRow("remove three rows in the middle [no sorting/filter]")
3548+ << (QStringList()
3549+ << "One"
3550+ << "Two"
3551+ << "Three"
3552+ << "Four"
3553+ << "Five")
3554+ << -1 // no sorting
3555+ << QString() // no filter
3556+ << 1 // position
3557+ << 3 // count
3558+ << true // success
3559+ << (QStringList() // expectedProxy
3560+ << "One"
3561+ << "Five")
3562+ << (QStringList() // expectedSource
3563+ << "One"
3564+ << "Five");
3565+
3566+ QTest::newRow("remove one row in the middle [ascending sorting, no filter]")
3567+ << (QStringList()
3568+ << "1"
3569+ << "5"
3570+ << "2"
3571+ << "4"
3572+ << "3")
3573+ << static_cast<int>(Qt::AscendingOrder)
3574+ << QString() // no filter
3575+ << 2 // position
3576+ << 1 // count
3577+ << true // success
3578+ << (QStringList() // expectedProxy
3579+ << "1"
3580+ << "2"
3581+ << "4"
3582+ << "5")
3583+ << (QStringList() // expectedSource
3584+ << "1"
3585+ << "5"
3586+ << "2"
3587+ << "4");
3588+
3589+ QTest::newRow("remove two rows in the middle [ascending sorting, no filter]")
3590+ << (QStringList()
3591+ << "1"
3592+ << "5"
3593+ << "2"
3594+ << "4"
3595+ << "3")
3596+ << static_cast<int>(Qt::AscendingOrder)
3597+ << QString() // no filter
3598+ << 2 // position
3599+ << 2 // count
3600+ << true // success
3601+ << (QStringList() // expectedProxy
3602+ << "1"
3603+ << "2"
3604+ << "5")
3605+ << (QStringList() // expectedSource
3606+ << "1"
3607+ << "5"
3608+ << "2");
3609+
3610+ QTest::newRow("remove two rows in the middle [descending sorting, no filter]")
3611+ << (QStringList()
3612+ << "1"
3613+ << "5"
3614+ << "2"
3615+ << "4"
3616+ << "3")
3617+ << static_cast<int>(Qt::DescendingOrder)
3618+ << QString() // no filter
3619+ << 2 // position
3620+ << 2 // count
3621+ << true // success
3622+ << (QStringList() // expectedProxy
3623+ << "5"
3624+ << "4"
3625+ << "1")
3626+ << (QStringList() // expectedSource
3627+ << "1"
3628+ << "5"
3629+ << "4");
3630+
3631+ QTest::newRow("remove one row in the middle [no sorting, filter=5|2|3]")
3632+ << (QStringList()
3633+ << "1"
3634+ << "5"
3635+ << "2"
3636+ << "4"
3637+ << "3")
3638+ << -1 // no sorting
3639+ << QString("5|2|3")
3640+ << 1 // position
3641+ << 1 // count
3642+ << true // success
3643+ << (QStringList() // expectedProxy
3644+ << "5"
3645+ << "3")
3646+ << (QStringList() // expectedSource
3647+ << "1"
3648+ << "5"
3649+ << "4"
3650+ << "3");
3651+
3652+ QTest::newRow("remove all [ascending sorting, no filter]")
3653+ << (QStringList()
3654+ << "1"
3655+ << "5"
3656+ << "2"
3657+ << "4"
3658+ << "3")
3659+ << static_cast<int>(Qt::AscendingOrder)
3660+ << QString() // no filter
3661+ << 0 // position
3662+ << 5 // count
3663+ << true // success
3664+ << QStringList() // expectedProxy
3665+ << QStringList(); // expectedSource
3666+}
3667+
3668+void tst_QSortFilterProxyModel::removeRows()
3669+{
3670+ QFETCH(QStringList, initial);
3671+ QFETCH(int, sortOrder);
3672+ QFETCH(QString, filter);
3673+ QFETCH(int, position);
3674+ QFETCH(int, count);
3675+ QFETCH(bool, success);
3676+ QFETCH(QStringList, expectedProxy);
3677+ QFETCH(QStringList, expectedSource);
3678+
3679+ QStandardItemModel model;
3680+ QSortFilterProxyModel proxy;
3681+ proxy.setSourceModel(&model);
3682+
3683+ // prepare model
3684+ foreach (QString s, initial)
3685+ model.appendRow(new QStandardItem(s));
3686+
3687+ if (sortOrder != -1)
3688+ proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
3689+ if (!filter.isEmpty())
3690+ proxy.setFilterRegExp(QRegExp(filter));
3691+
3692+ // remove the rows
3693+ QCOMPARE(proxy.removeRows(position, count, QModelIndex()), success);
3694+ QCOMPARE(model.rowCount(QModelIndex()), expectedSource.count());
3695+ QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxy.count());
3696+
3697+ // make sure the model is correct after remove
3698+ for (int row = 0; row < model.rowCount(QModelIndex()); ++row)
3699+ QCOMPARE(model.item(row)->text(), expectedSource.at(row));
3700+
3701+ // make sure the proxy is correct after remove
3702+ for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) {
3703+ QModelIndex index = proxy.index(row, 0, QModelIndex());
3704+ QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), expectedProxy.at(row));
3705+ }
3706+}
3707+
3708+class MyFilteredColumnProxyModel : public QSortFilterProxyModel
3709+{
3710+ Q_OBJECT
3711+public:
3712+ MyFilteredColumnProxyModel(QObject *parent = 0)
3713+ : QSortFilterProxyModel(parent) { }
3714+protected:
3715+ bool filterAcceptsColumn(int sourceColumn, const QModelIndex &) const
3716+ {
3717+ QString key = sourceModel()->headerData(sourceColumn, Qt::Horizontal).toString();
3718+ return key.contains(filterRegExp());
3719+ }
3720+};
3721+
3722+void tst_QSortFilterProxyModel::removeColumns_data()
3723+{
3724+ QTest::addColumn<QStringList>("initial");
3725+ QTest::addColumn<QString>("filter");
3726+ QTest::addColumn<int>("position");
3727+ QTest::addColumn<int>("count");
3728+ QTest::addColumn<bool>("success");
3729+ QTest::addColumn<QStringList>("expectedProxy");
3730+ QTest::addColumn<QStringList>("expectedSource");
3731+
3732+ QTest::newRow("remove one column in the middle [no filter]")
3733+ << (QStringList()
3734+ << "1"
3735+ << "2"
3736+ << "3"
3737+ << "4"
3738+ << "5")
3739+ << QString() // no filter
3740+ << 2 // position
3741+ << 1 // count
3742+ << true // success
3743+ << (QStringList() // expectedProxy
3744+ << "1"
3745+ << "2"
3746+ << "4"
3747+ << "5")
3748+ << (QStringList() // expectedSource
3749+ << "1"
3750+ << "2"
3751+ << "4"
3752+ << "5");
3753+
3754+ QTest::newRow("remove one column in the end [no filter]")
3755+ << (QStringList()
3756+ << "1"
3757+ << "2"
3758+ << "3"
3759+ << "4"
3760+ << "5")
3761+ << QString() // no filter
3762+ << 4 // position
3763+ << 1 // count
3764+ << true // success
3765+ << (QStringList() // expectedProxy
3766+ << "1"
3767+ << "2"
3768+ << "3"
3769+ << "4")
3770+ << (QStringList() // expectedSource
3771+ << "1"
3772+ << "2"
3773+ << "3"
3774+ << "4");
3775+
3776+ QTest::newRow("remove one column past the end [no filter]")
3777+ << (QStringList()
3778+ << "1"
3779+ << "2"
3780+ << "3"
3781+ << "4"
3782+ << "5")
3783+ << QString() // no filter
3784+ << 5 // position
3785+ << 1 // count
3786+ << false // success
3787+ << (QStringList() // expectedProxy
3788+ << "1"
3789+ << "2"
3790+ << "3"
3791+ << "4"
3792+ << "5")
3793+ << (QStringList() // expectedSource
3794+ << "1"
3795+ << "2"
3796+ << "3"
3797+ << "4"
3798+ << "5");
3799+
3800+ QTest::newRow("remove column -1 [no filter]")
3801+ << (QStringList()
3802+ << "1"
3803+ << "2"
3804+ << "3"
3805+ << "4"
3806+ << "5")
3807+ << QString() // no filter
3808+ << -1 // position
3809+ << 1 // count
3810+ << false // success
3811+ << (QStringList() // expectedProxy
3812+ << "1"
3813+ << "2"
3814+ << "3"
3815+ << "4"
3816+ << "5")
3817+ << (QStringList() // expectedSource
3818+ << "1"
3819+ << "2"
3820+ << "3"
3821+ << "4"
3822+ << "5");
3823+
3824+ QTest::newRow("remove all columns [no filter]")
3825+ << (QStringList()
3826+ << "1"
3827+ << "2"
3828+ << "3"
3829+ << "4"
3830+ << "5")
3831+ << QString() // no filter
3832+ << 0 // position
3833+ << 5 // count
3834+ << true // success
3835+ << QStringList() // expectedProxy
3836+ << QStringList(); // expectedSource
3837+
3838+ QTest::newRow("remove one column in the middle [filter=1|3|5]")
3839+ << (QStringList()
3840+ << "1"
3841+ << "2"
3842+ << "3"
3843+ << "4"
3844+ << "5")
3845+ << QString("1|3|5")
3846+ << 1 // position
3847+ << 1 // count
3848+ << true // success
3849+ << (QStringList() // expectedProxy
3850+ << "1"
3851+ << "5")
3852+ << (QStringList() // expectedSource
3853+ << "1"
3854+ << "2"
3855+ << "4"
3856+ << "5");
3857+
3858+ QTest::newRow("remove one column in the end [filter=1|3|5]")
3859+ << (QStringList()
3860+ << "1"
3861+ << "2"
3862+ << "3"
3863+ << "4"
3864+ << "5")
3865+ << QString("1|3|5")
3866+ << 2 // position
3867+ << 1 // count
3868+ << true // success
3869+ << (QStringList() // expectedProxy
3870+ << "1"
3871+ << "3")
3872+ << (QStringList() // expectedSource
3873+ << "1"
3874+ << "2"
3875+ << "3"
3876+ << "4");
3877+
3878+ QTest::newRow("remove one column past the end [filter=1|3|5]")
3879+ << (QStringList()
3880+ << "1"
3881+ << "2"
3882+ << "3"
3883+ << "4"
3884+ << "5")
3885+ << QString("1|3|5")
3886+ << 3 // position
3887+ << 1 // count
3888+ << false // success
3889+ << (QStringList() // expectedProxy
3890+ << "1"
3891+ << "3"
3892+ << "5")
3893+ << (QStringList() // expectedSource
3894+ << "1"
3895+ << "2"
3896+ << "3"
3897+ << "4"
3898+ << "5");
3899+
3900+ QTest::newRow("remove all columns [filter=1|3|5]")
3901+ << (QStringList()
3902+ << "1"
3903+ << "2"
3904+ << "3"
3905+ << "4"
3906+ << "5")
3907+ << QString("1|3|5")
3908+ << 0 // position
3909+ << 3 // count
3910+ << true // success
3911+ << QStringList() // expectedProxy
3912+ << (QStringList() // expectedSource
3913+ << "2"
3914+ << "4");
3915+}
3916+
3917+void tst_QSortFilterProxyModel::removeColumns()
3918+{
3919+ QFETCH(QStringList, initial);
3920+ QFETCH(QString, filter);
3921+ QFETCH(int, position);
3922+ QFETCH(int, count);
3923+ QFETCH(bool, success);
3924+ QFETCH(QStringList, expectedProxy);
3925+ QFETCH(QStringList, expectedSource);
3926+
3927+ QStandardItemModel model;
3928+ MyFilteredColumnProxyModel proxy;
3929+ proxy.setSourceModel(&model);
3930+ if (!filter.isEmpty())
3931+ proxy.setFilterRegExp(QRegExp(filter));
3932+
3933+ // prepare model
3934+ model.setHorizontalHeaderLabels(initial);
3935+
3936+ // remove the columns
3937+ QCOMPARE(proxy.removeColumns(position, count, QModelIndex()), success);
3938+ QCOMPARE(model.columnCount(QModelIndex()), expectedSource.count());
3939+ QCOMPARE(proxy.columnCount(QModelIndex()), expectedProxy.count());
3940+
3941+ // make sure the model is correct after remove
3942+ for (int col = 0; col < model.columnCount(QModelIndex()); ++col)
3943+ QCOMPARE(model.horizontalHeaderItem(col)->text(), expectedSource.at(col));
3944+
3945+ // make sure the proxy is correct after remove
3946+ for (int col = 0; col < proxy.columnCount(QModelIndex()); ++col) {
3947+ QCOMPARE(proxy.headerData(col, Qt::Horizontal, Qt::DisplayRole).toString(),
3948+ expectedProxy.at(col));
3949+ }
3950+}
3951+
3952+void tst_QSortFilterProxyModel::filterColumns_data()
3953+{
3954+ QTest::addColumn<QString>("pattern");
3955+ QTest::addColumn<QStringList>("initial");
3956+ QTest::addColumn<bool>("data");
3957+
3958+ QTest::newRow("all") << "a"
3959+ << (QStringList()
3960+ << "delta"
3961+ << "yankee"
3962+ << "bravo"
3963+ << "lima")
3964+ << true;
3965+
3966+ QTest::newRow("some") << "lie"
3967+ << (QStringList()
3968+ << "charlie"
3969+ << "juliet"
3970+ << "tango"
3971+ << "hotel")
3972+ << true;
3973+
3974+ QTest::newRow("nothing") << "zoo"
3975+ << (QStringList()
3976+ << "foxtrot"
3977+ << "uniform"
3978+ << "alpha"
3979+ << "golf")
3980+ << false;
3981+}
3982+
3983+void tst_QSortFilterProxyModel::filterColumns()
3984+{
3985+ QFETCH(QString, pattern);
3986+ QFETCH(QStringList, initial);
3987+ QFETCH(bool, data);
3988+ // prepare model
3989+ m_model->setColumnCount(initial.count());
3990+ m_model->setRowCount(1);
3991+ QCOMPARE(m_model->columnCount(QModelIndex()), initial.count());
3992+ QCOMPARE(m_model->rowCount(QModelIndex()), 1);
3993+ // set data
3994+ QCOMPARE(m_model->rowCount(QModelIndex()), 1);
3995+ for (int col = 0; col < m_model->columnCount(QModelIndex()); ++col) {
3996+ QModelIndex index = m_model->index(0, col, QModelIndex());
3997+ m_model->setData(index, initial.at(col), Qt::DisplayRole);
3998+ }
3999+ m_proxy->setFilterRegExp(pattern);
4000+ m_proxy->setFilterKeyColumn(-1);
4001+ // make sure the model is unchanged
4002+ for (int col = 0; col < m_model->columnCount(QModelIndex()); ++col) {
4003+ QModelIndex index = m_model->index(0, col, QModelIndex());
4004+ QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(col));
4005+ }
4006+ // make sure the proxy is filtered
4007+ QModelIndex index = m_proxy->index(0, 0, QModelIndex());
4008+ QCOMPARE(index.isValid(), data);
4009+}
4010+
4011+void tst_QSortFilterProxyModel::filter_data()
4012+{
4013+ QTest::addColumn<QString>("pattern");
4014+ QTest::addColumn<QStringList>("initial");
4015+ QTest::addColumn<QStringList>("expected");
4016+
4017+ QTest::newRow("flat") << "e"
4018+ << (QStringList()
4019+ << "delta"
4020+ << "yankee"
4021+ << "bravo"
4022+ << "lima"
4023+ << "charlie"
4024+ << "juliet"
4025+ << "tango"
4026+ << "hotel"
4027+ << "uniform"
4028+ << "alpha"
4029+ << "echo"
4030+ << "golf"
4031+ << "quebec"
4032+ << "foxtrot"
4033+ << "india"
4034+ << "romeo"
4035+ << "november"
4036+ << "oskar"
4037+ << "zulu"
4038+ << "kilo"
4039+ << "whiskey"
4040+ << "mike"
4041+ << "papa"
4042+ << "sierra"
4043+ << "xray"
4044+ << "viktor")
4045+ << (QStringList()
4046+ << "delta"
4047+ << "yankee"
4048+ << "charlie"
4049+ << "juliet"
4050+ << "hotel"
4051+ << "echo"
4052+ << "quebec"
4053+ << "romeo"
4054+ << "november"
4055+ << "whiskey"
4056+ << "mike"
4057+ << "sierra");
4058+}
4059+
4060+void tst_QSortFilterProxyModel::filter()
4061+{
4062+ QFETCH(QString, pattern);
4063+ QFETCH(QStringList, initial);
4064+ QFETCH(QStringList, expected);
4065+ // prepare model
4066+ QVERIFY(m_model->insertRows(0, initial.count(), QModelIndex()));
4067+ QCOMPARE(m_model->rowCount(QModelIndex()), initial.count());
4068+ // set data
4069+ QCOMPARE(m_model->columnCount(QModelIndex()), 1);
4070+ for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
4071+ QModelIndex index = m_model->index(row, 0, QModelIndex());
4072+ m_model->setData(index, initial.at(row), Qt::DisplayRole);
4073+ }
4074+ m_proxy->setFilterRegExp(pattern);
4075+ // make sure the proxy is unfiltered
4076+ QCOMPARE(m_proxy->columnCount(QModelIndex()), 1);
4077+ QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.count());
4078+ // make sure the model is unchanged
4079+ for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
4080+ QModelIndex index = m_model->index(row, 0, QModelIndex());
4081+ QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(row));
4082+ }
4083+ // make sure the proxy is filtered
4084+ for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
4085+ QModelIndex index = m_proxy->index(row, 0, QModelIndex());
4086+ QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), expected.at(row));
4087+ }
4088+}
4089+
4090+void tst_QSortFilterProxyModel::filterHierarchy_data()
4091+{
4092+ QTest::addColumn<QString>("pattern");
4093+ QTest::addColumn<QStringList>("initial");
4094+ QTest::addColumn<QStringList>("expected");
4095+
4096+ QTest::newRow("flat") << ".*oo"
4097+ << (QStringList()
4098+ << "foo" << "boo" << "baz" << "moo" << "laa" << "haa")
4099+ << (QStringList()
4100+ << "foo" << "boo" << "moo");
4101+
4102+ QTest::newRow("simple hierarchy") << "b.*z"
4103+ << (QStringList() << "baz" << "<" << "boz" << "<" << "moo" << ">" << ">")
4104+ << (QStringList() << "baz" << "<" << "boz" << ">");
4105+}
4106+
4107+void tst_QSortFilterProxyModel::filterHierarchy()
4108+{
4109+ QFETCH(QString, pattern);
4110+ QFETCH(QStringList, initial);
4111+ QFETCH(QStringList, expected);
4112+ buildHierarchy(initial, m_model);
4113+ m_proxy->setFilterRegExp(pattern);
4114+ checkHierarchy(initial, m_model);
4115+ checkHierarchy(expected, m_proxy);
4116+}
4117+
4118+void tst_QSortFilterProxyModel::buildHierarchy(const QStringList &l, QAbstractItemModel *m)
4119+{
4120+ int ind = 0;
4121+ int row = 0;
4122+ QStack<int> row_stack;
4123+ QModelIndex parent;
4124+ QStack<QModelIndex> parent_stack;
4125+ for (int i = 0; i < l.count(); ++i) {
4126+ QString token = l.at(i);
4127+ if (token == "<") { // start table
4128+ ++ind;
4129+ parent_stack.push(parent);
4130+ row_stack.push(row);
4131+ parent = m->index(row - 1, 0, parent);
4132+ row = 0;
4133+ QVERIFY(m->insertColumns(0, 1, parent)); // add column
4134+ } else if (token == ">") { // end table
4135+ --ind;
4136+ parent = parent_stack.pop();
4137+ row = row_stack.pop();
4138+ } else { // append row
4139+ QVERIFY(m->insertRows(row, 1, parent));
4140+ QModelIndex index = m->index(row, 0, parent);
4141+ QVERIFY(index.isValid());
4142+ m->setData(index, token, Qt::DisplayRole);
4143+ ++row;
4144+ }
4145+ }
4146+}
4147+
4148+void tst_QSortFilterProxyModel::checkHierarchy(const QStringList &l, const QAbstractItemModel *m)
4149+{
4150+ int row = 0;
4151+ int indent = 0;
4152+ QStack<int> row_stack;
4153+ QModelIndex parent;
4154+ QStack<QModelIndex> parent_stack;
4155+ for (int i = 0; i < l.count(); ++i) {
4156+ QString token = l.at(i);
4157+ if (token == "<") { // start table
4158+ ++indent;
4159+ parent_stack.push(parent);
4160+ row_stack.push(row);
4161+ parent = m->index(row - 1, 0, parent);
4162+ QVERIFY(parent.isValid());
4163+ row = 0;
4164+ } else if (token == ">") { // end table
4165+ --indent;
4166+ parent = parent_stack.pop();
4167+ row = row_stack.pop();
4168+ } else { // compare row
4169+ QModelIndex index = m->index(row, 0, parent);
4170+ QVERIFY(index.isValid());
4171+ QString str = m->data(index, Qt::DisplayRole).toString();
4172+ QCOMPARE(str, token);
4173+ ++row;
4174+ }
4175+ }
4176+}
4177+
4178+class TestModel: public QAbstractTableModel
4179+{
4180+public:
4181+ int rowCount(const QModelIndex &) const { return 10000; }
4182+ int columnCount(const QModelIndex &) const { return 1; }
4183+ QVariant data(const QModelIndex &index, int role) const
4184+ {
4185+ if (role != Qt::DisplayRole)
4186+ return QVariant();
4187+ return QString::number(index.row());
4188+ }
4189+};
4190+
4191+void tst_QSortFilterProxyModel::filterTable()
4192+{
4193+ TestModel model;
4194+ QSortFilterProxyModel filter;
4195+ filter.setSourceModel(&model);
4196+ filter.setFilterRegExp("9");
4197+
4198+ for (int i = 0; i < filter.rowCount(); ++i)
4199+ QVERIFY(filter.data(filter.index(i, 0)).toString().contains("9"));
4200+}
4201+
4202+void tst_QSortFilterProxyModel::insertAfterSelect()
4203+{
4204+ QStandardItemModel model(10, 2);
4205+ for (int i = 0; i<10;i++)
4206+ model.setData(model.index(i, 0), QVariant(i));
4207+ QSortFilterProxyModel filter;
4208+ filter.setSourceModel(&model);
4209+ QTreeView view;
4210+ view.setModel(&filter);
4211+ view.show();
4212+ QModelIndex firstIndex = filter.mapFromSource(model.index(0, 0, QModelIndex()));
4213+ QCOMPARE(firstIndex.model(), (const QAbstractItemModel *)view.model());
4214+ QVERIFY(firstIndex.isValid());
4215+ int itemOffset = view.visualRect(firstIndex).width() / 2;
4216+ QPoint p(itemOffset, 1);
4217+ QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);
4218+ QVERIFY(view.selectionModel()->selectedIndexes().size() > 0);
4219+ model.insertRows(5, 1, QModelIndex());
4220+ QVERIFY(view.selectionModel()->selectedIndexes().size() > 0); // Should still have a selection
4221+}
4222+
4223+void tst_QSortFilterProxyModel::removeAfterSelect()
4224+{
4225+ QStandardItemModel model(10, 2);
4226+ for (int i = 0; i<10;i++)
4227+ model.setData(model.index(i, 0), QVariant(i));
4228+ QSortFilterProxyModel filter;
4229+ filter.setSourceModel(&model);
4230+ QTreeView view;
4231+ view.setModel(&filter);
4232+ view.show();
4233+ QModelIndex firstIndex = filter.mapFromSource(model.index(0, 0, QModelIndex()));
4234+ QCOMPARE(firstIndex.model(), (const QAbstractItemModel *)view.model());
4235+ QVERIFY(firstIndex.isValid());
4236+ int itemOffset = view.visualRect(firstIndex).width() / 2;
4237+ QPoint p(itemOffset, 1);
4238+ QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);
4239+ QVERIFY(view.selectionModel()->selectedIndexes().size() > 0);
4240+ model.removeRows(5, 1, QModelIndex());
4241+ QVERIFY(view.selectionModel()->selectedIndexes().size() > 0); // Should still have a selection
4242+}
4243+
4244+void tst_QSortFilterProxyModel::filterCurrent()
4245+{
4246+ QStandardItemModel model(2, 1);
4247+ model.setData(model.index(0, 0), QString("AAA"));
4248+ model.setData(model.index(1, 0), QString("BBB"));
4249+ QSortFilterProxyModel proxy;
4250+ proxy.setSourceModel(&model);
4251+ QTreeView view;
4252+
4253+ view.show();
4254+ view.setModel(&proxy);
4255+ QSignalSpy spy(view.selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)));
4256+ QVERIFY(spy.isValid());
4257+
4258+ view.setCurrentIndex(proxy.index(0, 0));
4259+ QCOMPARE(spy.count(), 1);
4260+ proxy.setFilterRegExp(QRegExp("^B"));
4261+ QCOMPARE(spy.count(), 2);
4262+}
4263+
4264+void tst_QSortFilterProxyModel::changeSourceLayout()
4265+{
4266+ QStandardItemModel model(2, 1);
4267+ model.setData(model.index(0, 0), QString("b"));
4268+ model.setData(model.index(1, 0), QString("a"));
4269+ QSortFilterProxyModel proxy;
4270+ proxy.setSourceModel(&model);
4271+
4272+ QList<QPersistentModelIndex> persistentSourceIndexes;
4273+ QList<QPersistentModelIndex> persistentProxyIndexes;
4274+ for (int row = 0; row < model.rowCount(); ++row) {
4275+ persistentSourceIndexes.append(model.index(row, 0));
4276+ persistentProxyIndexes.append(proxy.index(row, 0));
4277+ }
4278+
4279+ // change layout of source model
4280+ model.sort(0, Qt::AscendingOrder);
4281+
4282+ for (int row = 0; row < model.rowCount(); ++row) {
4283+ QCOMPARE(persistentProxyIndexes.at(row).row(),
4284+ persistentSourceIndexes.at(row).row());
4285+ }
4286+}
4287+
4288+void tst_QSortFilterProxyModel::removeSourceRows_data()
4289+{
4290+ QTest::addColumn<QStringList>("sourceItems");
4291+ QTest::addColumn<int>("start");
4292+ QTest::addColumn<int>("count");
4293+ QTest::addColumn<int>("sortOrder");
4294+ QTest::addColumn<IntPairList>("expectedRemovedProxyIntervals");
4295+ QTest::addColumn<QStringList>("expectedProxyItems");
4296+
4297+ QTest::newRow("remove one, no sorting")
4298+ << (QStringList() << "a" << "b") // sourceItems
4299+ << 0 // start
4300+ << 1 // count
4301+ << -1 // sortOrder (no sorting)
4302+ << (IntPairList() << IntPair(0, 0)) // expectedRemovedIntervals
4303+ << (QStringList() << "b") // expectedProxyItems
4304+ ;
4305+ QTest::newRow("remove one, ascending sort (same order)")
4306+ << (QStringList() << "a" << "b") // sourceItems
4307+ << 0 // start
4308+ << 1 // count
4309+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4310+ << (IntPairList() << IntPair(0, 0)) // expectedRemovedIntervals
4311+ << (QStringList() << "b") // expectedProxyItems
4312+ ;
4313+ QTest::newRow("remove one, ascending sort (reverse order)")
4314+ << (QStringList() << "b" << "a") // sourceItems
4315+ << 0 // start
4316+ << 1 // count
4317+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4318+ << (IntPairList() << IntPair(1, 1)) // expectedRemovedIntervals
4319+ << (QStringList() << "a") // expectedProxyItems
4320+ ;
4321+ QTest::newRow("remove two, multiple proxy intervals")
4322+ << (QStringList() << "c" << "d" << "a" << "b") // sourceItems
4323+ << 1 // start
4324+ << 2 // count
4325+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4326+ << (IntPairList() << IntPair(3, 3) << IntPair(0, 0)) // expectedRemovedIntervals
4327+ << (QStringList() << "b" << "c") // expectedProxyItems
4328+ ;
4329+ QTest::newRow("remove three, multiple proxy intervals")
4330+ << (QStringList() << "b" << "d" << "f" << "a" << "c" << "e") // sourceItems
4331+ << 3 // start
4332+ << 3 // count
4333+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4334+ << (IntPairList() << IntPair(4, 4) << IntPair(2, 2) << IntPair(0, 0)) // expectedRemovedIntervals
4335+ << (QStringList() << "b" << "d" << "f") // expectedProxyItems
4336+ ;
4337+ QTest::newRow("remove all, single proxy intervals")
4338+ << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // sourceItems
4339+ << 0 // start
4340+ << 6 // count
4341+ << static_cast<int>(Qt::DescendingOrder) // sortOrder
4342+ << (IntPairList() << IntPair(0, 5)) // expectedRemovedIntervals
4343+ << QStringList() // expectedProxyItems
4344+ ;
4345+}
4346+
4347+// Check that correct proxy model rows are removed when rows are removed
4348+// from the source model
4349+void tst_QSortFilterProxyModel::removeSourceRows()
4350+{
4351+ QFETCH(QStringList, sourceItems);
4352+ QFETCH(int, start);
4353+ QFETCH(int, count);
4354+ QFETCH(int, sortOrder);
4355+ QFETCH(IntPairList, expectedRemovedProxyIntervals);
4356+ QFETCH(QStringList, expectedProxyItems);
4357+
4358+ QStandardItemModel model;
4359+ QSortFilterProxyModel proxy;
4360+
4361+ proxy.setSourceModel(&model);
4362+ model.insertColumns(0, 1);
4363+ model.insertRows(0, sourceItems.count());
4364+
4365+ for (int i = 0; i < sourceItems.count(); ++i) {
4366+ QModelIndex sindex = model.index(i, 0, QModelIndex());
4367+ model.setData(sindex, sourceItems.at(i), Qt::DisplayRole);
4368+ QModelIndex pindex = proxy.index(i, 0, QModelIndex());
4369+ QCOMPARE(proxy.data(pindex, Qt::DisplayRole), model.data(sindex, Qt::DisplayRole));
4370+ }
4371+
4372+ if (sortOrder != -1)
4373+ proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
4374+ (void)proxy.rowCount(QModelIndex()); // force mapping
4375+
4376+ QSignalSpy removeSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex,int,int)));
4377+ QSignalSpy insertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex,int,int)));
4378+ QSignalSpy aboutToRemoveSpy(&proxy, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
4379+ QSignalSpy aboutToInsertSpy(&proxy, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)));
4380+
4381+ QVERIFY(removeSpy.isValid());
4382+ QVERIFY(insertSpy.isValid());
4383+ QVERIFY(aboutToRemoveSpy.isValid());
4384+ QVERIFY(aboutToInsertSpy.isValid());
4385+
4386+ model.removeRows(start, count, QModelIndex());
4387+
4388+ QCOMPARE(aboutToRemoveSpy.count(), expectedRemovedProxyIntervals.count());
4389+ for (int i = 0; i < aboutToRemoveSpy.count(); ++i) {
4390+ QList<QVariant> args = aboutToRemoveSpy.at(i);
4391+ QVERIFY(args.at(1).type() == QVariant::Int);
4392+ QVERIFY(args.at(2).type() == QVariant::Int);
4393+ QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first);
4394+ QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second);
4395+ }
4396+ QCOMPARE(removeSpy.count(), expectedRemovedProxyIntervals.count());
4397+ for (int i = 0; i < removeSpy.count(); ++i) {
4398+ QList<QVariant> args = removeSpy.at(i);
4399+ QVERIFY(args.at(1).type() == QVariant::Int);
4400+ QVERIFY(args.at(2).type() == QVariant::Int);
4401+ QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first);
4402+ QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second);
4403+ }
4404+
4405+ QCOMPARE(insertSpy.count(), 0);
4406+ QCOMPARE(aboutToInsertSpy.count(), 0);
4407+
4408+ QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxyItems.count());
4409+ for (int i = 0; i < expectedProxyItems.count(); ++i) {
4410+ QModelIndex pindex = proxy.index(i, 0, QModelIndex());
4411+ QCOMPARE(proxy.data(pindex, Qt::DisplayRole).toString(), expectedProxyItems.at(i));
4412+ }
4413+}
4414+
4415+void tst_QSortFilterProxyModel::insertSourceRows_data()
4416+{
4417+ QTest::addColumn<QStringList>("sourceItems");
4418+ QTest::addColumn<int>("start");
4419+ QTest::addColumn<QStringList>("newItems");
4420+ QTest::addColumn<int>("sortOrder");
4421+ QTest::addColumn<QStringList>("proxyItems");
4422+
4423+ QTest::newRow("insert (1)")
4424+ << (QStringList() << "c" << "b") // sourceItems
4425+ << 1 // start
4426+ << (QStringList() << "a") // newItems
4427+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4428+ << (QStringList() << "a" << "b" << "c") // proxyItems
4429+ ;
4430+
4431+ QTest::newRow("insert (2)")
4432+ << (QStringList() << "d" << "b" << "c") // sourceItems
4433+ << 3 // start
4434+ << (QStringList() << "a") // newItems
4435+ << static_cast<int>(Qt::DescendingOrder) // sortOrder
4436+ << (QStringList() << "d" << "c" << "b" << "a") // proxyItems
4437+ ;
4438+}
4439+
4440+// Check that rows are inserted at correct position in proxy model when
4441+// rows are inserted into the source model
4442+void tst_QSortFilterProxyModel::insertSourceRows()
4443+{
4444+ QFETCH(QStringList, sourceItems);
4445+ QFETCH(int, start);
4446+ QFETCH(QStringList, newItems);
4447+ QFETCH(int, sortOrder);
4448+ QFETCH(QStringList, proxyItems);
4449+
4450+ QStandardItemModel model;
4451+ QSortFilterProxyModel proxy;
4452+ proxy.setDynamicSortFilter(true);
4453+
4454+ proxy.setSourceModel(&model);
4455+ model.insertColumns(0, 1);
4456+ model.insertRows(0, sourceItems.count());
4457+
4458+ for (int i = 0; i < sourceItems.count(); ++i) {
4459+ QModelIndex index = model.index(i, 0, QModelIndex());
4460+ model.setData(index, sourceItems.at(i), Qt::DisplayRole);
4461+ }
4462+
4463+ proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
4464+ (void)proxy.rowCount(QModelIndex()); // force mapping
4465+
4466+ model.insertRows(start, newItems.size(), QModelIndex());
4467+
4468+ QCOMPARE(proxy.rowCount(QModelIndex()), proxyItems.count());
4469+ for (int i = 0; i < newItems.count(); ++i) {
4470+ QModelIndex index = model.index(start + i, 0, QModelIndex());
4471+ model.setData(index, newItems.at(i), Qt::DisplayRole);
4472+ }
4473+
4474+ for (int i = 0; i < proxyItems.count(); ++i) {
4475+ QModelIndex index = proxy.index(i, 0, QModelIndex());
4476+ QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), proxyItems.at(i));
4477+ }
4478+}
4479+
4480+void tst_QSortFilterProxyModel::changeFilter_data()
4481+{
4482+ QTest::addColumn<QStringList>("sourceItems");
4483+ QTest::addColumn<int>("sortOrder");
4484+ QTest::addColumn<QString>("initialFilter");
4485+ QTest::addColumn<IntPairList>("initialRemoveIntervals");
4486+ QTest::addColumn<QStringList>("initialProxyItems");
4487+ QTest::addColumn<QString>("finalFilter");
4488+ QTest::addColumn<IntPairList>("finalRemoveIntervals");
4489+ QTest::addColumn<IntPairList>("insertIntervals");
4490+ QTest::addColumn<QStringList>("finalProxyItems");
4491+
4492+ QTest::newRow("filter (1)")
4493+ << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // sourceItems
4494+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4495+ << "a|b|c" // initialFilter
4496+ << (IntPairList() << IntPair(3, 5)) // initialRemoveIntervals
4497+ << (QStringList() << "a" << "b" << "c") // initialProxyItems
4498+ << "b|d|f" // finalFilter
4499+ << (IntPairList() << IntPair(2, 2) << IntPair(0, 0)) // finalRemoveIntervals
4500+ << (IntPairList() << IntPair(1, 2)) // insertIntervals
4501+ << (QStringList() << "b" << "d" << "f") // finalProxyItems
4502+ ;
4503+
4504+ QTest::newRow("filter (2)")
4505+ << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // sourceItems
4506+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4507+ << "a|c|e" // initialFilter
4508+ << (IntPairList() << IntPair(5, 5) << IntPair(3, 3) << IntPair(1, 1)) // initialRemoveIntervals
4509+ << (QStringList() << "a" << "c" << "e") // initialProxyItems
4510+ << "" // finalFilter
4511+ << IntPairList() // finalRemoveIntervals
4512+ << (IntPairList() << IntPair(3, 3) << IntPair(2, 2) << IntPair(1, 1)) // insertIntervals
4513+ << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // finalProxyItems
4514+ ;
4515+
4516+ QTest::newRow("filter (3)")
4517+ << (QStringList() << "a" << "b" << "c") // sourceItems
4518+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4519+ << "a" // initialFilter
4520+ << (IntPairList() << IntPair(1, 2)) // initialRemoveIntervals
4521+ << (QStringList() << "a") // initialProxyItems
4522+ << "a" // finalFilter
4523+ << IntPairList() // finalRemoveIntervals
4524+ << IntPairList() // insertIntervals
4525+ << (QStringList() << "a") // finalProxyItems
4526+ ;
4527+}
4528+
4529+// Check that rows are added/removed when filter changes
4530+void tst_QSortFilterProxyModel::changeFilter()
4531+{
4532+ QFETCH(QStringList, sourceItems);
4533+ QFETCH(int, sortOrder);
4534+ QFETCH(QString, initialFilter);
4535+ QFETCH(IntPairList, initialRemoveIntervals);
4536+ QFETCH(QStringList, initialProxyItems);
4537+ QFETCH(QString, finalFilter);
4538+ QFETCH(IntPairList, finalRemoveIntervals);
4539+ QFETCH(IntPairList, insertIntervals);
4540+ QFETCH(QStringList, finalProxyItems);
4541+
4542+ QStandardItemModel model;
4543+ QSortFilterProxyModel proxy;
4544+
4545+ proxy.setSourceModel(&model);
4546+ model.insertColumns(0, 1);
4547+ model.insertRows(0, sourceItems.count());
4548+
4549+ for (int i = 0; i < sourceItems.count(); ++i) {
4550+ QModelIndex index = model.index(i, 0, QModelIndex());
4551+ model.setData(index, sourceItems.at(i), Qt::DisplayRole);
4552+ }
4553+
4554+ proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
4555+ (void)proxy.rowCount(QModelIndex()); // force mapping
4556+
4557+ QSignalSpy initialRemoveSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex,int,int)));
4558+ QSignalSpy initialInsertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex,int,int)));
4559+
4560+ QVERIFY(initialRemoveSpy.isValid());
4561+ QVERIFY(initialInsertSpy.isValid());
4562+
4563+ proxy.setFilterRegExp(initialFilter);
4564+
4565+ QCOMPARE(initialRemoveSpy.count(), initialRemoveIntervals.count());
4566+ QCOMPARE(initialInsertSpy.count(), 0);
4567+ for (int i = 0; i < initialRemoveSpy.count(); ++i) {
4568+ QList<QVariant> args = initialRemoveSpy.at(i);
4569+ QVERIFY(args.at(1).type() == QVariant::Int);
4570+ QVERIFY(args.at(2).type() == QVariant::Int);
4571+ QCOMPARE(args.at(1).toInt(), initialRemoveIntervals.at(i).first);
4572+ QCOMPARE(args.at(2).toInt(), initialRemoveIntervals.at(i).second);
4573+ }
4574+
4575+ QCOMPARE(proxy.rowCount(QModelIndex()), initialProxyItems.count());
4576+ for (int i = 0; i < initialProxyItems.count(); ++i) {
4577+ QModelIndex index = proxy.index(i, 0, QModelIndex());
4578+ QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), initialProxyItems.at(i));
4579+ }
4580+
4581+ QSignalSpy finalRemoveSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex,int,int)));
4582+ QSignalSpy finalInsertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex,int,int)));
4583+
4584+ QVERIFY(finalRemoveSpy.isValid());
4585+ QVERIFY(finalInsertSpy.isValid());
4586+
4587+ proxy.setFilterRegExp(finalFilter);
4588+
4589+ QCOMPARE(finalRemoveSpy.count(), finalRemoveIntervals.count());
4590+ for (int i = 0; i < finalRemoveSpy.count(); ++i) {
4591+ QList<QVariant> args = finalRemoveSpy.at(i);
4592+ QVERIFY(args.at(1).type() == QVariant::Int);
4593+ QVERIFY(args.at(2).type() == QVariant::Int);
4594+ QCOMPARE(args.at(1).toInt(), finalRemoveIntervals.at(i).first);
4595+ QCOMPARE(args.at(2).toInt(), finalRemoveIntervals.at(i).second);
4596+ }
4597+
4598+#ifdef Q_OS_IRIX
4599+ QEXPECT_FAIL("filter (2)", "Not reliable on IRIX", Abort);
4600+#endif
4601+ QCOMPARE(finalInsertSpy.count(), insertIntervals.count());
4602+ for (int i = 0; i < finalInsertSpy.count(); ++i) {
4603+ QList<QVariant> args = finalInsertSpy.at(i);
4604+ QVERIFY(args.at(1).type() == QVariant::Int);
4605+ QVERIFY(args.at(2).type() == QVariant::Int);
4606+ QCOMPARE(args.at(1).toInt(), insertIntervals.at(i).first);
4607+ QCOMPARE(args.at(2).toInt(), insertIntervals.at(i).second);
4608+ }
4609+
4610+ QCOMPARE(proxy.rowCount(QModelIndex()), finalProxyItems.count());
4611+ for (int i = 0; i < finalProxyItems.count(); ++i) {
4612+ QModelIndex index = proxy.index(i, 0, QModelIndex());
4613+ QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), finalProxyItems.at(i));
4614+ }
4615+}
4616+
4617+void tst_QSortFilterProxyModel::changeSourceData_data()
4618+{
4619+ QTest::addColumn<QStringList>("sourceItems");
4620+ QTest::addColumn<int>("sortOrder");
4621+ QTest::addColumn<QString>("filter");
4622+ QTest::addColumn<bool>("dynamic");
4623+ QTest::addColumn<int>("row");
4624+ QTest::addColumn<QString>("newValue");
4625+ QTest::addColumn<IntPairList>("removeIntervals");
4626+ QTest::addColumn<IntPairList>("insertIntervals");
4627+ QTest::addColumn<QStringList>("proxyItems");
4628+
4629+ QTest::newRow("changeSourceData (1)")
4630+ << (QStringList() << "c" << "b" << "a") // sourceItems
4631+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4632+ << "" // filter
4633+ << true // dynamic
4634+ << 2 // row
4635+ << "z" // newValue
4636+ << IntPairList() // removeIntervals
4637+ << IntPairList() // insertIntervals
4638+ << (QStringList() << "b" << "c" << "z") // proxyItems
4639+ ;
4640+
4641+ QTest::newRow("changeSourceData (2)")
4642+ << (QStringList() << "b" << "c" << "z") // sourceItems
4643+ << static_cast<int>(Qt::DescendingOrder) // sortOrder
4644+ << "" // filter
4645+ << true // dynamic
4646+ << 1 // row
4647+ << "a" // newValue
4648+ << IntPairList() // removeIntervals
4649+ << IntPairList() // insertIntervals
4650+ << (QStringList() << "z" << "b" << "a") // proxyItems
4651+ ;
4652+
4653+ QTest::newRow("changeSourceData (3)")
4654+ << (QStringList() << "a" << "b") // sourceItems
4655+ << static_cast<int>(Qt::DescendingOrder) // sortOrder
4656+ << "" // filter
4657+ << true // dynamic
4658+ << 0 // row
4659+ << "a" // newValue
4660+ << IntPairList() // removeIntervals
4661+ << IntPairList() // insertIntervals
4662+ << (QStringList() << "b" << "a") // proxyItems
4663+ ;
4664+
4665+ QTest::newRow("changeSourceData (4)")
4666+ << (QStringList() << "a" << "b" << "c" << "d") // sourceItems
4667+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4668+ << "a|c" // filter
4669+ << true // dynamic
4670+ << 1 // row
4671+ << "x" // newValue
4672+ << IntPairList() // removeIntervals
4673+ << IntPairList() // insertIntervals
4674+ << (QStringList() << "a" << "c") // proxyItems
4675+ ;
4676+
4677+ QTest::newRow("changeSourceData (5)")
4678+ << (QStringList() << "a" << "b" << "c" << "d") // sourceItems
4679+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4680+ << "a|c|x" // filter
4681+ << true // dynamic
4682+ << 1 // row
4683+ << "x" // newValue
4684+ << IntPairList() // removeIntervals
4685+ << (IntPairList() << IntPair(2, 2)) // insertIntervals
4686+ << (QStringList() << "a" << "c" << "x") // proxyItems
4687+ ;
4688+
4689+ QTest::newRow("changeSourceData (6)")
4690+ << (QStringList() << "c" << "b" << "a") // sourceItems
4691+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4692+ << "" // filter
4693+ << false // dynamic
4694+ << 2 // row
4695+ << "x" // newValue
4696+ << IntPairList() // removeIntervals
4697+ << IntPairList() // insertIntervals
4698+ << (QStringList() << "x" << "b" << "c") // proxyItems
4699+ ;
4700+}
4701+
4702+void tst_QSortFilterProxyModel::changeSourceData()
4703+{
4704+ QFETCH(QStringList, sourceItems);
4705+ QFETCH(int, sortOrder);
4706+ QFETCH(QString, filter);
4707+ QFETCH(bool, dynamic);
4708+ QFETCH(int, row);
4709+ QFETCH(QString, newValue);
4710+ QFETCH(IntPairList, removeIntervals);
4711+ QFETCH(IntPairList, insertIntervals);
4712+ QFETCH(QStringList, proxyItems);
4713+
4714+ QStandardItemModel model;
4715+ QSortFilterProxyModel proxy;
4716+
4717+ proxy.setDynamicSortFilter(dynamic);
4718+ proxy.setSourceModel(&model);
4719+ model.insertColumns(0, 1);
4720+ model.insertRows(0, sourceItems.count());
4721+
4722+ for (int i = 0; i < sourceItems.count(); ++i) {
4723+ QModelIndex index = model.index(i, 0, QModelIndex());
4724+ model.setData(index, sourceItems.at(i), Qt::DisplayRole);
4725+ }
4726+
4727+ proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
4728+ (void)proxy.rowCount(QModelIndex()); // force mapping
4729+
4730+ proxy.setFilterRegExp(filter);
4731+
4732+ QSignalSpy removeSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex,int,int)));
4733+ QSignalSpy insertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex,int,int)));
4734+
4735+ QVERIFY(removeSpy.isValid());
4736+ QVERIFY(insertSpy.isValid());
4737+
4738+ {
4739+ QModelIndex index = model.index(row, 0, QModelIndex());
4740+ model.setData(index, newValue, Qt::DisplayRole);
4741+ }
4742+
4743+ QCOMPARE(removeSpy.count(), removeIntervals.count());
4744+ for (int i = 0; i < removeSpy.count(); ++i) {
4745+ QList<QVariant> args = removeSpy.at(i);
4746+ QVERIFY(args.at(1).type() == QVariant::Int);
4747+ QVERIFY(args.at(2).type() == QVariant::Int);
4748+ QCOMPARE(args.at(1).toInt(), removeIntervals.at(i).first);
4749+ QCOMPARE(args.at(2).toInt(), removeIntervals.at(i).second);
4750+ }
4751+
4752+ QCOMPARE(insertSpy.count(), insertIntervals.count());
4753+ for (int i = 0; i < insertSpy.count(); ++i) {
4754+ QList<QVariant> args = insertSpy.at(i);
4755+ QVERIFY(args.at(1).type() == QVariant::Int);
4756+ QVERIFY(args.at(2).type() == QVariant::Int);
4757+ QCOMPARE(args.at(1).toInt(), insertIntervals.at(i).first);
4758+ QCOMPARE(args.at(2).toInt(), insertIntervals.at(i).second);
4759+ }
4760+
4761+ QCOMPARE(proxy.rowCount(QModelIndex()), proxyItems.count());
4762+ for (int i = 0; i < proxyItems.count(); ++i) {
4763+ QModelIndex index = proxy.index(i, 0, QModelIndex());
4764+ QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), proxyItems.at(i));
4765+ }
4766+}
4767+
4768+void tst_QSortFilterProxyModel::sortFilterRole()
4769+{
4770+ QStandardItemModel model;
4771+ QSortFilterProxyModel proxy;
4772+ proxy.setSourceModel(&model);
4773+ model.insertColumns(0, 1);
4774+
4775+ QList<QPair<QVariant, QVariant> > sourceItems;
4776+ sourceItems = QList<QPair<QVariant, QVariant> >()
4777+ << QPair<QVariant, QVariant>("b", 3)
4778+ << QPair<QVariant, QVariant>("c", 2)
4779+ << QPair<QVariant, QVariant>("a", 1);
4780+
4781+ QList<int> orderedItems;
4782+ orderedItems = QList<int>()
4783+ << 2 << 1;
4784+
4785+ model.insertRows(0, sourceItems.count());
4786+ for (int i = 0; i < sourceItems.count(); ++i) {
4787+ QModelIndex index = model.index(i, 0, QModelIndex());
4788+ model.setData(index, sourceItems.at(i).first, Qt::DisplayRole);
4789+ model.setData(index, sourceItems.at(i).second, Qt::UserRole);
4790+ }
4791+
4792+ proxy.setFilterRegExp("2");
4793+ QCOMPARE(proxy.rowCount(), 0); // Qt::DisplayRole is default role
4794+
4795+ proxy.setFilterRole(Qt::UserRole);
4796+ QCOMPARE(proxy.rowCount(), 1);
4797+
4798+ proxy.setFilterRole(Qt::DisplayRole);
4799+ QCOMPARE(proxy.rowCount(), 0);
4800+
4801+ proxy.setFilterRegExp("1|2|3");
4802+ QCOMPARE(proxy.rowCount(), 0);
4803+
4804+ proxy.setFilterRole(Qt::UserRole);
4805+ QCOMPARE(proxy.rowCount(), 3);
4806+
4807+ proxy.sort(0, Qt::AscendingOrder);
4808+ QCOMPARE(proxy.rowCount(), 3);
4809+
4810+ proxy.setSortRole(Qt::UserRole);
4811+ proxy.setFilterRole(Qt::DisplayRole);
4812+ proxy.setFilterRegExp("a|c");
4813+ QCOMPARE(proxy.rowCount(), orderedItems.count());
4814+ for (int i = 0; i < proxy.rowCount(); ++i) {
4815+ QModelIndex index = proxy.index(i, 0, QModelIndex());
4816+ QCOMPARE(proxy.data(index, Qt::DisplayRole), sourceItems.at(orderedItems.at(i)).first);
4817+ }
4818+}
4819+
4820+void tst_QSortFilterProxyModel::selectionFilteredOut()
4821+{
4822+ QStandardItemModel model(2, 1);
4823+ model.setData(model.index(0, 0), QString("AAA"));
4824+ model.setData(model.index(1, 0), QString("BBB"));
4825+ QSortFilterProxyModel proxy;
4826+ proxy.setSourceModel(&model);
4827+ QTreeView view;
4828+
4829+ view.show();
4830+ view.setModel(&proxy);
4831+ QSignalSpy spy(view.selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)));
4832+ QVERIFY(spy.isValid());
4833+
4834+ view.setCurrentIndex(proxy.index(0, 0));
4835+ QCOMPARE(spy.count(), 1);
4836+ proxy.setFilterRegExp(QRegExp("^B"));
4837+ QCOMPARE(spy.count(), 2);
4838+}
4839+
4840+void tst_QSortFilterProxyModel::match_data()
4841+{
4842+ QTest::addColumn<QStringList>("sourceItems");
4843+ QTest::addColumn<int>("sortOrder");
4844+ QTest::addColumn<QString>("filter");
4845+ QTest::addColumn<int>("proxyStartRow");
4846+ QTest::addColumn<QString>("what");
4847+ QTest::addColumn<int>("matchFlags");
4848+ QTest::addColumn<IntList>("expectedProxyItems");
4849+ QTest::newRow("1")
4850+ << (QStringList() << "a") // sourceItems
4851+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4852+ << "" // filter
4853+ << 0 // proxyStartRow
4854+ << "a" // what
4855+ << static_cast<int>(Qt::MatchExactly) // matchFlags
4856+ << (IntList() << 0); // expectedProxyItems
4857+ QTest::newRow("2")
4858+ << (QStringList() << "a" << "b") // sourceItems
4859+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4860+ << "" // filter
4861+ << 0 // proxyStartRow
4862+ << "b" // what
4863+ << static_cast<int>(Qt::MatchExactly) // matchFlags
4864+ << (IntList() << 1); // expectedProxyItems
4865+ QTest::newRow("3")
4866+ << (QStringList() << "a" << "b") // sourceItems
4867+ << static_cast<int>(Qt::DescendingOrder) // sortOrder
4868+ << "" // filter
4869+ << 0 // proxyStartRow
4870+ << "a" // what
4871+ << static_cast<int>(Qt::MatchExactly) // matchFlags
4872+ << (IntList() << 1); // expectedProxyItems
4873+ QTest::newRow("4")
4874+ << (QStringList() << "b" << "d" << "a" << "c") // sourceItems
4875+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4876+ << "" // filter
4877+ << 1 // proxyStartRow
4878+ << "a" // what
4879+ << static_cast<int>(Qt::MatchExactly) // matchFlags
4880+ << IntList(); // expectedProxyItems
4881+ QTest::newRow("5")
4882+ << (QStringList() << "b" << "d" << "a" << "c") // sourceItems
4883+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
4884+ << "a|b" // filter
4885+ << 0 // proxyStartRow
4886+ << "c" // what
4887+ << static_cast<int>(Qt::MatchExactly) // matchFlags
4888+ << IntList(); // expectedProxyItems
4889+ QTest::newRow("6")
4890+ << (QStringList() << "b" << "d" << "a" << "c") // sourceItems
4891+ << static_cast<int>(Qt::DescendingOrder) // sortOrder
4892+ << "a|b" // filter
4893+ << 0 // proxyStartRow
4894+ << "b" // what
4895+ << static_cast<int>(Qt::MatchExactly) // matchFlags
4896+ << (IntList() << 0); // expectedProxyItems
4897+}
4898+
4899+void tst_QSortFilterProxyModel::match()
4900+{
4901+ QFETCH(QStringList, sourceItems);
4902+ QFETCH(int, sortOrder);
4903+ QFETCH(QString, filter);
4904+ QFETCH(int, proxyStartRow);
4905+ QFETCH(QString, what);
4906+ QFETCH(int, matchFlags);
4907+ QFETCH(IntList, expectedProxyItems);
4908+
4909+ QStandardItemModel model;
4910+ QSortFilterProxyModel proxy;
4911+
4912+ proxy.setSourceModel(&model);
4913+ model.insertColumns(0, 1);
4914+ model.insertRows(0, sourceItems.count());
4915+
4916+ for (int i = 0; i < sourceItems.count(); ++i) {
4917+ QModelIndex index = model.index(i, 0, QModelIndex());
4918+ model.setData(index, sourceItems.at(i), Qt::DisplayRole);
4919+ }
4920+
4921+ proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
4922+ proxy.setFilterRegExp(filter);
4923+
4924+ QModelIndex startIndex = proxy.index(proxyStartRow, 0);
4925+ QModelIndexList indexes = proxy.match(startIndex, Qt::DisplayRole, what,
4926+ expectedProxyItems.count(),
4927+ Qt::MatchFlags(matchFlags));
4928+ QCOMPARE(indexes.count(), expectedProxyItems.count());
4929+ for (int i = 0; i < indexes.count(); ++i)
4930+ QCOMPARE(indexes.at(i).row(), expectedProxyItems.at(i));
4931+}
4932+
4933+void tst_QSortFilterProxyModel::insertIntoChildrenlessItem()
4934+{
4935+ QStandardItemModel model;
4936+ QStandardItem *itemA = new QStandardItem("a");
4937+ model.appendRow(itemA);
4938+ QStandardItem *itemB = new QStandardItem("b");
4939+ model.appendRow(itemB);
4940+ QStandardItem *itemC = new QStandardItem("c");
4941+ model.appendRow(itemC);
4942+
4943+ QSortFilterProxyModel proxy;
4944+ proxy.setSourceModel(&model);
4945+
4946+ QSignalSpy colsInsertedSpy(&proxy, SIGNAL(columnsInserted(QModelIndex,int,int)));
4947+ QSignalSpy rowsInsertedSpy(&proxy, SIGNAL(rowsInserted(QModelIndex,int,int)));
4948+
4949+ QVERIFY(colsInsertedSpy.isValid());
4950+ QVERIFY(rowsInsertedSpy.isValid());
4951+
4952+ (void)proxy.rowCount(QModelIndex()); // force mapping of "a", "b", "c"
4953+ QCOMPARE(colsInsertedSpy.count(), 0);
4954+ QCOMPARE(rowsInsertedSpy.count(), 0);
4955+
4956+ // now add a child to itemB ==> should get insert notification from the proxy
4957+ itemB->appendRow(new QStandardItem("a.0"));
4958+ QCOMPARE(colsInsertedSpy.count(), 1);
4959+ QCOMPARE(rowsInsertedSpy.count(), 1);
4960+
4961+ QVariantList args = colsInsertedSpy.takeFirst();
4962+ QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), proxy.mapFromSource(itemB->index()));
4963+ QCOMPARE(qvariant_cast<int>(args.at(1)), 0);
4964+ QCOMPARE(qvariant_cast<int>(args.at(2)), 0);
4965+
4966+ args = rowsInsertedSpy.takeFirst();
4967+ QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), proxy.mapFromSource(itemB->index()));
4968+ QCOMPARE(qvariant_cast<int>(args.at(1)), 0);
4969+ QCOMPARE(qvariant_cast<int>(args.at(2)), 0);
4970+}
4971+
4972+void tst_QSortFilterProxyModel::invalidateMappedChildren()
4973+{
4974+ QStandardItemModel model;
4975+
4976+ QSortFilterProxyModel proxy;
4977+ proxy.setSourceModel(&model);
4978+
4979+ QStandardItem *itemA = new QStandardItem("a");
4980+ model.appendRow(itemA);
4981+ QStandardItem *itemB = new QStandardItem("b");
4982+ itemA->appendRow(itemB);
4983+
4984+ QStandardItem *itemC = new QStandardItem("c");
4985+ itemB->appendRow(itemC);
4986+ itemC->appendRow(new QStandardItem("d"));
4987+
4988+ // force mappings
4989+ (void)proxy.hasChildren(QModelIndex());
4990+ (void)proxy.hasChildren(proxy.mapFromSource(itemA->index()));
4991+ (void)proxy.hasChildren(proxy.mapFromSource(itemB->index()));
4992+ (void)proxy.hasChildren(proxy.mapFromSource(itemC->index()));
4993+
4994+ itemB->removeRow(0); // should invalidate mapping of itemC
4995+ itemC = new QStandardItem("c");
4996+ itemA->appendRow(itemC);
4997+ itemC->appendRow(new QStandardItem("d"));
4998+
4999+ itemA->removeRow(1); // should invalidate mapping of itemC
5000+ itemC = new QStandardItem("c");
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches