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
=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch'
=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/src'
=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/src/corelib'
=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/src/corelib/itemmodels'
=== added file '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/src/corelib/itemmodels/qsortfilterproxymodel.cpp'
--- .pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/src/corelib/itemmodels/qsortfilterproxymodel.cpp 1970-01-01 00:00:00 +0000
+++ .pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/src/corelib/itemmodels/qsortfilterproxymodel.cpp 2013-10-03 10:06:38 +0000
@@ -0,0 +1,2767 @@
1/****************************************************************************
2**
3** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qsortfilterproxymodel.h"
43
44#ifndef QT_NO_SORTFILTERPROXYMODEL
45
46#include "qitemselectionmodel.h"
47#include <qsize.h>
48#include <qdebug.h>
49#include <qdatetime.h>
50#include <qpair.h>
51#include <qstringlist.h>
52#include <private/qabstractitemmodel_p.h>
53#include <private/qabstractproxymodel_p.h>
54
55#include <algorithm>
56
57QT_BEGIN_NAMESPACE
58
59typedef QList<QPair<QModelIndex, QPersistentModelIndex> > QModelIndexPairList;
60
61static inline QSet<int> qVectorToSet(const QVector<int> &vector)
62{
63 QSet<int> set;
64 set.reserve(vector.size());
65 for(int i=0; i < vector.size(); ++i)
66 set << vector.at(i);
67 return set;
68}
69
70class QSortFilterProxyModelLessThan
71{
72public:
73 inline QSortFilterProxyModelLessThan(int column, const QModelIndex &parent,
74 const QAbstractItemModel *source,
75 const QSortFilterProxyModel *proxy)
76 : sort_column(column), source_parent(parent), source_model(source), proxy_model(proxy) {}
77
78 inline bool operator()(int r1, int r2) const
79 {
80 QModelIndex i1 = source_model->index(r1, sort_column, source_parent);
81 QModelIndex i2 = source_model->index(r2, sort_column, source_parent);
82 return proxy_model->lessThan(i1, i2);
83 }
84
85private:
86 int sort_column;
87 QModelIndex source_parent;
88 const QAbstractItemModel *source_model;
89 const QSortFilterProxyModel *proxy_model;
90};
91
92class QSortFilterProxyModelGreaterThan
93{
94public:
95 inline QSortFilterProxyModelGreaterThan(int column, const QModelIndex &parent,
96 const QAbstractItemModel *source,
97 const QSortFilterProxyModel *proxy)
98 : sort_column(column), source_parent(parent),
99 source_model(source), proxy_model(proxy) {}
100
101 inline bool operator()(int r1, int r2) const
102 {
103 QModelIndex i1 = source_model->index(r1, sort_column, source_parent);
104 QModelIndex i2 = source_model->index(r2, sort_column, source_parent);
105 return proxy_model->lessThan(i2, i1);
106 }
107
108private:
109 int sort_column;
110 QModelIndex source_parent;
111 const QAbstractItemModel *source_model;
112 const QSortFilterProxyModel *proxy_model;
113};
114
115
116//this struct is used to store what are the rows that are removed
117//between a call to rowsAboutToBeRemoved and rowsRemoved
118//it avoids readding rows to the mapping that are currently being removed
119struct QRowsRemoval
120{
121 QRowsRemoval(const QModelIndex &parent_source, int start, int end) : parent_source(parent_source), start(start), end(end)
122 {
123 }
124
125 QRowsRemoval() : start(-1), end(-1)
126 {
127 }
128
129 bool contains(QModelIndex parent, int row)
130 {
131 do {
132 if (parent == parent_source)
133 return row >= start && row <= end;
134 row = parent.row();
135 parent = parent.parent();
136 } while (row >= 0);
137 return false;
138 }
139private:
140 QModelIndex parent_source;
141 int start;
142 int end;
143};
144
145class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate
146{
147 Q_DECLARE_PUBLIC(QSortFilterProxyModel)
148
149public:
150 struct Mapping {
151 QVector<int> source_rows;
152 QVector<int> source_columns;
153 QVector<int> proxy_rows;
154 QVector<int> proxy_columns;
155 QVector<QModelIndex> mapped_children;
156 QHash<QModelIndex, Mapping *>::const_iterator map_iter;
157 };
158
159 mutable QHash<QModelIndex, Mapping*> source_index_mapping;
160
161 int source_sort_column;
162 int proxy_sort_column;
163 Qt::SortOrder sort_order;
164 Qt::CaseSensitivity sort_casesensitivity;
165 int sort_role;
166 bool sort_localeaware;
167
168 int filter_column;
169 QRegExp filter_regexp;
170 int filter_role;
171
172 bool dynamic_sortfilter;
173 QRowsRemoval itemsBeingRemoved;
174
175 QModelIndexPairList saved_persistent_indexes;
176
177 QHash<QModelIndex, Mapping *>::const_iterator create_mapping(
178 const QModelIndex &source_parent) const;
179 QModelIndex proxy_to_source(const QModelIndex &proxyIndex) const;
180 QModelIndex source_to_proxy(const QModelIndex &sourceIndex) const;
181 bool can_create_mapping(const QModelIndex &source_parent) const;
182
183 void remove_from_mapping(const QModelIndex &source_parent);
184
185 inline QHash<QModelIndex, Mapping *>::const_iterator index_to_iterator(
186 const QModelIndex &proxy_index) const
187 {
188 Q_ASSERT(proxy_index.isValid());
189 Q_ASSERT(proxy_index.model() == q_func());
190 const void *p = proxy_index.internalPointer();
191 Q_ASSERT(p);
192 QHash<QModelIndex, Mapping *>::const_iterator it =
193 static_cast<const Mapping*>(p)->map_iter;
194 Q_ASSERT(it != source_index_mapping.constEnd());
195 Q_ASSERT(it.value());
196 return it;
197 }
198
199 inline QModelIndex create_index(int row, int column,
200 QHash<QModelIndex, Mapping*>::const_iterator it) const
201 {
202 return q_func()->createIndex(row, column, *it);
203 }
204
205 void _q_sourceDataChanged(const QModelIndex &source_top_left,
206 const QModelIndex &source_bottom_right);
207 void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int start, int end);
208
209 void _q_sourceAboutToBeReset();
210 void _q_sourceReset();
211
212 void _q_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint);
213 void _q_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint);
214
215 void _q_sourceRowsAboutToBeInserted(const QModelIndex &source_parent,
216 int start, int end);
217 void _q_sourceRowsInserted(const QModelIndex &source_parent,
218 int start, int end);
219 void _q_sourceRowsAboutToBeRemoved(const QModelIndex &source_parent,
220 int start, int end);
221 void _q_sourceRowsRemoved(const QModelIndex &source_parent,
222 int start, int end);
223 void _q_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent,
224 int sourceStart, int sourceEnd,
225 const QModelIndex &destParent, int dest);
226 void _q_sourceRowsMoved(const QModelIndex &sourceParent,
227 int sourceStart, int sourceEnd,
228 const QModelIndex &destParent, int dest);
229 void _q_sourceColumnsAboutToBeInserted(const QModelIndex &source_parent,
230 int start, int end);
231 void _q_sourceColumnsInserted(const QModelIndex &source_parent,
232 int start, int end);
233 void _q_sourceColumnsAboutToBeRemoved(const QModelIndex &source_parent,
234 int start, int end);
235 void _q_sourceColumnsRemoved(const QModelIndex &source_parent,
236 int start, int end);
237 void _q_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent,
238 int sourceStart, int sourceEnd,
239 const QModelIndex &destParent, int dest);
240 void _q_sourceColumnsMoved(const QModelIndex &sourceParent,
241 int sourceStart, int sourceEnd,
242 const QModelIndex &destParent, int dest);
243
244 void _q_clearMapping();
245
246 void sort();
247 bool update_source_sort_column();
248 void sort_source_rows(QVector<int> &source_rows,
249 const QModelIndex &source_parent) const;
250 QVector<QPair<int, QVector<int > > > proxy_intervals_for_source_items_to_add(
251 const QVector<int> &proxy_to_source, const QVector<int> &source_items,
252 const QModelIndex &source_parent, Qt::Orientation orient) const;
253 QVector<QPair<int, int > > proxy_intervals_for_source_items(
254 const QVector<int> &source_to_proxy, const QVector<int> &source_items) const;
255 void insert_source_items(
256 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
257 const QVector<int> &source_items, const QModelIndex &source_parent,
258 Qt::Orientation orient, bool emit_signal = true);
259 void remove_source_items(
260 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
261 const QVector<int> &source_items, const QModelIndex &source_parent,
262 Qt::Orientation orient, bool emit_signal = true);
263 void remove_proxy_interval(
264 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
265 int proxy_start, int proxy_end, const QModelIndex &proxy_parent,
266 Qt::Orientation orient, bool emit_signal = true);
267 void build_source_to_proxy_mapping(
268 const QVector<int> &proxy_to_source, QVector<int> &source_to_proxy) const;
269 void source_items_inserted(const QModelIndex &source_parent,
270 int start, int end, Qt::Orientation orient);
271 void source_items_about_to_be_removed(const QModelIndex &source_parent,
272 int start, int end, Qt::Orientation orient);
273 void source_items_removed(const QModelIndex &source_parent,
274 int start, int end, Qt::Orientation orient);
275 void proxy_item_range(
276 const QVector<int> &source_to_proxy, const QVector<int> &source_items,
277 int &proxy_low, int &proxy_high) const;
278
279 QModelIndexPairList store_persistent_indexes();
280 void update_persistent_indexes(const QModelIndexPairList &source_indexes);
281
282 void filter_changed(const QModelIndex &source_parent = QModelIndex());
283 QSet<int> handle_filter_changed(
284 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
285 const QModelIndex &source_parent, Qt::Orientation orient);
286
287 void updateChildrenMapping(const QModelIndex &source_parent, Mapping *parent_mapping,
288 Qt::Orientation orient, int start, int end, int delta_item_count, bool remove);
289
290 virtual void _q_sourceModelDestroyed();
291};
292
293typedef QHash<QModelIndex, QSortFilterProxyModelPrivate::Mapping *> IndexMap;
294
295void QSortFilterProxyModelPrivate::_q_sourceModelDestroyed()
296{
297 QAbstractProxyModelPrivate::_q_sourceModelDestroyed();
298 _q_clearMapping();
299}
300
301void QSortFilterProxyModelPrivate::remove_from_mapping(const QModelIndex &source_parent)
302{
303 if (Mapping *m = source_index_mapping.take(source_parent)) {
304 for (int i = 0; i < m->mapped_children.size(); ++i)
305 remove_from_mapping(m->mapped_children.at(i));
306 delete m;
307 }
308}
309
310void QSortFilterProxyModelPrivate::_q_clearMapping()
311{
312 // store the persistent indexes
313 QModelIndexPairList source_indexes = store_persistent_indexes();
314
315 qDeleteAll(source_index_mapping);
316 source_index_mapping.clear();
317 if (dynamic_sortfilter && update_source_sort_column()) {
318 //update_source_sort_column might have created wrong mapping so we have to clear it again
319 qDeleteAll(source_index_mapping);
320 source_index_mapping.clear();
321 }
322
323 // update the persistent indexes
324 update_persistent_indexes(source_indexes);
325}
326
327IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping(
328 const QModelIndex &source_parent) const
329{
330 Q_Q(const QSortFilterProxyModel);
331
332 IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
333 if (it != source_index_mapping.constEnd()) // was mapped already
334 return it;
335
336 Mapping *m = new Mapping;
337
338 int source_rows = model->rowCount(source_parent);
339 m->source_rows.reserve(source_rows);
340 for (int i = 0; i < source_rows; ++i) {
341 if (q->filterAcceptsRow(i, source_parent))
342 m->source_rows.append(i);
343 }
344 int source_cols = model->columnCount(source_parent);
345 m->source_columns.reserve(source_cols);
346 for (int i = 0; i < source_cols; ++i) {
347 if (q->filterAcceptsColumn(i, source_parent))
348 m->source_columns.append(i);
349 }
350
351 sort_source_rows(m->source_rows, source_parent);
352 m->proxy_rows.resize(source_rows);
353 build_source_to_proxy_mapping(m->source_rows, m->proxy_rows);
354 m->proxy_columns.resize(source_cols);
355 build_source_to_proxy_mapping(m->source_columns, m->proxy_columns);
356
357 it = IndexMap::const_iterator(source_index_mapping.insert(source_parent, m));
358 m->map_iter = it;
359
360 if (source_parent.isValid()) {
361 QModelIndex source_grand_parent = source_parent.parent();
362 IndexMap::const_iterator it2 = create_mapping(source_grand_parent);
363 Q_ASSERT(it2 != source_index_mapping.constEnd());
364 it2.value()->mapped_children.append(source_parent);
365 }
366
367 Q_ASSERT(it != source_index_mapping.constEnd());
368 Q_ASSERT(it.value());
369
370 return it;
371}
372
373QModelIndex QSortFilterProxyModelPrivate::proxy_to_source(const QModelIndex &proxy_index) const
374{
375 if (!proxy_index.isValid())
376 return QModelIndex(); // for now; we may want to be able to set a root index later
377 if (proxy_index.model() != q_func()) {
378 qWarning() << "QSortFilterProxyModel: index from wrong model passed to mapToSource";
379 Q_ASSERT(!"QSortFilterProxyModel: index from wrong model passed to mapToSource");
380 return QModelIndex();
381 }
382 IndexMap::const_iterator it = index_to_iterator(proxy_index);
383 Mapping *m = it.value();
384 if ((proxy_index.row() >= m->source_rows.size()) || (proxy_index.column() >= m->source_columns.size()))
385 return QModelIndex();
386 int source_row = m->source_rows.at(proxy_index.row());
387 int source_col = m->source_columns.at(proxy_index.column());
388 return model->index(source_row, source_col, it.key());
389}
390
391QModelIndex QSortFilterProxyModelPrivate::source_to_proxy(const QModelIndex &source_index) const
392{
393 if (!source_index.isValid())
394 return QModelIndex(); // for now; we may want to be able to set a root index later
395 if (source_index.model() != model) {
396 qWarning() << "QSortFilterProxyModel: index from wrong model passed to mapFromSource";
397 Q_ASSERT(!"QSortFilterProxyModel: index from wrong model passed to mapFromSource");
398 return QModelIndex();
399 }
400 QModelIndex source_parent = source_index.parent();
401 IndexMap::const_iterator it = create_mapping(source_parent);
402 Mapping *m = it.value();
403 if ((source_index.row() >= m->proxy_rows.size()) || (source_index.column() >= m->proxy_columns.size()))
404 return QModelIndex();
405 int proxy_row = m->proxy_rows.at(source_index.row());
406 int proxy_column = m->proxy_columns.at(source_index.column());
407 if (proxy_row == -1 || proxy_column == -1)
408 return QModelIndex();
409 return create_index(proxy_row, proxy_column, it);
410}
411
412bool QSortFilterProxyModelPrivate::can_create_mapping(const QModelIndex &source_parent) const
413{
414 if (source_parent.isValid()) {
415 QModelIndex source_grand_parent = source_parent.parent();
416 IndexMap::const_iterator it = source_index_mapping.constFind(source_grand_parent);
417 if (it == source_index_mapping.constEnd()) {
418 // Don't care, since we don't have mapping for the grand parent
419 return false;
420 }
421 Mapping *gm = it.value();
422 if (gm->proxy_rows.at(source_parent.row()) == -1 ||
423 gm->proxy_columns.at(source_parent.column()) == -1) {
424 // Don't care, since parent is filtered
425 return false;
426 }
427 }
428 return true;
429}
430
431/*!
432 \internal
433
434 Sorts the existing mappings.
435*/
436void QSortFilterProxyModelPrivate::sort()
437{
438 Q_Q(QSortFilterProxyModel);
439 emit q->layoutAboutToBeChanged(QList<QPersistentModelIndex>(), QAbstractItemModel::VerticalSortHint);
440 QModelIndexPairList source_indexes = store_persistent_indexes();
441 IndexMap::const_iterator it = source_index_mapping.constBegin();
442 for (; it != source_index_mapping.constEnd(); ++it) {
443 QModelIndex source_parent = it.key();
444 Mapping *m = it.value();
445 sort_source_rows(m->source_rows, source_parent);
446 build_source_to_proxy_mapping(m->source_rows, m->proxy_rows);
447 }
448 update_persistent_indexes(source_indexes);
449 emit q->layoutChanged(QList<QPersistentModelIndex>(), QAbstractItemModel::VerticalSortHint);
450}
451
452/*!
453 \internal
454
455 update the source_sort_column according to the proxy_sort_column
456 return true if the column was changed
457*/
458bool QSortFilterProxyModelPrivate::update_source_sort_column()
459{
460 Q_Q(QSortFilterProxyModel);
461 QModelIndex proxy_index = q->index(0, proxy_sort_column, QModelIndex());
462 int old_source_sort_column = source_sort_column;
463 source_sort_column = q->mapToSource(proxy_index).column();
464 return old_source_sort_column != source_sort_column;
465}
466
467
468/*!
469 \internal
470
471 Sorts the given \a source_rows according to current sort column and order.
472*/
473void QSortFilterProxyModelPrivate::sort_source_rows(
474 QVector<int> &source_rows, const QModelIndex &source_parent) const
475{
476 Q_Q(const QSortFilterProxyModel);
477 if (source_sort_column >= 0) {
478 if (sort_order == Qt::AscendingOrder) {
479 QSortFilterProxyModelLessThan lt(source_sort_column, source_parent, model, q);
480 std::stable_sort(source_rows.begin(), source_rows.end(), lt);
481 } else {
482 QSortFilterProxyModelGreaterThan gt(source_sort_column, source_parent, model, q);
483 std::stable_sort(source_rows.begin(), source_rows.end(), gt);
484 }
485 } else { // restore the source model order
486 std::stable_sort(source_rows.begin(), source_rows.end());
487 }
488}
489
490/*!
491 \internal
492
493 Given source-to-proxy mapping \a source_to_proxy and the set of
494 source items \a source_items (which are part of that mapping),
495 determines the corresponding proxy item intervals that should
496 be removed from the proxy model.
497
498 The result is a vector of pairs, where each pair represents a
499 (start, end) tuple, sorted in ascending order.
500*/
501QVector<QPair<int, int > > QSortFilterProxyModelPrivate::proxy_intervals_for_source_items(
502 const QVector<int> &source_to_proxy, const QVector<int> &source_items) const
503{
504 QVector<QPair<int, int> > proxy_intervals;
505 if (source_items.isEmpty())
506 return proxy_intervals;
507
508 int source_items_index = 0;
509 while (source_items_index < source_items.size()) {
510 int first_proxy_item = source_to_proxy.at(source_items.at(source_items_index));
511 Q_ASSERT(first_proxy_item != -1);
512 int last_proxy_item = first_proxy_item;
513 ++source_items_index;
514 // Find end of interval
515 while ((source_items_index < source_items.size())
516 && (source_to_proxy.at(source_items.at(source_items_index)) == last_proxy_item + 1)) {
517 ++last_proxy_item;
518 ++source_items_index;
519 }
520 // Add interval to result
521 proxy_intervals.append(QPair<int, int>(first_proxy_item, last_proxy_item));
522 }
523 std::stable_sort(proxy_intervals.begin(), proxy_intervals.end());
524 return proxy_intervals;
525}
526
527/*!
528 \internal
529
530 Given source-to-proxy mapping \a src_to_proxy and proxy-to-source mapping
531 \a proxy_to_source, removes \a source_items from this proxy model.
532 The corresponding proxy items are removed in intervals, so that the proper
533 rows/columnsRemoved(start, end) signals will be generated.
534*/
535void QSortFilterProxyModelPrivate::remove_source_items(
536 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
537 const QVector<int> &source_items, const QModelIndex &source_parent,
538 Qt::Orientation orient, bool emit_signal)
539{
540 Q_Q(QSortFilterProxyModel);
541 QModelIndex proxy_parent = q->mapFromSource(source_parent);
542 if (!proxy_parent.isValid() && source_parent.isValid())
543 return; // nothing to do (already removed)
544
545 QVector<QPair<int, int> > proxy_intervals;
546 proxy_intervals = proxy_intervals_for_source_items(source_to_proxy, source_items);
547
548 for (int i = proxy_intervals.size()-1; i >= 0; --i) {
549 QPair<int, int> interval = proxy_intervals.at(i);
550 int proxy_start = interval.first;
551 int proxy_end = interval.second;
552 remove_proxy_interval(source_to_proxy, proxy_to_source, proxy_start, proxy_end,
553 proxy_parent, orient, emit_signal);
554 }
555}
556
557/*!
558 \internal
559
560 Given source-to-proxy mapping \a source_to_proxy and proxy-to-source mapping
561 \a proxy_to_source, removes items from \a proxy_start to \a proxy_end
562 (inclusive) from this proxy model.
563*/
564void QSortFilterProxyModelPrivate::remove_proxy_interval(
565 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source, int proxy_start, int proxy_end,
566 const QModelIndex &proxy_parent, Qt::Orientation orient, bool emit_signal)
567{
568 Q_Q(QSortFilterProxyModel);
569 if (emit_signal) {
570 if (orient == Qt::Vertical)
571 q->beginRemoveRows(proxy_parent, proxy_start, proxy_end);
572 else
573 q->beginRemoveColumns(proxy_parent, proxy_start, proxy_end);
574 }
575
576 // Remove items from proxy-to-source mapping
577 proxy_to_source.remove(proxy_start, proxy_end - proxy_start + 1);
578
579 build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
580
581 if (emit_signal) {
582 if (orient == Qt::Vertical)
583 q->endRemoveRows();
584 else
585 q->endRemoveColumns();
586 }
587}
588
589/*!
590 \internal
591
592 Given proxy-to-source mapping \a proxy_to_source and a set of
593 unmapped source items \a source_items, determines the proxy item
594 intervals at which the subsets of source items should be inserted
595 (but does not actually add them to the mapping).
596
597 The result is a vector of pairs, each pair representing a tuple (start,
598 items), where items is a vector containing the (sorted) source items that
599 should be inserted at that proxy model location.
600*/
601QVector<QPair<int, QVector<int > > > QSortFilterProxyModelPrivate::proxy_intervals_for_source_items_to_add(
602 const QVector<int> &proxy_to_source, const QVector<int> &source_items,
603 const QModelIndex &source_parent, Qt::Orientation orient) const
604{
605 Q_Q(const QSortFilterProxyModel);
606 QVector<QPair<int, QVector<int> > > proxy_intervals;
607 if (source_items.isEmpty())
608 return proxy_intervals;
609
610 int proxy_low = 0;
611 int proxy_item = 0;
612 int source_items_index = 0;
613 QVector<int> source_items_in_interval;
614 bool compare = (orient == Qt::Vertical && source_sort_column >= 0 && dynamic_sortfilter);
615 while (source_items_index < source_items.size()) {
616 source_items_in_interval.clear();
617 int first_new_source_item = source_items.at(source_items_index);
618 source_items_in_interval.append(first_new_source_item);
619 ++source_items_index;
620
621 // Find proxy item at which insertion should be started
622 int proxy_high = proxy_to_source.size() - 1;
623 QModelIndex i1 = compare ? model->index(first_new_source_item, source_sort_column, source_parent) : QModelIndex();
624 while (proxy_low <= proxy_high) {
625 proxy_item = (proxy_low + proxy_high) / 2;
626 if (compare) {
627 QModelIndex i2 = model->index(proxy_to_source.at(proxy_item), source_sort_column, source_parent);
628 if ((sort_order == Qt::AscendingOrder) ? q->lessThan(i1, i2) : q->lessThan(i2, i1))
629 proxy_high = proxy_item - 1;
630 else
631 proxy_low = proxy_item + 1;
632 } else {
633 if (first_new_source_item < proxy_to_source.at(proxy_item))
634 proxy_high = proxy_item - 1;
635 else
636 proxy_low = proxy_item + 1;
637 }
638 }
639 proxy_item = proxy_low;
640
641 // Find the sequence of new source items that should be inserted here
642 if (proxy_item >= proxy_to_source.size()) {
643 for ( ; source_items_index < source_items.size(); ++source_items_index)
644 source_items_in_interval.append(source_items.at(source_items_index));
645 } else {
646 i1 = compare ? model->index(proxy_to_source.at(proxy_item), source_sort_column, source_parent) : QModelIndex();
647 for ( ; source_items_index < source_items.size(); ++source_items_index) {
648 int new_source_item = source_items.at(source_items_index);
649 if (compare) {
650 QModelIndex i2 = model->index(new_source_item, source_sort_column, source_parent);
651 if ((sort_order == Qt::AscendingOrder) ? q->lessThan(i1, i2) : q->lessThan(i2, i1))
652 break;
653 } else {
654 if (proxy_to_source.at(proxy_item) < new_source_item)
655 break;
656 }
657 source_items_in_interval.append(new_source_item);
658 }
659 }
660
661 // Add interval to result
662 proxy_intervals.append(QPair<int, QVector<int> >(proxy_item, source_items_in_interval));
663 }
664 return proxy_intervals;
665}
666
667/*!
668 \internal
669
670 Given source-to-proxy mapping \a source_to_proxy and proxy-to-source mapping
671 \a proxy_to_source, inserts the given \a source_items into this proxy model.
672 The source items are inserted in intervals (based on some sorted order), so
673 that the proper rows/columnsInserted(start, end) signals will be generated.
674*/
675void QSortFilterProxyModelPrivate::insert_source_items(
676 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
677 const QVector<int> &source_items, const QModelIndex &source_parent,
678 Qt::Orientation orient, bool emit_signal)
679{
680 Q_Q(QSortFilterProxyModel);
681 QModelIndex proxy_parent = q->mapFromSource(source_parent);
682 if (!proxy_parent.isValid() && source_parent.isValid())
683 return; // nothing to do (source_parent is not mapped)
684
685 QVector<QPair<int, QVector<int> > > proxy_intervals;
686 proxy_intervals = proxy_intervals_for_source_items_to_add(
687 proxy_to_source, source_items, source_parent, orient);
688
689 for (int i = proxy_intervals.size()-1; i >= 0; --i) {
690 QPair<int, QVector<int> > interval = proxy_intervals.at(i);
691 int proxy_start = interval.first;
692 QVector<int> source_items = interval.second;
693 int proxy_end = proxy_start + source_items.size() - 1;
694
695 if (emit_signal) {
696 if (orient == Qt::Vertical)
697 q->beginInsertRows(proxy_parent, proxy_start, proxy_end);
698 else
699 q->beginInsertColumns(proxy_parent, proxy_start, proxy_end);
700 }
701
702 for (int i = 0; i < source_items.size(); ++i)
703 proxy_to_source.insert(proxy_start + i, source_items.at(i));
704
705 build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
706
707 if (emit_signal) {
708 if (orient == Qt::Vertical)
709 q->endInsertRows();
710 else
711 q->endInsertColumns();
712 }
713 }
714}
715
716/*!
717 \internal
718
719 Handles source model items insertion (columnsInserted(), rowsInserted()).
720 Determines
721 1) which of the inserted items to also insert into proxy model (filtering),
722 2) where to insert the items into the proxy model (sorting),
723 then inserts those items.
724 The items are inserted into the proxy model in intervals (based on
725 sorted order), so that the proper rows/columnsInserted(start, end)
726 signals will be generated.
727*/
728void QSortFilterProxyModelPrivate::source_items_inserted(
729 const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
730{
731 Q_Q(QSortFilterProxyModel);
732 if ((start < 0) || (end < 0))
733 return;
734 IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
735 if (it == source_index_mapping.constEnd()) {
736 if (!can_create_mapping(source_parent))
737 return;
738 it = create_mapping(source_parent);
739 Mapping *m = it.value();
740 QModelIndex proxy_parent = q->mapFromSource(source_parent);
741 if (m->source_rows.count() > 0) {
742 q->beginInsertRows(proxy_parent, 0, m->source_rows.count() - 1);
743 q->endInsertRows();
744 }
745 if (m->source_columns.count() > 0) {
746 q->beginInsertColumns(proxy_parent, 0, m->source_columns.count() - 1);
747 q->endInsertColumns();
748 }
749 return;
750 }
751
752 Mapping *m = it.value();
753 QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
754 QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
755
756 int delta_item_count = end - start + 1;
757 int old_item_count = source_to_proxy.size();
758
759 updateChildrenMapping(source_parent, m, orient, start, end, delta_item_count, false);
760
761 // Expand source-to-proxy mapping to account for new items
762 if (start < 0 || start > source_to_proxy.size()) {
763 qWarning("QSortFilterProxyModel: invalid inserted rows reported by source model");
764 remove_from_mapping(source_parent);
765 return;
766 }
767 source_to_proxy.insert(start, delta_item_count, -1);
768
769 if (start < old_item_count) {
770 // Adjust existing "stale" indexes in proxy-to-source mapping
771 int proxy_count = proxy_to_source.size();
772 for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
773 int source_item = proxy_to_source.at(proxy_item);
774 if (source_item >= start)
775 proxy_to_source.replace(proxy_item, source_item + delta_item_count);
776 }
777 build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
778 }
779
780 // Figure out which items to add to mapping based on filter
781 QVector<int> source_items;
782 for (int i = start; i <= end; ++i) {
783 if ((orient == Qt::Vertical)
784 ? q->filterAcceptsRow(i, source_parent)
785 : q->filterAcceptsColumn(i, source_parent)) {
786 source_items.append(i);
787 }
788 }
789
790 if (model->rowCount(source_parent) == delta_item_count) {
791 // Items were inserted where there were none before.
792 // If it was new rows make sure to create mappings for columns so that a
793 // valid mapping can be retrieved later and vice-versa.
794
795 QVector<int> &orthogonal_proxy_to_source = (orient == Qt::Horizontal) ? m->source_rows : m->source_columns;
796 QVector<int> &orthogonal_source_to_proxy = (orient == Qt::Horizontal) ? m->proxy_rows : m->proxy_columns;
797
798 if (orthogonal_source_to_proxy.isEmpty()) {
799 const int ortho_end = (orient == Qt::Horizontal) ? model->rowCount(source_parent) : model->columnCount(source_parent);
800
801 orthogonal_source_to_proxy.resize(ortho_end);
802
803 for (int ortho_item = 0; ortho_item < ortho_end; ++ortho_item) {
804 if ((orient == Qt::Horizontal) ? q->filterAcceptsRow(ortho_item, source_parent)
805 : q->filterAcceptsColumn(ortho_item, source_parent)) {
806 orthogonal_proxy_to_source.append(ortho_item);
807 }
808 }
809 if (orient == Qt::Horizontal) {
810 // We're reacting to columnsInserted, but we've just inserted new rows. Sort them.
811 sort_source_rows(orthogonal_proxy_to_source, source_parent);
812 }
813 build_source_to_proxy_mapping(orthogonal_proxy_to_source, orthogonal_source_to_proxy);
814 }
815 }
816
817 // Sort and insert the items
818 if (orient == Qt::Vertical) // Only sort rows
819 sort_source_rows(source_items, source_parent);
820 insert_source_items(source_to_proxy, proxy_to_source, source_items, source_parent, orient);
821}
822
823/*!
824 \internal
825
826 Handles source model items removal
827 (columnsAboutToBeRemoved(), rowsAboutToBeRemoved()).
828*/
829void QSortFilterProxyModelPrivate::source_items_about_to_be_removed(
830 const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
831{
832 if ((start < 0) || (end < 0))
833 return;
834 IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
835 if (it == source_index_mapping.constEnd()) {
836 // Don't care, since we don't have mapping for this index
837 return;
838 }
839
840 Mapping *m = it.value();
841 QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
842 QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
843
844 // figure out which items to remove
845 QVector<int> source_items_to_remove;
846 int proxy_count = proxy_to_source.size();
847 for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
848 int source_item = proxy_to_source.at(proxy_item);
849 if ((source_item >= start) && (source_item <= end))
850 source_items_to_remove.append(source_item);
851 }
852
853 remove_source_items(source_to_proxy, proxy_to_source, source_items_to_remove,
854 source_parent, orient);
855}
856
857/*!
858 \internal
859
860 Handles source model items removal (columnsRemoved(), rowsRemoved()).
861*/
862void QSortFilterProxyModelPrivate::source_items_removed(
863 const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
864{
865 if ((start < 0) || (end < 0))
866 return;
867 IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
868 if (it == source_index_mapping.constEnd()) {
869 // Don't care, since we don't have mapping for this index
870 return;
871 }
872
873 Mapping *m = it.value();
874 QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
875 QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
876
877 if (end >= source_to_proxy.size())
878 end = source_to_proxy.size() - 1;
879
880 // Shrink the source-to-proxy mapping to reflect the new item count
881 int delta_item_count = end - start + 1;
882 source_to_proxy.remove(start, delta_item_count);
883
884 int proxy_count = proxy_to_source.size();
885 if (proxy_count > source_to_proxy.size()) {
886 // mapping is in an inconsistent state -- redo the whole mapping
887 qWarning("QSortFilterProxyModel: inconsistent changes reported by source model");
888 Q_Q(QSortFilterProxyModel);
889 q->beginResetModel();
890 remove_from_mapping(source_parent);
891 q->endResetModel();
892 return;
893 }
894
895 // Adjust "stale" indexes in proxy-to-source mapping
896 for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
897 int source_item = proxy_to_source.at(proxy_item);
898 if (source_item >= start) {
899 Q_ASSERT(source_item - delta_item_count >= 0);
900 proxy_to_source.replace(proxy_item, source_item - delta_item_count);
901 }
902 }
903 build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
904
905 updateChildrenMapping(source_parent, m, orient, start, end, delta_item_count, true);
906
907}
908
909
910/*!
911 \internal
912 updates the mapping of the children when inserting or removing items
913*/
914void QSortFilterProxyModelPrivate::updateChildrenMapping(const QModelIndex &source_parent, Mapping *parent_mapping,
915 Qt::Orientation orient, int start, int end, int delta_item_count, bool remove)
916{
917 // see if any mapped children should be (re)moved
918 QVector<QPair<QModelIndex, Mapping*> > moved_source_index_mappings;
919 QVector<QModelIndex>::iterator it2 = parent_mapping->mapped_children.begin();
920 for ( ; it2 != parent_mapping->mapped_children.end();) {
921 const QModelIndex source_child_index = *it2;
922 const int pos = (orient == Qt::Vertical)
923 ? source_child_index.row()
924 : source_child_index.column();
925 if (pos < start) {
926 // not affected
927 ++it2;
928 } else if (remove && pos <= end) {
929 // in the removed interval
930 it2 = parent_mapping->mapped_children.erase(it2);
931 remove_from_mapping(source_child_index);
932 } else {
933 // below the removed items -- recompute the index
934 QModelIndex new_index;
935 const int newpos = remove ? pos - delta_item_count : pos + delta_item_count;
936 if (orient == Qt::Vertical) {
937 new_index = model->index(newpos,
938 source_child_index.column(),
939 source_parent);
940 } else {
941 new_index = model->index(source_child_index.row(),
942 newpos,
943 source_parent);
944 }
945 *it2 = new_index;
946 ++it2;
947
948 // update mapping
949 Mapping *cm = source_index_mapping.take(source_child_index);
950 Q_ASSERT(cm);
951 // we do not reinsert right away, because the new index might be identical with another, old index
952 moved_source_index_mappings.append(QPair<QModelIndex, Mapping*>(new_index, cm));
953 }
954 }
955
956 // reinsert moved, mapped indexes
957 QVector<QPair<QModelIndex, Mapping*> >::iterator it = moved_source_index_mappings.begin();
958 for (; it != moved_source_index_mappings.end(); ++it) {
959#ifdef QT_STRICT_ITERATORS
960 source_index_mapping.insert((*it).first, (*it).second);
961 (*it).second->map_iter = source_index_mapping.constFind((*it).first);
962#else
963 (*it).second->map_iter = source_index_mapping.insert((*it).first, (*it).second);
964#endif
965 }
966}
967
968/*!
969 \internal
970*/
971void QSortFilterProxyModelPrivate::proxy_item_range(
972 const QVector<int> &source_to_proxy, const QVector<int> &source_items,
973 int &proxy_low, int &proxy_high) const
974{
975 proxy_low = INT_MAX;
976 proxy_high = INT_MIN;
977 for (int i = 0; i < source_items.count(); ++i) {
978 int proxy_item = source_to_proxy.at(source_items.at(i));
979 Q_ASSERT(proxy_item != -1);
980 if (proxy_item < proxy_low)
981 proxy_low = proxy_item;
982 if (proxy_item > proxy_high)
983 proxy_high = proxy_item;
984 }
985}
986
987/*!
988 \internal
989*/
990void QSortFilterProxyModelPrivate::build_source_to_proxy_mapping(
991 const QVector<int> &proxy_to_source, QVector<int> &source_to_proxy) const
992{
993 source_to_proxy.fill(-1);
994 int proxy_count = proxy_to_source.size();
995 for (int i = 0; i < proxy_count; ++i)
996 source_to_proxy[proxy_to_source.at(i)] = i;
997}
998
999/*!
1000 \internal
1001
1002 Maps the persistent proxy indexes to source indexes and
1003 returns the list of source indexes.
1004*/
1005QModelIndexPairList QSortFilterProxyModelPrivate::store_persistent_indexes()
1006{
1007 Q_Q(QSortFilterProxyModel);
1008 QModelIndexPairList source_indexes;
1009 foreach (QPersistentModelIndexData *data, persistent.indexes) {
1010 QModelIndex proxy_index = data->index;
1011 QModelIndex source_index = q->mapToSource(proxy_index);
1012 source_indexes.append(qMakePair(proxy_index, QPersistentModelIndex(source_index)));
1013 }
1014 return source_indexes;
1015}
1016
1017/*!
1018 \internal
1019
1020 Maps \a source_indexes to proxy indexes and stores those
1021 as persistent indexes.
1022*/
1023void QSortFilterProxyModelPrivate::update_persistent_indexes(
1024 const QModelIndexPairList &source_indexes)
1025{
1026 Q_Q(QSortFilterProxyModel);
1027 QModelIndexList from, to;
1028 for (int i = 0; i < source_indexes.count(); ++i) {
1029 QModelIndex source_index = source_indexes.at(i).second;
1030 QModelIndex old_proxy_index = source_indexes.at(i).first;
1031 create_mapping(source_index.parent());
1032 QModelIndex proxy_index = q->mapFromSource(source_index);
1033 from << old_proxy_index;
1034 to << proxy_index;
1035 }
1036 q->changePersistentIndexList(from, to);
1037}
1038
1039
1040/*!
1041 \internal
1042
1043 Updates the proxy model (adds/removes rows) based on the
1044 new filter.
1045*/
1046void QSortFilterProxyModelPrivate::filter_changed(const QModelIndex &source_parent)
1047{
1048 IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
1049 if (it == source_index_mapping.constEnd())
1050 return;
1051 Mapping *m = it.value();
1052 QSet<int> rows_removed = handle_filter_changed(m->proxy_rows, m->source_rows, source_parent, Qt::Vertical);
1053 QSet<int> columns_removed = handle_filter_changed(m->proxy_columns, m->source_columns, source_parent, Qt::Horizontal);
1054
1055 // We need to iterate over a copy of m->mapped_children because otherwise it may be changed by other code, invalidating
1056 // the iterator it2.
1057 // The m->mapped_children vector can be appended to with indexes which are no longer filtered
1058 // out (in create_mapping) when this function recurses for child indexes.
1059 const QVector<QModelIndex> mappedChildren = m->mapped_children;
1060 QVector<int> indexesToRemove;
1061 for (int i = 0; i < mappedChildren.size(); ++i) {
1062 const QModelIndex source_child_index = mappedChildren.at(i);
1063 if (rows_removed.contains(source_child_index.row()) || columns_removed.contains(source_child_index.column())) {
1064 indexesToRemove.push_back(i);
1065 remove_from_mapping(source_child_index);
1066 } else {
1067 filter_changed(source_child_index);
1068 }
1069 }
1070 QVector<int>::const_iterator removeIt = indexesToRemove.constEnd();
1071 const QVector<int>::const_iterator removeBegin = indexesToRemove.constBegin();
1072
1073 // We can't just remove these items from mappedChildren while iterating above and then
1074 // do something like m->mapped_children = mappedChildren, because mapped_children might
1075 // be appended to in create_mapping, and we would lose those new items.
1076 // Because they are always appended in create_mapping, we can still remove them by
1077 // position here.
1078 while (removeIt != removeBegin) {
1079 --removeIt;
1080 m->mapped_children.remove(*removeIt);
1081 }
1082}
1083
1084/*!
1085 \internal
1086 returns the removed items indexes
1087*/
1088QSet<int> QSortFilterProxyModelPrivate::handle_filter_changed(
1089 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
1090 const QModelIndex &source_parent, Qt::Orientation orient)
1091{
1092 Q_Q(QSortFilterProxyModel);
1093 // Figure out which mapped items to remove
1094 QVector<int> source_items_remove;
1095 for (int i = 0; i < proxy_to_source.count(); ++i) {
1096 const int source_item = proxy_to_source.at(i);
1097 if ((orient == Qt::Vertical)
1098 ? !q->filterAcceptsRow(source_item, source_parent)
1099 : !q->filterAcceptsColumn(source_item, source_parent)) {
1100 // This source item does not satisfy the filter, so it must be removed
1101 source_items_remove.append(source_item);
1102 }
1103 }
1104 // Figure out which non-mapped items to insert
1105 QVector<int> source_items_insert;
1106 int source_count = source_to_proxy.size();
1107 for (int source_item = 0; source_item < source_count; ++source_item) {
1108 if (source_to_proxy.at(source_item) == -1) {
1109 if ((orient == Qt::Vertical)
1110 ? q->filterAcceptsRow(source_item, source_parent)
1111 : q->filterAcceptsColumn(source_item, source_parent)) {
1112 // This source item satisfies the filter, so it must be added
1113 source_items_insert.append(source_item);
1114 }
1115 }
1116 }
1117 if (!source_items_remove.isEmpty() || !source_items_insert.isEmpty()) {
1118 // Do item removal and insertion
1119 remove_source_items(source_to_proxy, proxy_to_source,
1120 source_items_remove, source_parent, orient);
1121 if (orient == Qt::Vertical)
1122 sort_source_rows(source_items_insert, source_parent);
1123 insert_source_items(source_to_proxy, proxy_to_source,
1124 source_items_insert, source_parent, orient);
1125 }
1126 return qVectorToSet(source_items_remove);
1127}
1128
1129void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &source_top_left,
1130 const QModelIndex &source_bottom_right)
1131{
1132 Q_Q(QSortFilterProxyModel);
1133 if (!source_top_left.isValid() || !source_bottom_right.isValid())
1134 return;
1135 QModelIndex source_parent = source_top_left.parent();
1136 IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
1137 if (it == source_index_mapping.constEnd()) {
1138 // Don't care, since we don't have mapping for this index
1139 return;
1140 }
1141 Mapping *m = it.value();
1142
1143 // Figure out how the source changes affect us
1144 QVector<int> source_rows_remove;
1145 QVector<int> source_rows_insert;
1146 QVector<int> source_rows_change;
1147 QVector<int> source_rows_resort;
1148 int end = qMin(source_bottom_right.row(), m->proxy_rows.count() - 1);
1149 for (int source_row = source_top_left.row(); source_row <= end; ++source_row) {
1150 if (dynamic_sortfilter) {
1151 if (m->proxy_rows.at(source_row) != -1) {
1152 if (!q->filterAcceptsRow(source_row, source_parent)) {
1153 // This source row no longer satisfies the filter, so it must be removed
1154 source_rows_remove.append(source_row);
1155 } else if (source_sort_column >= source_top_left.column() && source_sort_column <= source_bottom_right.column()) {
1156 // This source row has changed in a way that may affect sorted order
1157 source_rows_resort.append(source_row);
1158 } else {
1159 // This row has simply changed, without affecting filtering nor sorting
1160 source_rows_change.append(source_row);
1161 }
1162 } else {
1163 if (!itemsBeingRemoved.contains(source_parent, source_row) && q->filterAcceptsRow(source_row, source_parent)) {
1164 // This source row now satisfies the filter, so it must be added
1165 source_rows_insert.append(source_row);
1166 }
1167 }
1168 } else {
1169 if (m->proxy_rows.at(source_row) != -1)
1170 source_rows_change.append(source_row);
1171 }
1172 }
1173
1174 if (!source_rows_remove.isEmpty()) {
1175 remove_source_items(m->proxy_rows, m->source_rows,
1176 source_rows_remove, source_parent, Qt::Vertical);
1177 QSet<int> source_rows_remove_set = qVectorToSet(source_rows_remove);
1178 QVector<QModelIndex>::iterator childIt = m->mapped_children.end();
1179 while (childIt != m->mapped_children.begin()) {
1180 --childIt;
1181 const QModelIndex source_child_index = *childIt;
1182 if (source_rows_remove_set.contains(source_child_index.row())) {
1183 childIt = m->mapped_children.erase(childIt);
1184 remove_from_mapping(source_child_index);
1185 }
1186 }
1187 }
1188
1189 if (!source_rows_resort.isEmpty()) {
1190 // Re-sort the rows of this level
1191 QList<QPersistentModelIndex> parents;
1192 parents << q->mapFromSource(source_parent);
1193 emit q->layoutAboutToBeChanged(parents, QAbstractItemModel::VerticalSortHint);
1194 QModelIndexPairList source_indexes = store_persistent_indexes();
1195 remove_source_items(m->proxy_rows, m->source_rows, source_rows_resort,
1196 source_parent, Qt::Vertical, false);
1197 sort_source_rows(source_rows_resort, source_parent);
1198 insert_source_items(m->proxy_rows, m->source_rows, source_rows_resort,
1199 source_parent, Qt::Vertical, false);
1200 update_persistent_indexes(source_indexes);
1201 emit q->layoutChanged(parents, QAbstractItemModel::VerticalSortHint);
1202 // Make sure we also emit dataChanged for the rows
1203 source_rows_change += source_rows_resort;
1204 }
1205
1206 if (!source_rows_change.isEmpty()) {
1207 // Find the proxy row range
1208 int proxy_start_row;
1209 int proxy_end_row;
1210 proxy_item_range(m->proxy_rows, source_rows_change,
1211 proxy_start_row, proxy_end_row);
1212 // ### Find the proxy column range also
1213 if (proxy_end_row >= 0) {
1214 // the row was accepted, but some columns might still be filtered out
1215 int source_left_column = source_top_left.column();
1216 while (source_left_column < source_bottom_right.column()
1217 && m->proxy_columns.at(source_left_column) == -1)
1218 ++source_left_column;
1219 const QModelIndex proxy_top_left = create_index(
1220 proxy_start_row, m->proxy_columns.at(source_left_column), it);
1221 int source_right_column = source_bottom_right.column();
1222 while (source_right_column > source_top_left.column()
1223 && m->proxy_columns.at(source_right_column) == -1)
1224 --source_right_column;
1225 const QModelIndex proxy_bottom_right = create_index(
1226 proxy_end_row, m->proxy_columns.at(source_right_column), it);
1227 emit q->dataChanged(proxy_top_left, proxy_bottom_right);
1228 }
1229 }
1230
1231 if (!source_rows_insert.isEmpty()) {
1232 sort_source_rows(source_rows_insert, source_parent);
1233 insert_source_items(m->proxy_rows, m->source_rows,
1234 source_rows_insert, source_parent, Qt::Vertical);
1235 }
1236}
1237
1238void QSortFilterProxyModelPrivate::_q_sourceHeaderDataChanged(Qt::Orientation orientation,
1239 int start, int end)
1240{
1241 Q_ASSERT(start <= end);
1242
1243 Q_Q(QSortFilterProxyModel);
1244 Mapping *m = create_mapping(QModelIndex()).value();
1245
1246 const QVector<int> &source_to_proxy = (orientation == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
1247
1248 QVector<int> proxy_positions;
1249 proxy_positions.reserve(end - start + 1);
1250 {
1251 Q_ASSERT(source_to_proxy.size() > end);
1252 QVector<int>::const_iterator it = source_to_proxy.constBegin() + start;
1253 const QVector<int>::const_iterator endIt = source_to_proxy.constBegin() + end + 1;
1254 for ( ; it != endIt; ++it) {
1255 if (*it != -1)
1256 proxy_positions.push_back(*it);
1257 }
1258 }
1259
1260 std::sort(proxy_positions.begin(), proxy_positions.end());
1261
1262 int last_index = 0;
1263 const int numItems = proxy_positions.size();
1264 while (last_index < numItems) {
1265 const int proxyStart = proxy_positions.at(last_index);
1266 int proxyEnd = proxyStart;
1267 ++last_index;
1268 for (int i = last_index; i < numItems; ++i) {
1269 if (proxy_positions.at(i) == proxyEnd + 1) {
1270 ++last_index;
1271 ++proxyEnd;
1272 } else {
1273 break;
1274 }
1275 }
1276 emit q->headerDataChanged(orientation, proxyStart, proxyEnd);
1277 }
1278}
1279
1280void QSortFilterProxyModelPrivate::_q_sourceAboutToBeReset()
1281{
1282 Q_Q(QSortFilterProxyModel);
1283 q->beginResetModel();
1284}
1285
1286void QSortFilterProxyModelPrivate::_q_sourceReset()
1287{
1288 Q_Q(QSortFilterProxyModel);
1289 invalidatePersistentIndexes();
1290 _q_clearMapping();
1291 // All internal structures are deleted in clear()
1292 q->endResetModel();
1293 update_source_sort_column();
1294 if (dynamic_sortfilter)
1295 sort();
1296}
1297
1298void QSortFilterProxyModelPrivate::_q_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
1299{
1300 Q_Q(QSortFilterProxyModel);
1301 saved_persistent_indexes.clear();
1302
1303 QList<QPersistentModelIndex> parents;
1304 foreach (const QPersistentModelIndex &parent, sourceParents) {
1305 if (!parent.isValid()) {
1306 parents << QPersistentModelIndex();
1307 continue;
1308 }
1309 const QModelIndex mappedParent = q->mapFromSource(parent);
1310 // Might be filtered out.
1311 if (mappedParent.isValid())
1312 parents << mappedParent;
1313 }
1314
1315 // All parents filtered out.
1316 if (!sourceParents.isEmpty() && parents.isEmpty())
1317 return;
1318
1319 emit q->layoutAboutToBeChanged(parents, hint);
1320 if (persistent.indexes.isEmpty())
1321 return;
1322
1323 saved_persistent_indexes = store_persistent_indexes();
1324}
1325
1326void QSortFilterProxyModelPrivate::_q_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
1327{
1328 Q_Q(QSortFilterProxyModel);
1329
1330 // Optimize: We only actually have to clear the mapping related to the contents of
1331 // sourceParents, not everything.
1332 qDeleteAll(source_index_mapping);
1333 source_index_mapping.clear();
1334
1335 update_persistent_indexes(saved_persistent_indexes);
1336 saved_persistent_indexes.clear();
1337
1338 if (dynamic_sortfilter && update_source_sort_column()) {
1339 //update_source_sort_column might have created wrong mapping so we have to clear it again
1340 qDeleteAll(source_index_mapping);
1341 source_index_mapping.clear();
1342 }
1343
1344 QList<QPersistentModelIndex> parents;
1345 foreach (const QPersistentModelIndex &parent, sourceParents) {
1346 if (!parent.isValid()) {
1347 parents << QPersistentModelIndex();
1348 continue;
1349 }
1350 const QModelIndex mappedParent = q->mapFromSource(parent);
1351 if (mappedParent.isValid())
1352 parents << mappedParent;
1353 }
1354
1355 if (!sourceParents.isEmpty() && parents.isEmpty())
1356 return;
1357
1358 emit q->layoutChanged(parents, hint);
1359}
1360
1361void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeInserted(
1362 const QModelIndex &source_parent, int start, int end)
1363{
1364 Q_UNUSED(start);
1365 Q_UNUSED(end);
1366 //Force the creation of a mapping now, even if its empty.
1367 //We need it because the proxy can be acessed at the moment it emits rowsAboutToBeInserted in insert_source_items
1368 if (can_create_mapping(source_parent))
1369 create_mapping(source_parent);
1370}
1371
1372void QSortFilterProxyModelPrivate::_q_sourceRowsInserted(
1373 const QModelIndex &source_parent, int start, int end)
1374{
1375 source_items_inserted(source_parent, start, end, Qt::Vertical);
1376 if (update_source_sort_column() && dynamic_sortfilter) //previous call to update_source_sort_column may fail if the model has no column.
1377 sort(); // now it should succeed so we need to make sure to sort again
1378}
1379
1380void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeRemoved(
1381 const QModelIndex &source_parent, int start, int end)
1382{
1383 itemsBeingRemoved = QRowsRemoval(source_parent, start, end);
1384 source_items_about_to_be_removed(source_parent, start, end,
1385 Qt::Vertical);
1386}
1387
1388void QSortFilterProxyModelPrivate::_q_sourceRowsRemoved(
1389 const QModelIndex &source_parent, int start, int end)
1390{
1391 itemsBeingRemoved = QRowsRemoval();
1392 source_items_removed(source_parent, start, end, Qt::Vertical);
1393}
1394
1395void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeMoved(
1396 const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1397{
1398 Q_Q(QSortFilterProxyModel);
1399 // Because rows which are contiguous in the source model might not be contiguous
1400 // in the proxy due to sorting, the best thing we can do here is be specific about what
1401 // parents are having their children changed.
1402 // Optimize: Emit move signals if the proxy is not sorted. Will need to account for rows
1403 // being filtered out though.
1404
1405 saved_persistent_indexes.clear();
1406
1407 QList<QPersistentModelIndex> parents;
1408 parents << q->mapFromSource(sourceParent);
1409 if (sourceParent != destParent)
1410 parents << q->mapFromSource(destParent);
1411 emit q->layoutAboutToBeChanged(parents);
1412 if (persistent.indexes.isEmpty())
1413 return;
1414 saved_persistent_indexes = store_persistent_indexes();
1415}
1416
1417void QSortFilterProxyModelPrivate::_q_sourceRowsMoved(
1418 const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1419{
1420 Q_Q(QSortFilterProxyModel);
1421
1422 // Optimize: We only need to clear and update the persistent indexes which are children of
1423 // sourceParent or destParent
1424 qDeleteAll(source_index_mapping);
1425 source_index_mapping.clear();
1426
1427 update_persistent_indexes(saved_persistent_indexes);
1428 saved_persistent_indexes.clear();
1429
1430 if (dynamic_sortfilter && update_source_sort_column()) {
1431 //update_source_sort_column might have created wrong mapping so we have to clear it again
1432 qDeleteAll(source_index_mapping);
1433 source_index_mapping.clear();
1434 }
1435
1436 QList<QPersistentModelIndex> parents;
1437 parents << q->mapFromSource(sourceParent);
1438 if (sourceParent != destParent)
1439 parents << q->mapFromSource(destParent);
1440 emit q->layoutChanged(parents);
1441}
1442
1443void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeInserted(
1444 const QModelIndex &source_parent, int start, int end)
1445{
1446 Q_UNUSED(start);
1447 Q_UNUSED(end);
1448 //Force the creation of a mapping now, even if its empty.
1449 //We need it because the proxy can be acessed at the moment it emits columnsAboutToBeInserted in insert_source_items
1450 if (can_create_mapping(source_parent))
1451 create_mapping(source_parent);
1452}
1453
1454void QSortFilterProxyModelPrivate::_q_sourceColumnsInserted(
1455 const QModelIndex &source_parent, int start, int end)
1456{
1457 Q_Q(const QSortFilterProxyModel);
1458 source_items_inserted(source_parent, start, end, Qt::Horizontal);
1459
1460 if (source_parent.isValid())
1461 return; //we sort according to the root column only
1462 if (source_sort_column == -1) {
1463 //we update the source_sort_column depending on the proxy_sort_column
1464 if (update_source_sort_column() && dynamic_sortfilter)
1465 sort();
1466 } else {
1467 if (start <= source_sort_column)
1468 source_sort_column += end - start + 1;
1469
1470 proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column();
1471 }
1472}
1473
1474void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeRemoved(
1475 const QModelIndex &source_parent, int start, int end)
1476{
1477 source_items_about_to_be_removed(source_parent, start, end,
1478 Qt::Horizontal);
1479}
1480
1481void QSortFilterProxyModelPrivate::_q_sourceColumnsRemoved(
1482 const QModelIndex &source_parent, int start, int end)
1483{
1484 Q_Q(const QSortFilterProxyModel);
1485 source_items_removed(source_parent, start, end, Qt::Horizontal);
1486
1487 if (source_parent.isValid())
1488 return; //we sort according to the root column only
1489 if (start <= source_sort_column) {
1490 if (end < source_sort_column)
1491 source_sort_column -= end - start + 1;
1492 else
1493 source_sort_column = -1;
1494 }
1495
1496 proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column();
1497}
1498
1499void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeMoved(
1500 const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1501{
1502 Q_Q(QSortFilterProxyModel);
1503
1504 saved_persistent_indexes.clear();
1505
1506 QList<QPersistentModelIndex> parents;
1507 parents << q->mapFromSource(sourceParent);
1508 if (sourceParent != destParent)
1509 parents << q->mapFromSource(destParent);
1510 emit q->layoutAboutToBeChanged(parents);
1511
1512 if (persistent.indexes.isEmpty())
1513 return;
1514 saved_persistent_indexes = store_persistent_indexes();
1515}
1516
1517void QSortFilterProxyModelPrivate::_q_sourceColumnsMoved(
1518 const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1519{
1520 Q_Q(QSortFilterProxyModel);
1521
1522 qDeleteAll(source_index_mapping);
1523 source_index_mapping.clear();
1524
1525 update_persistent_indexes(saved_persistent_indexes);
1526 saved_persistent_indexes.clear();
1527
1528 if (dynamic_sortfilter && update_source_sort_column()) {
1529 qDeleteAll(source_index_mapping);
1530 source_index_mapping.clear();
1531 }
1532
1533 QList<QPersistentModelIndex> parents;
1534 parents << q->mapFromSource(sourceParent);
1535 if (sourceParent != destParent)
1536 parents << q->mapFromSource(destParent);
1537 emit q->layoutChanged(parents);
1538}
1539
1540/*!
1541 \since 4.1
1542 \class QSortFilterProxyModel
1543 \inmodule QtCore
1544 \brief The QSortFilterProxyModel class provides support for sorting and
1545 filtering data passed between another model and a view.
1546
1547 \ingroup model-view
1548
1549 QSortFilterProxyModel can be used for sorting items, filtering out items,
1550 or both. The model transforms the structure of a source model by mapping
1551 the model indexes it supplies to new indexes, corresponding to different
1552 locations, for views to use. This approach allows a given source model to
1553 be restructured as far as views are concerned without requiring any
1554 transformations on the underlying data, and without duplicating the data in
1555 memory.
1556
1557 Let's assume that we want to sort and filter the items provided by a custom
1558 model. The code to set up the model and the view, \e without sorting and
1559 filtering, would look like this:
1560
1561 \snippet qsortfilterproxymodel-details/main.cpp 1
1562
1563 To add sorting and filtering support to \c MyItemModel, we need to create
1564 a QSortFilterProxyModel, call setSourceModel() with the \c MyItemModel as
1565 argument, and install the QSortFilterProxyModel on the view:
1566
1567 \snippet qsortfilterproxymodel-details/main.cpp 0
1568 \snippet qsortfilterproxymodel-details/main.cpp 2
1569
1570 At this point, neither sorting nor filtering is enabled; the original data
1571 is displayed in the view. Any changes made through the
1572 QSortFilterProxyModel are applied to the original model.
1573
1574 The QSortFilterProxyModel acts as a wrapper for the original model. If you
1575 need to convert source \l{QModelIndex}es to sorted/filtered model indexes
1576 or vice versa, use mapToSource(), mapFromSource(), mapSelectionToSource(),
1577 and mapSelectionFromSource().
1578
1579 \note By default, the model dynamically re-sorts and re-filters data
1580 whenever the original model changes. This behavior can be changed by
1581 setting the \l{QSortFilterProxyModel::dynamicSortFilter}{dynamicSortFilter}
1582 property.
1583
1584 The \l{itemviews/basicsortfiltermodel}{Basic Sort/Filter Model} and
1585 \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model} examples
1586 illustrate how to use QSortFilterProxyModel to perform basic sorting and
1587 filtering and how to subclass it to implement custom behavior.
1588
1589 \section1 Sorting
1590
1591 QTableView and QTreeView have a
1592 \l{QTreeView::sortingEnabled}{sortingEnabled} property that controls
1593 whether the user can sort the view by clicking the view's horizontal
1594 header. For example:
1595
1596 \snippet qsortfilterproxymodel-details/main.cpp 3
1597
1598 When this feature is on (the default is off), clicking on a header section
1599 sorts the items according to that column. By clicking repeatedly, the user
1600 can alternate between ascending and descending order.
1601
1602 \image qsortfilterproxymodel-sorting.png A sorted QTreeView
1603
1604 Behind the scene, the view calls the sort() virtual function on the model
1605 to reorder the data in the model. To make your data sortable, you can
1606 either implement sort() in your model, or use a QSortFilterProxyModel to
1607 wrap your model -- QSortFilterProxyModel provides a generic sort()
1608 reimplementation that operates on the sortRole() (Qt::DisplayRole by
1609 default) of the items and that understands several data types, including
1610 \c int, QString, and QDateTime. For hierarchical models, sorting is applied
1611 recursively to all child items. String comparisons are case sensitive by
1612 default; this can be changed by setting the \l{QSortFilterProxyModel::}
1613 {sortCaseSensitivity} property.
1614
1615 Custom sorting behavior is achieved by subclassing
1616 QSortFilterProxyModel and reimplementing lessThan(), which is
1617 used to compare items. For example:
1618
1619 \snippet ../widgets/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 5
1620
1621 (This code snippet comes from the
1622 \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model}
1623 example.)
1624
1625 An alternative approach to sorting is to disable sorting on the view and to
1626 impose a certain order to the user. This is done by explicitly calling
1627 sort() with the desired column and order as arguments on the
1628 QSortFilterProxyModel (or on the original model if it implements sort()).
1629 For example:
1630
1631 \snippet qsortfilterproxymodel-details/main.cpp 4
1632
1633 QSortFilterProxyModel can be sorted by column -1, in which case it returns
1634 to the sort order of the underlying source model.
1635
1636 \section1 Filtering
1637
1638 In addition to sorting, QSortFilterProxyModel can be used to hide items
1639 that do not match a certain filter. The filter is specified using a QRegExp
1640 object and is applied to the filterRole() (Qt::DisplayRole by default) of
1641 each item, for a given column. The QRegExp object can be used to match a
1642 regular expression, a wildcard pattern, or a fixed string. For example:
1643
1644 \snippet qsortfilterproxymodel-details/main.cpp 5
1645
1646 For hierarchical models, the filter is applied recursively to all children.
1647 If a parent item doesn't match the filter, none of its children will be
1648 shown.
1649
1650 A common use case is to let the user specify the filter regexp, wildcard
1651 pattern, or fixed string in a QLineEdit and to connect the
1652 \l{QLineEdit::textChanged()}{textChanged()} signal to setFilterRegExp(),
1653 setFilterWildcard(), or setFilterFixedString() to reapply the filter.
1654
1655 Custom filtering behavior can be achieved by reimplementing the
1656 filterAcceptsRow() and filterAcceptsColumn() functions. For
1657 example (from the \l{itemviews/customsortfiltermodel}
1658 {Custom Sort/Filter Model} example), the following implementation ignores
1659 the \l{QSortFilterProxyModel::filterKeyColumn}{filterKeyColumn} property
1660 and performs filtering on columns 0, 1, and 2:
1661
1662 \snippet ../widgets/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 3
1663
1664 (This code snippet comes from the
1665 \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model}
1666 example.)
1667
1668 If you are working with large amounts of filtering and have to invoke
1669 invalidateFilter() repeatedly, using reset() may be more efficient,
1670 depending on the implementation of your model. However, reset() returns the
1671 proxy model to its original state, losing selection information, and will
1672 cause the proxy model to be repopulated.
1673
1674 \section1 Subclassing
1675
1676 Since QAbstractProxyModel and its subclasses are derived from
1677 QAbstractItemModel, much of the same advice about subclassing normal models
1678 also applies to proxy models. In addition, it is worth noting that many of
1679 the default implementations of functions in this class are written so that
1680 they call the equivalent functions in the relevant source model. This
1681 simple proxying mechanism may need to be overridden for source models with
1682 more complex behavior; for example, if the source model provides a custom
1683 hasChildren() implementation, you should also provide one in the proxy
1684 model.
1685
1686 \note Some general guidelines for subclassing models are available in the
1687 \l{Model Subclassing Reference}.
1688
1689 \sa QAbstractProxyModel, QAbstractItemModel, {Model/View Programming},
1690 {Basic Sort/Filter Model Example}, {Custom Sort/Filter Model Example}, QIdentityProxyModel
1691*/
1692
1693/*!
1694 Constructs a sorting filter model with the given \a parent.
1695*/
1696
1697QSortFilterProxyModel::QSortFilterProxyModel(QObject *parent)
1698 : QAbstractProxyModel(*new QSortFilterProxyModelPrivate, parent)
1699{
1700 Q_D(QSortFilterProxyModel);
1701 d->proxy_sort_column = d->source_sort_column = -1;
1702 d->sort_order = Qt::AscendingOrder;
1703 d->sort_casesensitivity = Qt::CaseSensitive;
1704 d->sort_role = Qt::DisplayRole;
1705 d->sort_localeaware = false;
1706 d->filter_column = 0;
1707 d->filter_role = Qt::DisplayRole;
1708 d->dynamic_sortfilter = true;
1709 connect(this, SIGNAL(modelReset()), this, SLOT(_q_clearMapping()));
1710}
1711
1712/*!
1713 Destroys this sorting filter model.
1714*/
1715QSortFilterProxyModel::~QSortFilterProxyModel()
1716{
1717 Q_D(QSortFilterProxyModel);
1718 qDeleteAll(d->source_index_mapping);
1719 d->source_index_mapping.clear();
1720}
1721
1722/*!
1723 \reimp
1724*/
1725void QSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
1726{
1727 Q_D(QSortFilterProxyModel);
1728
1729 beginResetModel();
1730
1731 disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
1732 this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex)));
1733
1734 disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
1735 this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int)));
1736
1737 disconnect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
1738 this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int)));
1739
1740 disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
1741 this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int)));
1742
1743 disconnect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
1744 this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int)));
1745
1746 disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
1747 this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int)));
1748
1749 disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
1750 this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
1751
1752 disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
1753 this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int)));
1754
1755 disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
1756 this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int)));
1757
1758 disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
1759 this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int)));
1760
1761 disconnect(d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
1762 this, SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
1763
1764 disconnect(d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
1765 this, SLOT(_q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)));
1766
1767 disconnect(d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
1768 this, SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
1769
1770 disconnect(d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
1771 this, SLOT(_q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int)));
1772
1773 disconnect(d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
1774 this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
1775
1776 disconnect(d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
1777 this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
1778
1779 disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_sourceAboutToBeReset()));
1780 disconnect(d->model, SIGNAL(modelReset()), this, SLOT(_q_sourceReset()));
1781
1782 QAbstractProxyModel::setSourceModel(sourceModel);
1783
1784 connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
1785 this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex)));
1786
1787 connect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
1788 this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int)));
1789
1790 connect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
1791 this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int)));
1792
1793 connect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
1794 this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int)));
1795
1796 connect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
1797 this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int)));
1798
1799 connect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
1800 this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int)));
1801
1802 connect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
1803 this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
1804
1805 connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
1806 this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int)));
1807
1808 connect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
1809 this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int)));
1810
1811 connect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
1812 this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int)));
1813
1814 connect(d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
1815 this, SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
1816
1817 connect(d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
1818 this, SLOT(_q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)));
1819
1820 connect(d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
1821 this, SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
1822
1823 connect(d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
1824 this, SLOT(_q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int)));
1825
1826 connect(d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
1827 this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
1828
1829 connect(d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
1830 this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
1831
1832 connect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_sourceAboutToBeReset()));
1833 connect(d->model, SIGNAL(modelReset()), this, SLOT(_q_sourceReset()));
1834
1835 d->_q_clearMapping();
1836 endResetModel();
1837 if (d->update_source_sort_column() && d->dynamic_sortfilter)
1838 d->sort();
1839}
1840
1841/*!
1842 \reimp
1843*/
1844QModelIndex QSortFilterProxyModel::index(int row, int column, const QModelIndex &parent) const
1845{
1846 Q_D(const QSortFilterProxyModel);
1847 if (row < 0 || column < 0)
1848 return QModelIndex();
1849
1850 QModelIndex source_parent = mapToSource(parent); // parent is already mapped at this point
1851 IndexMap::const_iterator it = d->create_mapping(source_parent); // but make sure that the children are mapped
1852 if (it.value()->source_rows.count() <= row || it.value()->source_columns.count() <= column)
1853 return QModelIndex();
1854
1855 return d->create_index(row, column, it);
1856}
1857
1858/*!
1859 \reimp
1860*/
1861QModelIndex QSortFilterProxyModel::parent(const QModelIndex &child) const
1862{
1863 Q_D(const QSortFilterProxyModel);
1864 if (!d->indexValid(child))
1865 return QModelIndex();
1866 IndexMap::const_iterator it = d->index_to_iterator(child);
1867 Q_ASSERT(it != d->source_index_mapping.constEnd());
1868 QModelIndex source_parent = it.key();
1869 QModelIndex proxy_parent = mapFromSource(source_parent);
1870 return proxy_parent;
1871}
1872
1873/*!
1874 \reimp
1875*/
1876QModelIndex QSortFilterProxyModel::sibling(int row, int column, const QModelIndex &idx) const
1877{
1878 Q_D(const QSortFilterProxyModel);
1879 if (!d->indexValid(idx))
1880 return QModelIndex();
1881
1882 const IndexMap::const_iterator it = d->index_to_iterator(idx);
1883 if (it.value()->source_rows.count() <= row || it.value()->source_columns.count() <= column)
1884 return QModelIndex();
1885
1886 return d->create_index(row, column, it);
1887}
1888
1889/*!
1890 \reimp
1891*/
1892int QSortFilterProxyModel::rowCount(const QModelIndex &parent) const
1893{
1894 Q_D(const QSortFilterProxyModel);
1895 QModelIndex source_parent = mapToSource(parent);
1896 if (parent.isValid() && !source_parent.isValid())
1897 return 0;
1898 IndexMap::const_iterator it = d->create_mapping(source_parent);
1899 return it.value()->source_rows.count();
1900}
1901
1902/*!
1903 \reimp
1904*/
1905int QSortFilterProxyModel::columnCount(const QModelIndex &parent) const
1906{
1907 Q_D(const QSortFilterProxyModel);
1908 QModelIndex source_parent = mapToSource(parent);
1909 if (parent.isValid() && !source_parent.isValid())
1910 return 0;
1911 IndexMap::const_iterator it = d->create_mapping(source_parent);
1912 return it.value()->source_columns.count();
1913}
1914
1915/*!
1916 \reimp
1917*/
1918bool QSortFilterProxyModel::hasChildren(const QModelIndex &parent) const
1919{
1920 Q_D(const QSortFilterProxyModel);
1921 QModelIndex source_parent = mapToSource(parent);
1922 if (parent.isValid() && !source_parent.isValid())
1923 return false;
1924 if (!d->model->hasChildren(source_parent))
1925 return false;
1926
1927 if (d->model->canFetchMore(source_parent))
1928 return true; //we assume we might have children that can be fetched
1929
1930 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
1931 return m->source_rows.count() != 0 && m->source_columns.count() != 0;
1932}
1933
1934/*!
1935 \reimp
1936*/
1937QVariant QSortFilterProxyModel::data(const QModelIndex &index, int role) const
1938{
1939 Q_D(const QSortFilterProxyModel);
1940 QModelIndex source_index = mapToSource(index);
1941 if (index.isValid() && !source_index.isValid())
1942 return QVariant();
1943 return d->model->data(source_index, role);
1944}
1945
1946/*!
1947 \reimp
1948*/
1949bool QSortFilterProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
1950{
1951 Q_D(QSortFilterProxyModel);
1952 QModelIndex source_index = mapToSource(index);
1953 if (index.isValid() && !source_index.isValid())
1954 return false;
1955 return d->model->setData(source_index, value, role);
1956}
1957
1958/*!
1959 \reimp
1960*/
1961QVariant QSortFilterProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
1962{
1963 Q_D(const QSortFilterProxyModel);
1964 IndexMap::const_iterator it = d->create_mapping(QModelIndex());
1965 if (it.value()->source_rows.count() * it.value()->source_columns.count() > 0)
1966 return QAbstractProxyModel::headerData(section, orientation, role);
1967 int source_section;
1968 if (orientation == Qt::Vertical) {
1969 if (section < 0 || section >= it.value()->source_rows.count())
1970 return QVariant();
1971 source_section = it.value()->source_rows.at(section);
1972 } else {
1973 if (section < 0 || section >= it.value()->source_columns.count())
1974 return QVariant();
1975 source_section = it.value()->source_columns.at(section);
1976 }
1977 return d->model->headerData(source_section, orientation, role);
1978}
1979
1980/*!
1981 \reimp
1982*/
1983bool QSortFilterProxyModel::setHeaderData(int section, Qt::Orientation orientation,
1984 const QVariant &value, int role)
1985{
1986 Q_D(QSortFilterProxyModel);
1987 IndexMap::const_iterator it = d->create_mapping(QModelIndex());
1988 if (it.value()->source_rows.count() * it.value()->source_columns.count() > 0)
1989 return QAbstractProxyModel::setHeaderData(section, orientation, value, role);
1990 int source_section;
1991 if (orientation == Qt::Vertical) {
1992 if (section < 0 || section >= it.value()->source_rows.count())
1993 return false;
1994 source_section = it.value()->source_rows.at(section);
1995 } else {
1996 if (section < 0 || section >= it.value()->source_columns.count())
1997 return false;
1998 source_section = it.value()->source_columns.at(section);
1999 }
2000 return d->model->setHeaderData(source_section, orientation, value, role);
2001}
2002
2003/*!
2004 \reimp
2005*/
2006QMimeData *QSortFilterProxyModel::mimeData(const QModelIndexList &indexes) const
2007{
2008 Q_D(const QSortFilterProxyModel);
2009 QModelIndexList source_indexes;
2010 for (int i = 0; i < indexes.count(); ++i)
2011 source_indexes << mapToSource(indexes.at(i));
2012 return d->model->mimeData(source_indexes);
2013}
2014
2015/*!
2016 \reimp
2017*/
2018QStringList QSortFilterProxyModel::mimeTypes() const
2019{
2020 Q_D(const QSortFilterProxyModel);
2021 return d->model->mimeTypes();
2022}
2023
2024/*!
2025 \reimp
2026*/
2027Qt::DropActions QSortFilterProxyModel::supportedDropActions() const
2028{
2029 Q_D(const QSortFilterProxyModel);
2030 return d->model->supportedDropActions();
2031}
2032
2033/*!
2034 \reimp
2035*/
2036bool QSortFilterProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
2037 int row, int column, const QModelIndex &parent)
2038{
2039 Q_D(QSortFilterProxyModel);
2040 if ((row == -1) && (column == -1))
2041 return d->model->dropMimeData(data, action, -1, -1, mapToSource(parent));
2042 int source_destination_row = -1;
2043 int source_destination_column = -1;
2044 QModelIndex source_parent;
2045 if (row == rowCount(parent)) {
2046 source_parent = mapToSource(parent);
2047 source_destination_row = d->model->rowCount(source_parent);
2048 } else {
2049 QModelIndex proxy_index = index(row, column, parent);
2050 QModelIndex source_index = mapToSource(proxy_index);
2051 source_destination_row = source_index.row();
2052 source_destination_column = source_index.column();
2053 source_parent = source_index.parent();
2054 }
2055 return d->model->dropMimeData(data, action, source_destination_row,
2056 source_destination_column, source_parent);
2057}
2058
2059/*!
2060 \reimp
2061*/
2062bool QSortFilterProxyModel::insertRows(int row, int count, const QModelIndex &parent)
2063{
2064 Q_D(QSortFilterProxyModel);
2065 if (row < 0 || count <= 0)
2066 return false;
2067 QModelIndex source_parent = mapToSource(parent);
2068 if (parent.isValid() && !source_parent.isValid())
2069 return false;
2070 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2071 if (row > m->source_rows.count())
2072 return false;
2073 int source_row = (row >= m->source_rows.count()
2074 ? m->source_rows.count()
2075 : m->source_rows.at(row));
2076 return d->model->insertRows(source_row, count, source_parent);
2077}
2078
2079/*!
2080 \reimp
2081*/
2082bool QSortFilterProxyModel::insertColumns(int column, int count, const QModelIndex &parent)
2083{
2084 Q_D(QSortFilterProxyModel);
2085 if (column < 0|| count <= 0)
2086 return false;
2087 QModelIndex source_parent = mapToSource(parent);
2088 if (parent.isValid() && !source_parent.isValid())
2089 return false;
2090 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2091 if (column > m->source_columns.count())
2092 return false;
2093 int source_column = (column >= m->source_columns.count()
2094 ? m->source_columns.count()
2095 : m->source_columns.at(column));
2096 return d->model->insertColumns(source_column, count, source_parent);
2097}
2098
2099/*!
2100 \reimp
2101*/
2102bool QSortFilterProxyModel::removeRows(int row, int count, const QModelIndex &parent)
2103{
2104 Q_D(QSortFilterProxyModel);
2105 if (row < 0 || count <= 0)
2106 return false;
2107 QModelIndex source_parent = mapToSource(parent);
2108 if (parent.isValid() && !source_parent.isValid())
2109 return false;
2110 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2111 if (row + count > m->source_rows.count())
2112 return false;
2113 if ((count == 1)
2114 || ((d->source_sort_column < 0) && (m->proxy_rows.count() == m->source_rows.count()))) {
2115 int source_row = m->source_rows.at(row);
2116 return d->model->removeRows(source_row, count, source_parent);
2117 }
2118 // remove corresponding source intervals
2119 // ### if this proves to be slow, we can switch to single-row removal
2120 QVector<int> rows;
2121 for (int i = row; i < row + count; ++i)
2122 rows.append(m->source_rows.at(i));
2123 std::sort(rows.begin(), rows.end());
2124
2125 int pos = rows.count() - 1;
2126 bool ok = true;
2127 while (pos >= 0) {
2128 const int source_end = rows.at(pos--);
2129 int source_start = source_end;
2130 while ((pos >= 0) && (rows.at(pos) == (source_start - 1))) {
2131 --source_start;
2132 --pos;
2133 }
2134 ok = ok && d->model->removeRows(source_start, source_end - source_start + 1,
2135 source_parent);
2136 }
2137 return ok;
2138}
2139
2140/*!
2141 \reimp
2142*/
2143bool QSortFilterProxyModel::removeColumns(int column, int count, const QModelIndex &parent)
2144{
2145 Q_D(QSortFilterProxyModel);
2146 if (column < 0 || count <= 0)
2147 return false;
2148 QModelIndex source_parent = mapToSource(parent);
2149 if (parent.isValid() && !source_parent.isValid())
2150 return false;
2151 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2152 if (column + count > m->source_columns.count())
2153 return false;
2154 if ((count == 1) || (m->proxy_columns.count() == m->source_columns.count())) {
2155 int source_column = m->source_columns.at(column);
2156 return d->model->removeColumns(source_column, count, source_parent);
2157 }
2158 // remove corresponding source intervals
2159 QVector<int> columns;
2160 for (int i = column; i < column + count; ++i)
2161 columns.append(m->source_columns.at(i));
2162
2163 int pos = columns.count() - 1;
2164 bool ok = true;
2165 while (pos >= 0) {
2166 const int source_end = columns.at(pos--);
2167 int source_start = source_end;
2168 while ((pos >= 0) && (columns.at(pos) == (source_start - 1))) {
2169 --source_start;
2170 --pos;
2171 }
2172 ok = ok && d->model->removeColumns(source_start, source_end - source_start + 1,
2173 source_parent);
2174 }
2175 return ok;
2176}
2177
2178/*!
2179 \reimp
2180*/
2181void QSortFilterProxyModel::fetchMore(const QModelIndex &parent)
2182{
2183 Q_D(QSortFilterProxyModel);
2184 QModelIndex source_parent;
2185 if (d->indexValid(parent))
2186 source_parent = mapToSource(parent);
2187 d->model->fetchMore(source_parent);
2188}
2189
2190/*!
2191 \reimp
2192*/
2193bool QSortFilterProxyModel::canFetchMore(const QModelIndex &parent) const
2194{
2195 Q_D(const QSortFilterProxyModel);
2196 QModelIndex source_parent;
2197 if (d->indexValid(parent))
2198 source_parent = mapToSource(parent);
2199 return d->model->canFetchMore(source_parent);
2200}
2201
2202/*!
2203 \reimp
2204*/
2205Qt::ItemFlags QSortFilterProxyModel::flags(const QModelIndex &index) const
2206{
2207 Q_D(const QSortFilterProxyModel);
2208 QModelIndex source_index;
2209 if (d->indexValid(index))
2210 source_index = mapToSource(index);
2211 return d->model->flags(source_index);
2212}
2213
2214/*!
2215 \reimp
2216*/
2217QModelIndex QSortFilterProxyModel::buddy(const QModelIndex &index) const
2218{
2219 Q_D(const QSortFilterProxyModel);
2220 if (!d->indexValid(index))
2221 return QModelIndex();
2222 QModelIndex source_index = mapToSource(index);
2223 QModelIndex source_buddy = d->model->buddy(source_index);
2224 if (source_index == source_buddy)
2225 return index;
2226 return mapFromSource(source_buddy);
2227}
2228
2229/*!
2230 \reimp
2231*/
2232QModelIndexList QSortFilterProxyModel::match(const QModelIndex &start, int role,
2233 const QVariant &value, int hits,
2234 Qt::MatchFlags flags) const
2235{
2236 return QAbstractProxyModel::match(start, role, value, hits, flags);
2237}
2238
2239/*!
2240 \reimp
2241*/
2242QSize QSortFilterProxyModel::span(const QModelIndex &index) const
2243{
2244 Q_D(const QSortFilterProxyModel);
2245 QModelIndex source_index = mapToSource(index);
2246 if (index.isValid() && !source_index.isValid())
2247 return QSize();
2248 return d->model->span(source_index);
2249}
2250
2251/*!
2252 \reimp
2253*/
2254void QSortFilterProxyModel::sort(int column, Qt::SortOrder order)
2255{
2256 Q_D(QSortFilterProxyModel);
2257 if (d->dynamic_sortfilter && d->proxy_sort_column == column && d->sort_order == order)
2258 return;
2259 d->sort_order = order;
2260 d->proxy_sort_column = column;
2261 d->update_source_sort_column();
2262 d->sort();
2263}
2264
2265/*!
2266 \since 4.5
2267 \brief the column currently used for sorting
2268
2269 This returns the most recently used sort column.
2270*/
2271int QSortFilterProxyModel::sortColumn() const
2272{
2273 Q_D(const QSortFilterProxyModel);
2274 return d->proxy_sort_column;
2275}
2276
2277/*!
2278 \since 4.5
2279 \brief the order currently used for sorting
2280
2281 This returns the most recently used sort order.
2282*/
2283Qt::SortOrder QSortFilterProxyModel::sortOrder() const
2284{
2285 Q_D(const QSortFilterProxyModel);
2286 return d->sort_order;
2287}
2288
2289/*!
2290 \property QSortFilterProxyModel::filterRegExp
2291 \brief the QRegExp used to filter the contents of the source model
2292
2293 Setting this property overwrites the current
2294 \l{QSortFilterProxyModel::filterCaseSensitivity}{filterCaseSensitivity}.
2295 By default, the QRegExp is an empty string matching all contents.
2296
2297 If no QRegExp or an empty string is set, everything in the source model
2298 will be accepted.
2299
2300 \sa filterCaseSensitivity, setFilterWildcard(), setFilterFixedString()
2301*/
2302QRegExp QSortFilterProxyModel::filterRegExp() const
2303{
2304 Q_D(const QSortFilterProxyModel);
2305 return d->filter_regexp;
2306}
2307
2308void QSortFilterProxyModel::setFilterRegExp(const QRegExp &regExp)
2309{
2310 Q_D(QSortFilterProxyModel);
2311 d->filter_regexp = regExp;
2312 d->filter_changed();
2313}
2314
2315/*!
2316 \property QSortFilterProxyModel::filterKeyColumn
2317 \brief the column where the key used to filter the contents of the
2318 source model is read from.
2319
2320 The default value is 0. If the value is -1, the keys will be read
2321 from all columns.
2322*/
2323int QSortFilterProxyModel::filterKeyColumn() const
2324{
2325 Q_D(const QSortFilterProxyModel);
2326 return d->filter_column;
2327}
2328
2329void QSortFilterProxyModel::setFilterKeyColumn(int column)
2330{
2331 Q_D(QSortFilterProxyModel);
2332 d->filter_column = column;
2333 d->filter_changed();
2334}
2335
2336/*!
2337 \property QSortFilterProxyModel::filterCaseSensitivity
2338
2339 \brief the case sensitivity of the QRegExp pattern used to filter the
2340 contents of the source model
2341
2342 By default, the filter is case sensitive.
2343
2344 \sa filterRegExp, sortCaseSensitivity
2345*/
2346Qt::CaseSensitivity QSortFilterProxyModel::filterCaseSensitivity() const
2347{
2348 Q_D(const QSortFilterProxyModel);
2349 return d->filter_regexp.caseSensitivity();
2350}
2351
2352void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs)
2353{
2354 Q_D(QSortFilterProxyModel);
2355 if (cs == d->filter_regexp.caseSensitivity())
2356 return;
2357 d->filter_regexp.setCaseSensitivity(cs);
2358 d->filter_changed();
2359}
2360
2361/*!
2362 \since 4.2
2363 \property QSortFilterProxyModel::sortCaseSensitivity
2364 \brief the case sensitivity setting used for comparing strings when sorting
2365
2366 By default, sorting is case sensitive.
2367
2368 \sa filterCaseSensitivity, lessThan()
2369*/
2370Qt::CaseSensitivity QSortFilterProxyModel::sortCaseSensitivity() const
2371{
2372 Q_D(const QSortFilterProxyModel);
2373 return d->sort_casesensitivity;
2374}
2375
2376void QSortFilterProxyModel::setSortCaseSensitivity(Qt::CaseSensitivity cs)
2377{
2378 Q_D(QSortFilterProxyModel);
2379 if (d->sort_casesensitivity == cs)
2380 return;
2381
2382 d->sort_casesensitivity = cs;
2383 d->sort();
2384}
2385
2386/*!
2387 \since 4.3
2388 \property QSortFilterProxyModel::isSortLocaleAware
2389 \brief the local aware setting used for comparing strings when sorting
2390
2391 By default, sorting is not local aware.
2392
2393 \sa sortCaseSensitivity, lessThan()
2394*/
2395bool QSortFilterProxyModel::isSortLocaleAware() const
2396{
2397 Q_D(const QSortFilterProxyModel);
2398 return d->sort_localeaware;
2399}
2400
2401void QSortFilterProxyModel::setSortLocaleAware(bool on)
2402{
2403 Q_D(QSortFilterProxyModel);
2404 if (d->sort_localeaware == on)
2405 return;
2406
2407 d->sort_localeaware = on;
2408 d->sort();
2409}
2410
2411/*!
2412 \overload
2413
2414 Sets the regular expression used to filter the contents
2415 of the source model to \a pattern.
2416
2417 \sa setFilterCaseSensitivity(), setFilterWildcard(), setFilterFixedString(), filterRegExp()
2418*/
2419void QSortFilterProxyModel::setFilterRegExp(const QString &pattern)
2420{
2421 Q_D(QSortFilterProxyModel);
2422 d->filter_regexp.setPatternSyntax(QRegExp::RegExp);
2423 d->filter_regexp.setPattern(pattern);
2424 d->filter_changed();
2425}
2426
2427/*!
2428 Sets the wildcard expression used to filter the contents
2429 of the source model to the given \a pattern.
2430
2431 \sa setFilterCaseSensitivity(), setFilterRegExp(), setFilterFixedString(), filterRegExp()
2432*/
2433void QSortFilterProxyModel::setFilterWildcard(const QString &pattern)
2434{
2435 Q_D(QSortFilterProxyModel);
2436 d->filter_regexp.setPatternSyntax(QRegExp::Wildcard);
2437 d->filter_regexp.setPattern(pattern);
2438 d->filter_changed();
2439}
2440
2441/*!
2442 Sets the fixed string used to filter the contents
2443 of the source model to the given \a pattern.
2444
2445 \sa setFilterCaseSensitivity(), setFilterRegExp(), setFilterWildcard(), filterRegExp()
2446*/
2447void QSortFilterProxyModel::setFilterFixedString(const QString &pattern)
2448{
2449 Q_D(QSortFilterProxyModel);
2450 d->filter_regexp.setPatternSyntax(QRegExp::FixedString);
2451 d->filter_regexp.setPattern(pattern);
2452 d->filter_changed();
2453}
2454
2455/*!
2456 \since 4.2
2457 \property QSortFilterProxyModel::dynamicSortFilter
2458 \brief whether the proxy model is dynamically sorted and filtered
2459 whenever the contents of the source model change
2460
2461 Note that you should not update the source model through the proxy
2462 model when dynamicSortFilter is true. For instance, if you set the
2463 proxy model on a QComboBox, then using functions that update the
2464 model, e.g., \l{QComboBox::}{addItem()}, will not work as
2465 expected. An alternative is to set dynamicSortFilter to false and
2466 call \l{QSortFilterProxyModel::}{sort()} after adding items to the
2467 QComboBox.
2468
2469 The default value is true.
2470*/
2471bool QSortFilterProxyModel::dynamicSortFilter() const
2472{
2473 Q_D(const QSortFilterProxyModel);
2474 return d->dynamic_sortfilter;
2475}
2476
2477void QSortFilterProxyModel::setDynamicSortFilter(bool enable)
2478{
2479 Q_D(QSortFilterProxyModel);
2480 d->dynamic_sortfilter = enable;
2481 if (enable)
2482 d->sort();
2483}
2484
2485/*!
2486 \since 4.2
2487 \property QSortFilterProxyModel::sortRole
2488 \brief the item role that is used to query the source model's data when sorting items
2489
2490 The default value is Qt::DisplayRole.
2491
2492 \sa lessThan()
2493*/
2494int QSortFilterProxyModel::sortRole() const
2495{
2496 Q_D(const QSortFilterProxyModel);
2497 return d->sort_role;
2498}
2499
2500void QSortFilterProxyModel::setSortRole(int role)
2501{
2502 Q_D(QSortFilterProxyModel);
2503 if (d->sort_role == role)
2504 return;
2505 d->sort_role = role;
2506 d->sort();
2507}
2508
2509/*!
2510 \since 4.2
2511 \property QSortFilterProxyModel::filterRole
2512 \brief the item role that is used to query the source model's data when filtering items
2513
2514 The default value is Qt::DisplayRole.
2515
2516 \sa filterAcceptsRow()
2517*/
2518int QSortFilterProxyModel::filterRole() const
2519{
2520 Q_D(const QSortFilterProxyModel);
2521 return d->filter_role;
2522}
2523
2524void QSortFilterProxyModel::setFilterRole(int role)
2525{
2526 Q_D(QSortFilterProxyModel);
2527 if (d->filter_role == role)
2528 return;
2529 d->filter_role = role;
2530 d->filter_changed();
2531}
2532
2533/*!
2534 \obsolete
2535
2536 This function is obsolete. Use invalidate() instead.
2537*/
2538void QSortFilterProxyModel::clear()
2539{
2540 Q_D(QSortFilterProxyModel);
2541 emit layoutAboutToBeChanged();
2542 d->_q_clearMapping();
2543 emit layoutChanged();
2544}
2545
2546/*!
2547 \since 4.3
2548
2549 Invalidates the current sorting and filtering.
2550
2551 \sa invalidateFilter()
2552*/
2553void QSortFilterProxyModel::invalidate()
2554{
2555 Q_D(QSortFilterProxyModel);
2556 emit layoutAboutToBeChanged();
2557 d->_q_clearMapping();
2558 emit layoutChanged();
2559}
2560
2561/*!
2562 \obsolete
2563
2564 This function is obsolete. Use invalidateFilter() instead.
2565*/
2566void QSortFilterProxyModel::filterChanged()
2567{
2568 Q_D(QSortFilterProxyModel);
2569 d->filter_changed();
2570}
2571
2572/*!
2573 \since 4.3
2574
2575 Invalidates the current filtering.
2576
2577 This function should be called if you are implementing custom filtering
2578 (e.g. filterAcceptsRow()), and your filter parameters have changed.
2579
2580 \sa invalidate()
2581*/
2582void QSortFilterProxyModel::invalidateFilter()
2583{
2584 Q_D(QSortFilterProxyModel);
2585 d->filter_changed();
2586}
2587
2588/*!
2589 Returns true if the value of the item referred to by the given
2590 index \a left is less than the value of the item referred to by
2591 the given index \a right, otherwise returns false.
2592
2593 This function is used as the < operator when sorting, and handles
2594 the following QVariant types:
2595
2596 \list
2597 \li QVariant::Int
2598 \li QVariant::UInt
2599 \li QVariant::LongLong
2600 \li QVariant::ULongLong
2601 \li QVariant::Double
2602 \li QVariant::Char
2603 \li QVariant::Date
2604 \li QVariant::Time
2605 \li QVariant::DateTime
2606 \li QVariant::String
2607 \endlist
2608
2609 Any other type will be converted to a QString using
2610 QVariant::toString().
2611
2612 Comparison of \l{QString}s is case sensitive by default; this can
2613 be changed using the \l {QSortFilterProxyModel::sortCaseSensitivity}
2614 {sortCaseSensitivity} property.
2615
2616 By default, the Qt::DisplayRole associated with the
2617 \l{QModelIndex}es is used for comparisons. This can be changed by
2618 setting the \l {QSortFilterProxyModel::sortRole} {sortRole} property.
2619
2620 \note The indices passed in correspond to the source model.
2621
2622 \sa sortRole, sortCaseSensitivity, dynamicSortFilter
2623*/
2624bool QSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
2625{
2626 Q_D(const QSortFilterProxyModel);
2627 QVariant l = (left.model() ? left.model()->data(left, d->sort_role) : QVariant());
2628 QVariant r = (right.model() ? right.model()->data(right, d->sort_role) : QVariant());
2629 switch (l.userType()) {
2630 case QVariant::Invalid:
2631 return (r.type() != QVariant::Invalid);
2632 case QVariant::Int:
2633 return l.toInt() < r.toInt();
2634 case QVariant::UInt:
2635 return l.toUInt() < r.toUInt();
2636 case QVariant::LongLong:
2637 return l.toLongLong() < r.toLongLong();
2638 case QVariant::ULongLong:
2639 return l.toULongLong() < r.toULongLong();
2640 case QMetaType::Float:
2641 return l.toFloat() < r.toFloat();
2642 case QVariant::Double:
2643 return l.toDouble() < r.toDouble();
2644 case QVariant::Char:
2645 return l.toChar() < r.toChar();
2646 case QVariant::Date:
2647 return l.toDate() < r.toDate();
2648 case QVariant::Time:
2649 return l.toTime() < r.toTime();
2650 case QVariant::DateTime:
2651 return l.toDateTime() < r.toDateTime();
2652 case QVariant::String:
2653 default:
2654 if (d->sort_localeaware)
2655 return l.toString().localeAwareCompare(r.toString()) < 0;
2656 else
2657 return l.toString().compare(r.toString(), d->sort_casesensitivity) < 0;
2658 }
2659 return false;
2660}
2661
2662/*!
2663 Returns true if the item in the row indicated by the given \a source_row
2664 and \a source_parent should be included in the model; otherwise returns
2665 false.
2666
2667 The default implementation returns true if the value held by the relevant item
2668 matches the filter string, wildcard string or regular expression.
2669
2670 \note By default, the Qt::DisplayRole is used to determine if the row
2671 should be accepted or not. This can be changed by setting the
2672 \l{QSortFilterProxyModel::filterRole}{filterRole} property.
2673
2674 \sa filterAcceptsColumn(), setFilterFixedString(), setFilterRegExp(), setFilterWildcard()
2675*/
2676bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
2677{
2678 Q_D(const QSortFilterProxyModel);
2679 if (d->filter_regexp.isEmpty())
2680 return true;
2681 if (d->filter_column == -1) {
2682 int column_count = d->model->columnCount(source_parent);
2683 for (int column = 0; column < column_count; ++column) {
2684 QModelIndex source_index = d->model->index(source_row, column, source_parent);
2685 QString key = d->model->data(source_index, d->filter_role).toString();
2686 if (key.contains(d->filter_regexp))
2687 return true;
2688 }
2689 return false;
2690 }
2691 QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent);
2692 if (!source_index.isValid()) // the column may not exist
2693 return true;
2694 QString key = d->model->data(source_index, d->filter_role).toString();
2695 return key.contains(d->filter_regexp);
2696}
2697
2698/*!
2699 Returns true if the item in the column indicated by the given \a source_column
2700 and \a source_parent should be included in the model; otherwise returns false.
2701
2702 The default implementation returns true if the value held by the relevant item
2703 matches the filter string, wildcard string or regular expression.
2704
2705 \note By default, the Qt::DisplayRole is used to determine if the row
2706 should be accepted or not. This can be changed by setting the \l
2707 filterRole property.
2708
2709 \sa filterAcceptsRow(), setFilterFixedString(), setFilterRegExp(), setFilterWildcard()
2710*/
2711bool QSortFilterProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
2712{
2713 Q_UNUSED(source_column);
2714 Q_UNUSED(source_parent);
2715 return true;
2716}
2717
2718/*!
2719 Returns the source model index corresponding to the given \a
2720 proxyIndex from the sorting filter model.
2721
2722 \sa mapFromSource()
2723*/
2724QModelIndex QSortFilterProxyModel::mapToSource(const QModelIndex &proxyIndex) const
2725{
2726 Q_D(const QSortFilterProxyModel);
2727 return d->proxy_to_source(proxyIndex);
2728}
2729
2730/*!
2731 Returns the model index in the QSortFilterProxyModel given the \a
2732 sourceIndex from the source model.
2733
2734 \sa mapToSource()
2735*/
2736QModelIndex QSortFilterProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
2737{
2738 Q_D(const QSortFilterProxyModel);
2739 return d->source_to_proxy(sourceIndex);
2740}
2741
2742/*!
2743 \reimp
2744*/
2745QItemSelection QSortFilterProxyModel::mapSelectionToSource(const QItemSelection &proxySelection) const
2746{
2747 return QAbstractProxyModel::mapSelectionToSource(proxySelection);
2748}
2749
2750/*!
2751 \reimp
2752*/
2753QItemSelection QSortFilterProxyModel::mapSelectionFromSource(const QItemSelection &sourceSelection) const
2754{
2755 return QAbstractProxyModel::mapSelectionFromSource(sourceSelection);
2756}
2757
2758/*!
2759 \fn QObject *QSortFilterProxyModel::parent() const
2760 \internal
2761*/
2762
2763QT_END_NAMESPACE
2764
2765#include "moc_qsortfilterproxymodel.cpp"
2766
2767#endif // QT_NO_SORTFILTERPROXYMODEL
02768
=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests'
=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests/auto'
=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests/auto/corelib'
=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests/auto/corelib/itemmodels'
=== added directory '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests/auto/corelib/itemmodels/qsortfilterproxymodel'
=== added file '.pc/0001-Fix-rowsInserted-not-being-emmited-in-some-cases-in-.patch/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp'
--- .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
+++ .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
@@ -0,0 +1,3640 @@
1/****************************************************************************
2**
3** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <QtTest/QtTest>
43#include "dynamictreemodel.h"
44#include "modeltest.h"
45
46#include <QtCore/QCoreApplication>
47#include <QtGui/QStandardItem>
48#include <QtWidgets/QTreeView>
49#include <QtWidgets/QTableView>
50
51#include <qdebug.h>
52
53typedef QList<int> IntList;
54typedef QPair<int, int> IntPair;
55typedef QList<IntPair> IntPairList;
56
57class tst_QSortFilterProxyModel : public QObject
58{
59 Q_OBJECT
60public:
61 tst_QSortFilterProxyModel();
62
63public slots:
64 void initTestCase();
65 void cleanupTestCase();
66 void cleanup();
67
68private slots:
69 void getSetCheck();
70 void sort_data();
71 void sort();
72 void sortHierarchy_data();
73 void sortHierarchy();
74
75 void insertRows_data();
76 void insertRows();
77 void prependRow();
78 void removeRows_data();
79 void removeRows();
80 void removeColumns_data();
81 void removeColumns();
82 void insertAfterSelect();
83 void removeAfterSelect();
84 void filter_data();
85 void filter();
86 void filterHierarchy_data();
87 void filterHierarchy();
88 void filterColumns_data();
89 void filterColumns();
90
91 void filterTable();
92 void filterCurrent();
93
94 void changeSourceLayout();
95 void removeSourceRows_data();
96 void removeSourceRows();
97 void insertSourceRows_data();
98 void insertSourceRows();
99 void changeFilter_data();
100 void changeFilter();
101 void changeSourceData_data();
102 void changeSourceData();
103 void sortFilterRole();
104 void selectionFilteredOut();
105 void match_data();
106 void match();
107 void insertIntoChildrenlessItem();
108 void invalidateMappedChildren();
109 void insertRowIntoFilteredParent();
110 void filterOutParentAndFilterInChild();
111
112 void sourceInsertRows();
113 void sourceModelDeletion();
114
115 void sortColumnTracking1();
116 void sortColumnTracking2();
117
118 void sortStable();
119
120 void hiddenColumns();
121 void insertRowsSort();
122 void staticSorting();
123 void dynamicSorting();
124 void fetchMore();
125 void hiddenChildren();
126 void mapFromToSource();
127 void removeRowsRecursive();
128 void doubleProxySelectionSetSourceModel();
129 void appearsAndSort();
130 void unnecessaryDynamicSorting();
131 void unnecessaryMapCreation();
132 void resetInvalidate_data();
133 void resetInvalidate();
134
135 void testMultipleProxiesWithSelection();
136 void mapSelectionFromSource();
137 void filteredColumns();
138 void headerDataChanged();
139
140 void testParentLayoutChanged();
141 void moveSourceRows();
142
143 void hierarchyFilterInvalidation();
144 void simpleFilterInvalidation();
145
146protected:
147 void buildHierarchy(const QStringList &data, QAbstractItemModel *model);
148 void checkHierarchy(const QStringList &data, const QAbstractItemModel *model);
149
150private:
151 QStandardItemModel *m_model;
152 QSortFilterProxyModel *m_proxy;
153};
154
155// Testing get/set functions
156void tst_QSortFilterProxyModel::getSetCheck()
157{
158 QSortFilterProxyModel obj1;
159 QCOMPARE(obj1.sourceModel(), (QAbstractItemModel *)0);
160 // int QSortFilterProxyModel::filterKeyColumn()
161 // void QSortFilterProxyModel::setFilterKeyColumn(int)
162 obj1.setFilterKeyColumn(0);
163 QCOMPARE(0, obj1.filterKeyColumn());
164 obj1.setFilterKeyColumn(INT_MIN);
165 QCOMPARE(INT_MIN, obj1.filterKeyColumn());
166 obj1.setFilterKeyColumn(INT_MAX);
167 QCOMPARE(INT_MAX, obj1.filterKeyColumn());
168}
169
170tst_QSortFilterProxyModel::tst_QSortFilterProxyModel()
171 : m_model(0), m_proxy(0)
172{
173}
174
175void tst_QSortFilterProxyModel::initTestCase()
176{
177 qRegisterMetaType<IntList>("IntList");
178 qRegisterMetaType<IntPair>("IntPair");
179 qRegisterMetaType<IntPairList>("IntPairList");
180 m_model = new QStandardItemModel(0, 1);
181 m_proxy = new QSortFilterProxyModel();
182 m_proxy->setSourceModel(m_model);
183}
184
185void tst_QSortFilterProxyModel::cleanupTestCase()
186{
187 delete m_proxy;
188 delete m_model;
189}
190
191void tst_QSortFilterProxyModel::cleanup()
192{
193 m_proxy->setFilterRegExp(QRegExp());
194 m_proxy->sort(-1, Qt::AscendingOrder);
195 m_model->clear();
196 m_model->insertColumns(0, 1);
197}
198
199/*
200 tests
201*/
202
203void tst_QSortFilterProxyModel::sort_data()
204{
205 QTest::addColumn<int>("sortOrder");
206 QTest::addColumn<int>("sortCaseSensitivity");
207 QTest::addColumn<QStringList>("initial");
208 QTest::addColumn<QStringList>("expected");
209
210 QTest::newRow("flat descending") << static_cast<int>(Qt::DescendingOrder)
211 << int(Qt::CaseSensitive)
212 << (QStringList()
213 << "delta"
214 << "yankee"
215 << "bravo"
216 << "lima"
217 << "charlie"
218 << "juliet"
219 << "tango"
220 << "hotel"
221 << "uniform"
222 << "alpha"
223 << "echo"
224 << "golf"
225 << "quebec"
226 << "foxtrot"
227 << "india"
228 << "romeo"
229 << "november"
230 << "oskar"
231 << "zulu"
232 << "kilo"
233 << "whiskey"
234 << "mike"
235 << "papa"
236 << "sierra"
237 << "xray"
238 << "viktor")
239 << (QStringList()
240 << "zulu"
241 << "yankee"
242 << "xray"
243 << "whiskey"
244 << "viktor"
245 << "uniform"
246 << "tango"
247 << "sierra"
248 << "romeo"
249 << "quebec"
250 << "papa"
251 << "oskar"
252 << "november"
253 << "mike"
254 << "lima"
255 << "kilo"
256 << "juliet"
257 << "india"
258 << "hotel"
259 << "golf"
260 << "foxtrot"
261 << "echo"
262 << "delta"
263 << "charlie"
264 << "bravo"
265 << "alpha");
266 QTest::newRow("flat ascending") << static_cast<int>(Qt::AscendingOrder)
267 << int(Qt::CaseSensitive)
268 << (QStringList()
269 << "delta"
270 << "yankee"
271 << "bravo"
272 << "lima"
273 << "charlie"
274 << "juliet"
275 << "tango"
276 << "hotel"
277 << "uniform"
278 << "alpha"
279 << "echo"
280 << "golf"
281 << "quebec"
282 << "foxtrot"
283 << "india"
284 << "romeo"
285 << "november"
286 << "oskar"
287 << "zulu"
288 << "kilo"
289 << "whiskey"
290 << "mike"
291 << "papa"
292 << "sierra"
293 << "xray"
294 << "viktor")
295 << (QStringList()
296 << "alpha"
297 << "bravo"
298 << "charlie"
299 << "delta"
300 << "echo"
301 << "foxtrot"
302 << "golf"
303 << "hotel"
304 << "india"
305 << "juliet"
306 << "kilo"
307 << "lima"
308 << "mike"
309 << "november"
310 << "oskar"
311 << "papa"
312 << "quebec"
313 << "romeo"
314 << "sierra"
315 << "tango"
316 << "uniform"
317 << "viktor"
318 << "whiskey"
319 << "xray"
320 << "yankee"
321 << "zulu");
322 QTest::newRow("case insensitive") << static_cast<int>(Qt::AscendingOrder)
323 << int(Qt::CaseInsensitive)
324 << (QStringList()
325 << "alpha" << "BETA" << "Gamma" << "delta")
326 << (QStringList()
327 << "alpha" << "BETA" << "delta" << "Gamma");
328 QTest::newRow("case sensitive") << static_cast<int>(Qt::AscendingOrder)
329 << int(Qt::CaseSensitive)
330 << (QStringList()
331 << "alpha" << "BETA" << "Gamma" << "delta")
332 << (QStringList()
333 << "BETA" << "Gamma" << "alpha" << "delta");
334
335 QStringList list;
336 for (int i = 10000; i < 20000; ++i)
337 list.append(QString("Number: %1").arg(i));
338 QTest::newRow("large set ascending") << static_cast<int>(Qt::AscendingOrder) << int(Qt::CaseSensitive) << list << list;
339}
340
341void tst_QSortFilterProxyModel::sort()
342{
343 QFETCH(int, sortOrder);
344 QFETCH(int, sortCaseSensitivity);
345 QFETCH(QStringList, initial);
346 QFETCH(QStringList, expected);
347
348 // prepare model
349 QStandardItem *root = m_model->invisibleRootItem ();
350 QList<QStandardItem *> items;
351 for (int i = 0; i < initial.count(); ++i) {
352 items.append(new QStandardItem(initial.at(i)));
353 }
354 root->insertRows(0, items);
355 QCOMPARE(m_model->rowCount(QModelIndex()), initial.count());
356 QCOMPARE(m_model->columnCount(QModelIndex()), 1);
357
358 // make sure the proxy is unsorted
359 QCOMPARE(m_proxy->columnCount(QModelIndex()), 1);
360 QCOMPARE(m_proxy->rowCount(QModelIndex()), initial.count());
361 for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
362 QModelIndex index = m_proxy->index(row, 0, QModelIndex());
363 QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(row));
364 }
365
366 // sort
367 m_proxy->sort(0, static_cast<Qt::SortOrder>(sortOrder));
368 m_proxy->setSortCaseSensitivity(static_cast<Qt::CaseSensitivity>(sortCaseSensitivity));
369
370 // make sure the model is unchanged
371 for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
372 QModelIndex index = m_model->index(row, 0, QModelIndex());
373 QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(row));
374 }
375 // make sure the proxy is sorted
376 for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
377 QModelIndex index = m_proxy->index(row, 0, QModelIndex());
378 QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), expected.at(row));
379 }
380
381 // restore the unsorted order
382 m_proxy->sort(-1);
383
384 // make sure the proxy is unsorted again
385 for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
386 QModelIndex index = m_proxy->index(row, 0, QModelIndex());
387 QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(row));
388 }
389}
390
391void tst_QSortFilterProxyModel::sortHierarchy_data()
392{
393 QTest::addColumn<int>("sortOrder");
394 QTest::addColumn<QStringList>("initial");
395 QTest::addColumn<QStringList>("expected");
396
397 QTest::newRow("flat ascending")
398 << static_cast<int>(Qt::AscendingOrder)
399 << (QStringList()
400 << "c" << "f" << "d" << "e" << "a" << "b")
401 << (QStringList()
402 << "a" << "b" << "c" << "d" << "e" << "f");
403
404 QTest::newRow("simple hierarchy")
405 << static_cast<int>(Qt::AscendingOrder)
406 << (QStringList() << "a" << "<" << "b" << "<" << "c" << ">" << ">")
407 << (QStringList() << "a" << "<" << "b" << "<" << "c" << ">" << ">");
408
409 QTest::newRow("hierarchical ascending")
410 << static_cast<int>(Qt::AscendingOrder)
411 << (QStringList()
412 << "c"
413 << "<"
414 << "h"
415 << "<"
416 << "2"
417 << "0"
418 << "1"
419 << ">"
420 << "g"
421 << "i"
422 << ">"
423 << "b"
424 << "<"
425 << "l"
426 << "k"
427 << "<"
428 << "8"
429 << "7"
430 << "9"
431 << ">"
432 << "m"
433 << ">"
434 << "a"
435 << "<"
436 << "z"
437 << "y"
438 << "x"
439 << ">")
440 << (QStringList()
441 << "a"
442 << "<"
443 << "x"
444 << "y"
445 << "z"
446 << ">"
447 << "b"
448 << "<"
449 << "k"
450 << "<"
451 << "7"
452 << "8"
453 << "9"
454 << ">"
455 << "l"
456 << "m"
457 << ">"
458 << "c"
459 << "<"
460 << "g"
461 << "h"
462 << "<"
463 << "0"
464 << "1"
465 << "2"
466 << ">"
467 << "i"
468 << ">");
469}
470
471void tst_QSortFilterProxyModel::sortHierarchy()
472{
473 QFETCH(int, sortOrder);
474 QFETCH(QStringList, initial);
475 QFETCH(QStringList, expected);
476
477 buildHierarchy(initial, m_model);
478 checkHierarchy(initial, m_model);
479 checkHierarchy(initial, m_proxy);
480 m_proxy->sort(0, static_cast<Qt::SortOrder>(sortOrder));
481 checkHierarchy(initial, m_model);
482 checkHierarchy(expected, m_proxy);
483}
484
485void tst_QSortFilterProxyModel::insertRows_data()
486{
487 QTest::addColumn<QStringList>("initial");
488 QTest::addColumn<QStringList>("expected");
489 QTest::addColumn<QStringList>("insert");
490 QTest::addColumn<int>("position");
491
492 QTest::newRow("insert one row in the middle")
493 << (QStringList()
494 << "One"
495 << "Two"
496 << "Four"
497 << "Five")
498 << (QStringList()
499 << "One"
500 << "Two"
501 << "Three"
502 << "Four"
503 << "Five")
504 << (QStringList()
505 << "Three")
506 << 2;
507
508 QTest::newRow("insert one row in the beginning")
509 << (QStringList()
510 << "Two"
511 << "Three"
512 << "Four"
513 << "Five")
514 << (QStringList()
515 << "One"
516 << "Two"
517 << "Three"
518 << "Four"
519 << "Five")
520 << (QStringList()
521 << "One")
522 << 0;
523
524 QTest::newRow("insert one row in the end")
525 << (QStringList()
526 << "One"
527 << "Two"
528 << "Three"
529 << "Four")
530 << (QStringList()
531 << "One"
532 << "Two"
533 << "Three"
534 << "Four"
535 << "Five")
536 << (QStringList()
537 <<"Five")
538 << 4;
539}
540
541void tst_QSortFilterProxyModel::insertRows()
542{
543 QFETCH(QStringList, initial);
544 QFETCH(QStringList, expected);
545 QFETCH(QStringList, insert);
546 QFETCH(int, position);
547 // prepare model
548 m_model->insertRows(0, initial.count(), QModelIndex());
549 //m_model->insertColumns(0, 1, QModelIndex());
550 QCOMPARE(m_model->columnCount(QModelIndex()), 1);
551 QCOMPARE(m_model->rowCount(QModelIndex()), initial.count());
552 for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
553 QModelIndex index = m_model->index(row, 0, QModelIndex());
554 m_model->setData(index, initial.at(row), Qt::DisplayRole);
555 }
556 // make sure the model correct before insert
557 for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
558 QModelIndex index = m_model->index(row, 0, QModelIndex());
559 QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(row));
560 }
561 // make sure the proxy is correct before insert
562 for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
563 QModelIndex index = m_proxy->index(row, 0, QModelIndex());
564 QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(row));
565 }
566
567 // insert the row
568 m_proxy->insertRows(position, insert.count(), QModelIndex());
569 QCOMPARE(m_model->rowCount(QModelIndex()), expected.count());
570 QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.count());
571
572 // set the data for the inserted row
573 for (int i = 0; i < insert.count(); ++i) {
574 QModelIndex index = m_proxy->index(position + i, 0, QModelIndex());
575 m_proxy->setData(index, insert.at(i), Qt::DisplayRole);
576 }
577
578 // make sure the model correct after insert
579 for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
580 QModelIndex index = m_model->index(row, 0, QModelIndex());
581 QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), expected.at(row));
582 }
583
584 // make sure the proxy is correct after insert
585 for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
586 QModelIndex index = m_proxy->index(row, 0, QModelIndex());
587 QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), expected.at(row));
588 }
589}
590
591void tst_QSortFilterProxyModel::prependRow()
592{
593 //this tests that data is correctly handled by the sort filter when prepending a row
594 QStandardItemModel model;
595 QSortFilterProxyModel proxy;
596 proxy.setSourceModel(&model);
597
598 QStandardItem item("root");
599 model.appendRow(&item);
600
601 QStandardItem sub("sub");
602 item.appendRow(&sub);
603
604 sub.appendRow(new QStandardItem("test1"));
605 sub.appendRow(new QStandardItem("test2"));
606
607 QStandardItem sub2("sub2");
608 sub2.appendRow(new QStandardItem("sub3"));
609 item.insertRow(0, &sub2);
610
611 QModelIndex index_sub2 = proxy.mapFromSource(model.indexFromItem(&sub2));
612
613 QCOMPARE(sub2.rowCount(), proxy.rowCount(index_sub2));
614 QCOMPARE(proxy.rowCount(QModelIndex()), 1); //only the "root" item is there
615}
616
617void tst_QSortFilterProxyModel::removeRows_data()
618{
619 QTest::addColumn<QStringList>("initial");
620 QTest::addColumn<int>("sortOrder");
621 QTest::addColumn<QString>("filter");
622 QTest::addColumn<int>("position");
623 QTest::addColumn<int>("count");
624 QTest::addColumn<bool>("success");
625 QTest::addColumn<QStringList>("expectedProxy");
626 QTest::addColumn<QStringList>("expectedSource");
627
628 QTest::newRow("remove one row in the middle [no sorting/filter]")
629 << (QStringList()
630 << "One"
631 << "Two"
632 << "Three"
633 << "Four"
634 << "Five")
635 << -1 // no sorting
636 << QString() // no filter
637 << 2 // position
638 << 1 // count
639 << true // success
640 << (QStringList() // expectedProxy
641 << "One"
642 << "Two"
643 << "Four"
644 << "Five")
645 << (QStringList() // expectedSource
646 << "One"
647 << "Two"
648 << "Four"
649 << "Five");
650
651 QTest::newRow("remove one row in the beginning [no sorting/filter]")
652 << (QStringList()
653 << "One"
654 << "Two"
655 << "Three"
656 << "Four"
657 << "Five")
658 << -1 // no sorting
659 << QString() // no filter
660 << 0 // position
661 << 1 // count
662 << true // success
663 << (QStringList() // expectedProxy
664 << "Two"
665 << "Three"
666 << "Four"
667 << "Five")
668 << (QStringList() // expectedSource
669 << "Two"
670 << "Three"
671 << "Four"
672 << "Five");
673
674 QTest::newRow("remove one row in the end [no sorting/filter]")
675 << (QStringList()
676 << "One"
677 << "Two"
678 << "Three"
679 << "Four"
680 << "Five")
681 << -1 // no sorting
682 << QString() // no filter
683 << 4 // position
684 << 1 // count
685 << true // success
686 << (QStringList() // expectedProxy
687 << "One"
688 << "Two"
689 << "Three"
690 << "Four")
691 << (QStringList() // expectedSource
692 << "One"
693 << "Two"
694 << "Three"
695 << "Four");
696
697 QTest::newRow("remove all [no sorting/filter]")
698 << (QStringList()
699 << "One"
700 << "Two"
701 << "Three"
702 << "Four"
703 << "Five")
704 << -1 // no sorting
705 << QString() // no filter
706 << 0 // position
707 << 5 // count
708 << true // success
709 << QStringList() // expectedProxy
710 << QStringList(); // expectedSource
711
712 QTest::newRow("remove one row past the end [no sorting/filter]")
713 << (QStringList()
714 << "One"
715 << "Two"
716 << "Three"
717 << "Four"
718 << "Five")
719 << -1 // no sorting
720 << QString() // no filter
721 << 5 // position
722 << 1 // count
723 << false // success
724 << (QStringList() // expectedProxy
725 << "One"
726 << "Two"
727 << "Three"
728 << "Four"
729 << "Five")
730 << (QStringList() // expectedSource
731 << "One"
732 << "Two"
733 << "Three"
734 << "Four"
735 << "Five");
736
737 QTest::newRow("remove row -1 [no sorting/filter]")
738 << (QStringList()
739 << "One"
740 << "Two"
741 << "Three"
742 << "Four"
743 << "Five")
744 << -1 // no sorting
745 << QString() // no filter
746 << -1 // position
747 << 1 // count
748 << false // success
749 << (QStringList() // expectedProxy
750 << "One"
751 << "Two"
752 << "Three"
753 << "Four"
754 << "Five")
755 << (QStringList() // expectedSource
756 << "One"
757 << "Two"
758 << "Three"
759 << "Four"
760 << "Five");
761
762 QTest::newRow("remove three rows in the middle [no sorting/filter]")
763 << (QStringList()
764 << "One"
765 << "Two"
766 << "Three"
767 << "Four"
768 << "Five")
769 << -1 // no sorting
770 << QString() // no filter
771 << 1 // position
772 << 3 // count
773 << true // success
774 << (QStringList() // expectedProxy
775 << "One"
776 << "Five")
777 << (QStringList() // expectedSource
778 << "One"
779 << "Five");
780
781 QTest::newRow("remove one row in the middle [ascending sorting, no filter]")
782 << (QStringList()
783 << "1"
784 << "5"
785 << "2"
786 << "4"
787 << "3")
788 << static_cast<int>(Qt::AscendingOrder)
789 << QString() // no filter
790 << 2 // position
791 << 1 // count
792 << true // success
793 << (QStringList() // expectedProxy
794 << "1"
795 << "2"
796 << "4"
797 << "5")
798 << (QStringList() // expectedSource
799 << "1"
800 << "5"
801 << "2"
802 << "4");
803
804 QTest::newRow("remove two rows in the middle [ascending sorting, no filter]")
805 << (QStringList()
806 << "1"
807 << "5"
808 << "2"
809 << "4"
810 << "3")
811 << static_cast<int>(Qt::AscendingOrder)
812 << QString() // no filter
813 << 2 // position
814 << 2 // count
815 << true // success
816 << (QStringList() // expectedProxy
817 << "1"
818 << "2"
819 << "5")
820 << (QStringList() // expectedSource
821 << "1"
822 << "5"
823 << "2");
824
825 QTest::newRow("remove two rows in the middle [descending sorting, no filter]")
826 << (QStringList()
827 << "1"
828 << "5"
829 << "2"
830 << "4"
831 << "3")
832 << static_cast<int>(Qt::DescendingOrder)
833 << QString() // no filter
834 << 2 // position
835 << 2 // count
836 << true // success
837 << (QStringList() // expectedProxy
838 << "5"
839 << "4"
840 << "1")
841 << (QStringList() // expectedSource
842 << "1"
843 << "5"
844 << "4");
845
846 QTest::newRow("remove one row in the middle [no sorting, filter=5|2|3]")
847 << (QStringList()
848 << "1"
849 << "5"
850 << "2"
851 << "4"
852 << "3")
853 << -1 // no sorting
854 << QString("5|2|3")
855 << 1 // position
856 << 1 // count
857 << true // success
858 << (QStringList() // expectedProxy
859 << "5"
860 << "3")
861 << (QStringList() // expectedSource
862 << "1"
863 << "5"
864 << "4"
865 << "3");
866
867 QTest::newRow("remove all [ascending sorting, no filter]")
868 << (QStringList()
869 << "1"
870 << "5"
871 << "2"
872 << "4"
873 << "3")
874 << static_cast<int>(Qt::AscendingOrder)
875 << QString() // no filter
876 << 0 // position
877 << 5 // count
878 << true // success
879 << QStringList() // expectedProxy
880 << QStringList(); // expectedSource
881}
882
883void tst_QSortFilterProxyModel::removeRows()
884{
885 QFETCH(QStringList, initial);
886 QFETCH(int, sortOrder);
887 QFETCH(QString, filter);
888 QFETCH(int, position);
889 QFETCH(int, count);
890 QFETCH(bool, success);
891 QFETCH(QStringList, expectedProxy);
892 QFETCH(QStringList, expectedSource);
893
894 QStandardItemModel model;
895 QSortFilterProxyModel proxy;
896 proxy.setSourceModel(&model);
897
898 // prepare model
899 foreach (QString s, initial)
900 model.appendRow(new QStandardItem(s));
901
902 if (sortOrder != -1)
903 proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
904 if (!filter.isEmpty())
905 proxy.setFilterRegExp(QRegExp(filter));
906
907 // remove the rows
908 QCOMPARE(proxy.removeRows(position, count, QModelIndex()), success);
909 QCOMPARE(model.rowCount(QModelIndex()), expectedSource.count());
910 QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxy.count());
911
912 // make sure the model is correct after remove
913 for (int row = 0; row < model.rowCount(QModelIndex()); ++row)
914 QCOMPARE(model.item(row)->text(), expectedSource.at(row));
915
916 // make sure the proxy is correct after remove
917 for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) {
918 QModelIndex index = proxy.index(row, 0, QModelIndex());
919 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), expectedProxy.at(row));
920 }
921}
922
923class MyFilteredColumnProxyModel : public QSortFilterProxyModel
924{
925 Q_OBJECT
926public:
927 MyFilteredColumnProxyModel(QObject *parent = 0)
928 : QSortFilterProxyModel(parent) { }
929protected:
930 bool filterAcceptsColumn(int sourceColumn, const QModelIndex &) const
931 {
932 QString key = sourceModel()->headerData(sourceColumn, Qt::Horizontal).toString();
933 return key.contains(filterRegExp());
934 }
935};
936
937void tst_QSortFilterProxyModel::removeColumns_data()
938{
939 QTest::addColumn<QStringList>("initial");
940 QTest::addColumn<QString>("filter");
941 QTest::addColumn<int>("position");
942 QTest::addColumn<int>("count");
943 QTest::addColumn<bool>("success");
944 QTest::addColumn<QStringList>("expectedProxy");
945 QTest::addColumn<QStringList>("expectedSource");
946
947 QTest::newRow("remove one column in the middle [no filter]")
948 << (QStringList()
949 << "1"
950 << "2"
951 << "3"
952 << "4"
953 << "5")
954 << QString() // no filter
955 << 2 // position
956 << 1 // count
957 << true // success
958 << (QStringList() // expectedProxy
959 << "1"
960 << "2"
961 << "4"
962 << "5")
963 << (QStringList() // expectedSource
964 << "1"
965 << "2"
966 << "4"
967 << "5");
968
969 QTest::newRow("remove one column in the end [no filter]")
970 << (QStringList()
971 << "1"
972 << "2"
973 << "3"
974 << "4"
975 << "5")
976 << QString() // no filter
977 << 4 // position
978 << 1 // count
979 << true // success
980 << (QStringList() // expectedProxy
981 << "1"
982 << "2"
983 << "3"
984 << "4")
985 << (QStringList() // expectedSource
986 << "1"
987 << "2"
988 << "3"
989 << "4");
990
991 QTest::newRow("remove one column past the end [no filter]")
992 << (QStringList()
993 << "1"
994 << "2"
995 << "3"
996 << "4"
997 << "5")
998 << QString() // no filter
999 << 5 // position
1000 << 1 // count
1001 << false // success
1002 << (QStringList() // expectedProxy
1003 << "1"
1004 << "2"
1005 << "3"
1006 << "4"
1007 << "5")
1008 << (QStringList() // expectedSource
1009 << "1"
1010 << "2"
1011 << "3"
1012 << "4"
1013 << "5");
1014
1015 QTest::newRow("remove column -1 [no filter]")
1016 << (QStringList()
1017 << "1"
1018 << "2"
1019 << "3"
1020 << "4"
1021 << "5")
1022 << QString() // no filter
1023 << -1 // position
1024 << 1 // count
1025 << false // success
1026 << (QStringList() // expectedProxy
1027 << "1"
1028 << "2"
1029 << "3"
1030 << "4"
1031 << "5")
1032 << (QStringList() // expectedSource
1033 << "1"
1034 << "2"
1035 << "3"
1036 << "4"
1037 << "5");
1038
1039 QTest::newRow("remove all columns [no filter]")
1040 << (QStringList()
1041 << "1"
1042 << "2"
1043 << "3"
1044 << "4"
1045 << "5")
1046 << QString() // no filter
1047 << 0 // position
1048 << 5 // count
1049 << true // success
1050 << QStringList() // expectedProxy
1051 << QStringList(); // expectedSource
1052
1053 QTest::newRow("remove one column in the middle [filter=1|3|5]")
1054 << (QStringList()
1055 << "1"
1056 << "2"
1057 << "3"
1058 << "4"
1059 << "5")
1060 << QString("1|3|5")
1061 << 1 // position
1062 << 1 // count
1063 << true // success
1064 << (QStringList() // expectedProxy
1065 << "1"
1066 << "5")
1067 << (QStringList() // expectedSource
1068 << "1"
1069 << "2"
1070 << "4"
1071 << "5");
1072
1073 QTest::newRow("remove one column in the end [filter=1|3|5]")
1074 << (QStringList()
1075 << "1"
1076 << "2"
1077 << "3"
1078 << "4"
1079 << "5")
1080 << QString("1|3|5")
1081 << 2 // position
1082 << 1 // count
1083 << true // success
1084 << (QStringList() // expectedProxy
1085 << "1"
1086 << "3")
1087 << (QStringList() // expectedSource
1088 << "1"
1089 << "2"
1090 << "3"
1091 << "4");
1092
1093 QTest::newRow("remove one column past the end [filter=1|3|5]")
1094 << (QStringList()
1095 << "1"
1096 << "2"
1097 << "3"
1098 << "4"
1099 << "5")
1100 << QString("1|3|5")
1101 << 3 // position
1102 << 1 // count
1103 << false // success
1104 << (QStringList() // expectedProxy
1105 << "1"
1106 << "3"
1107 << "5")
1108 << (QStringList() // expectedSource
1109 << "1"
1110 << "2"
1111 << "3"
1112 << "4"
1113 << "5");
1114
1115 QTest::newRow("remove all columns [filter=1|3|5]")
1116 << (QStringList()
1117 << "1"
1118 << "2"
1119 << "3"
1120 << "4"
1121 << "5")
1122 << QString("1|3|5")
1123 << 0 // position
1124 << 3 // count
1125 << true // success
1126 << QStringList() // expectedProxy
1127 << (QStringList() // expectedSource
1128 << "2"
1129 << "4");
1130}
1131
1132void tst_QSortFilterProxyModel::removeColumns()
1133{
1134 QFETCH(QStringList, initial);
1135 QFETCH(QString, filter);
1136 QFETCH(int, position);
1137 QFETCH(int, count);
1138 QFETCH(bool, success);
1139 QFETCH(QStringList, expectedProxy);
1140 QFETCH(QStringList, expectedSource);
1141
1142 QStandardItemModel model;
1143 MyFilteredColumnProxyModel proxy;
1144 proxy.setSourceModel(&model);
1145 if (!filter.isEmpty())
1146 proxy.setFilterRegExp(QRegExp(filter));
1147
1148 // prepare model
1149 model.setHorizontalHeaderLabels(initial);
1150
1151 // remove the columns
1152 QCOMPARE(proxy.removeColumns(position, count, QModelIndex()), success);
1153 QCOMPARE(model.columnCount(QModelIndex()), expectedSource.count());
1154 QCOMPARE(proxy.columnCount(QModelIndex()), expectedProxy.count());
1155
1156 // make sure the model is correct after remove
1157 for (int col = 0; col < model.columnCount(QModelIndex()); ++col)
1158 QCOMPARE(model.horizontalHeaderItem(col)->text(), expectedSource.at(col));
1159
1160 // make sure the proxy is correct after remove
1161 for (int col = 0; col < proxy.columnCount(QModelIndex()); ++col) {
1162 QCOMPARE(proxy.headerData(col, Qt::Horizontal, Qt::DisplayRole).toString(),
1163 expectedProxy.at(col));
1164 }
1165}
1166
1167void tst_QSortFilterProxyModel::filterColumns_data()
1168{
1169 QTest::addColumn<QString>("pattern");
1170 QTest::addColumn<QStringList>("initial");
1171 QTest::addColumn<bool>("data");
1172
1173 QTest::newRow("all") << "a"
1174 << (QStringList()
1175 << "delta"
1176 << "yankee"
1177 << "bravo"
1178 << "lima")
1179 << true;
1180
1181 QTest::newRow("some") << "lie"
1182 << (QStringList()
1183 << "charlie"
1184 << "juliet"
1185 << "tango"
1186 << "hotel")
1187 << true;
1188
1189 QTest::newRow("nothing") << "zoo"
1190 << (QStringList()
1191 << "foxtrot"
1192 << "uniform"
1193 << "alpha"
1194 << "golf")
1195 << false;
1196}
1197
1198void tst_QSortFilterProxyModel::filterColumns()
1199{
1200 QFETCH(QString, pattern);
1201 QFETCH(QStringList, initial);
1202 QFETCH(bool, data);
1203 // prepare model
1204 m_model->setColumnCount(initial.count());
1205 m_model->setRowCount(1);
1206 QCOMPARE(m_model->columnCount(QModelIndex()), initial.count());
1207 QCOMPARE(m_model->rowCount(QModelIndex()), 1);
1208 // set data
1209 QCOMPARE(m_model->rowCount(QModelIndex()), 1);
1210 for (int col = 0; col < m_model->columnCount(QModelIndex()); ++col) {
1211 QModelIndex index = m_model->index(0, col, QModelIndex());
1212 m_model->setData(index, initial.at(col), Qt::DisplayRole);
1213 }
1214 m_proxy->setFilterRegExp(pattern);
1215 m_proxy->setFilterKeyColumn(-1);
1216 // make sure the model is unchanged
1217 for (int col = 0; col < m_model->columnCount(QModelIndex()); ++col) {
1218 QModelIndex index = m_model->index(0, col, QModelIndex());
1219 QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(col));
1220 }
1221 // make sure the proxy is filtered
1222 QModelIndex index = m_proxy->index(0, 0, QModelIndex());
1223 QCOMPARE(index.isValid(), data);
1224}
1225
1226void tst_QSortFilterProxyModel::filter_data()
1227{
1228 QTest::addColumn<QString>("pattern");
1229 QTest::addColumn<QStringList>("initial");
1230 QTest::addColumn<QStringList>("expected");
1231
1232 QTest::newRow("flat") << "e"
1233 << (QStringList()
1234 << "delta"
1235 << "yankee"
1236 << "bravo"
1237 << "lima"
1238 << "charlie"
1239 << "juliet"
1240 << "tango"
1241 << "hotel"
1242 << "uniform"
1243 << "alpha"
1244 << "echo"
1245 << "golf"
1246 << "quebec"
1247 << "foxtrot"
1248 << "india"
1249 << "romeo"
1250 << "november"
1251 << "oskar"
1252 << "zulu"
1253 << "kilo"
1254 << "whiskey"
1255 << "mike"
1256 << "papa"
1257 << "sierra"
1258 << "xray"
1259 << "viktor")
1260 << (QStringList()
1261 << "delta"
1262 << "yankee"
1263 << "charlie"
1264 << "juliet"
1265 << "hotel"
1266 << "echo"
1267 << "quebec"
1268 << "romeo"
1269 << "november"
1270 << "whiskey"
1271 << "mike"
1272 << "sierra");
1273}
1274
1275void tst_QSortFilterProxyModel::filter()
1276{
1277 QFETCH(QString, pattern);
1278 QFETCH(QStringList, initial);
1279 QFETCH(QStringList, expected);
1280 // prepare model
1281 QVERIFY(m_model->insertRows(0, initial.count(), QModelIndex()));
1282 QCOMPARE(m_model->rowCount(QModelIndex()), initial.count());
1283 // set data
1284 QCOMPARE(m_model->columnCount(QModelIndex()), 1);
1285 for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
1286 QModelIndex index = m_model->index(row, 0, QModelIndex());
1287 m_model->setData(index, initial.at(row), Qt::DisplayRole);
1288 }
1289 m_proxy->setFilterRegExp(pattern);
1290 // make sure the proxy is unfiltered
1291 QCOMPARE(m_proxy->columnCount(QModelIndex()), 1);
1292 QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.count());
1293 // make sure the model is unchanged
1294 for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
1295 QModelIndex index = m_model->index(row, 0, QModelIndex());
1296 QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(row));
1297 }
1298 // make sure the proxy is filtered
1299 for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
1300 QModelIndex index = m_proxy->index(row, 0, QModelIndex());
1301 QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), expected.at(row));
1302 }
1303}
1304
1305void tst_QSortFilterProxyModel::filterHierarchy_data()
1306{
1307 QTest::addColumn<QString>("pattern");
1308 QTest::addColumn<QStringList>("initial");
1309 QTest::addColumn<QStringList>("expected");
1310
1311 QTest::newRow("flat") << ".*oo"
1312 << (QStringList()
1313 << "foo" << "boo" << "baz" << "moo" << "laa" << "haa")
1314 << (QStringList()
1315 << "foo" << "boo" << "moo");
1316
1317 QTest::newRow("simple hierarchy") << "b.*z"
1318 << (QStringList() << "baz" << "<" << "boz" << "<" << "moo" << ">" << ">")
1319 << (QStringList() << "baz" << "<" << "boz" << ">");
1320}
1321
1322void tst_QSortFilterProxyModel::filterHierarchy()
1323{
1324 QFETCH(QString, pattern);
1325 QFETCH(QStringList, initial);
1326 QFETCH(QStringList, expected);
1327 buildHierarchy(initial, m_model);
1328 m_proxy->setFilterRegExp(pattern);
1329 checkHierarchy(initial, m_model);
1330 checkHierarchy(expected, m_proxy);
1331}
1332
1333void tst_QSortFilterProxyModel::buildHierarchy(const QStringList &l, QAbstractItemModel *m)
1334{
1335 int ind = 0;
1336 int row = 0;
1337 QStack<int> row_stack;
1338 QModelIndex parent;
1339 QStack<QModelIndex> parent_stack;
1340 for (int i = 0; i < l.count(); ++i) {
1341 QString token = l.at(i);
1342 if (token == "<") { // start table
1343 ++ind;
1344 parent_stack.push(parent);
1345 row_stack.push(row);
1346 parent = m->index(row - 1, 0, parent);
1347 row = 0;
1348 QVERIFY(m->insertColumns(0, 1, parent)); // add column
1349 } else if (token == ">") { // end table
1350 --ind;
1351 parent = parent_stack.pop();
1352 row = row_stack.pop();
1353 } else { // append row
1354 QVERIFY(m->insertRows(row, 1, parent));
1355 QModelIndex index = m->index(row, 0, parent);
1356 QVERIFY(index.isValid());
1357 m->setData(index, token, Qt::DisplayRole);
1358 ++row;
1359 }
1360 }
1361}
1362
1363void tst_QSortFilterProxyModel::checkHierarchy(const QStringList &l, const QAbstractItemModel *m)
1364{
1365 int row = 0;
1366 int indent = 0;
1367 QStack<int> row_stack;
1368 QModelIndex parent;
1369 QStack<QModelIndex> parent_stack;
1370 for (int i = 0; i < l.count(); ++i) {
1371 QString token = l.at(i);
1372 if (token == "<") { // start table
1373 ++indent;
1374 parent_stack.push(parent);
1375 row_stack.push(row);
1376 parent = m->index(row - 1, 0, parent);
1377 QVERIFY(parent.isValid());
1378 row = 0;
1379 } else if (token == ">") { // end table
1380 --indent;
1381 parent = parent_stack.pop();
1382 row = row_stack.pop();
1383 } else { // compare row
1384 QModelIndex index = m->index(row, 0, parent);
1385 QVERIFY(index.isValid());
1386 QString str = m->data(index, Qt::DisplayRole).toString();
1387 QCOMPARE(str, token);
1388 ++row;
1389 }
1390 }
1391}
1392
1393class TestModel: public QAbstractTableModel
1394{
1395public:
1396 int rowCount(const QModelIndex &) const { return 10000; }
1397 int columnCount(const QModelIndex &) const { return 1; }
1398 QVariant data(const QModelIndex &index, int role) const
1399 {
1400 if (role != Qt::DisplayRole)
1401 return QVariant();
1402 return QString::number(index.row());
1403 }
1404};
1405
1406void tst_QSortFilterProxyModel::filterTable()
1407{
1408 TestModel model;
1409 QSortFilterProxyModel filter;
1410 filter.setSourceModel(&model);
1411 filter.setFilterRegExp("9");
1412
1413 for (int i = 0; i < filter.rowCount(); ++i)
1414 QVERIFY(filter.data(filter.index(i, 0)).toString().contains("9"));
1415}
1416
1417void tst_QSortFilterProxyModel::insertAfterSelect()
1418{
1419 QStandardItemModel model(10, 2);
1420 for (int i = 0; i<10;i++)
1421 model.setData(model.index(i, 0), QVariant(i));
1422 QSortFilterProxyModel filter;
1423 filter.setSourceModel(&model);
1424 QTreeView view;
1425 view.setModel(&filter);
1426 view.show();
1427 QModelIndex firstIndex = filter.mapFromSource(model.index(0, 0, QModelIndex()));
1428 QCOMPARE(firstIndex.model(), (const QAbstractItemModel *)view.model());
1429 QVERIFY(firstIndex.isValid());
1430 int itemOffset = view.visualRect(firstIndex).width() / 2;
1431 QPoint p(itemOffset, 1);
1432 QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);
1433 QVERIFY(view.selectionModel()->selectedIndexes().size() > 0);
1434 model.insertRows(5, 1, QModelIndex());
1435 QVERIFY(view.selectionModel()->selectedIndexes().size() > 0); // Should still have a selection
1436}
1437
1438void tst_QSortFilterProxyModel::removeAfterSelect()
1439{
1440 QStandardItemModel model(10, 2);
1441 for (int i = 0; i<10;i++)
1442 model.setData(model.index(i, 0), QVariant(i));
1443 QSortFilterProxyModel filter;
1444 filter.setSourceModel(&model);
1445 QTreeView view;
1446 view.setModel(&filter);
1447 view.show();
1448 QModelIndex firstIndex = filter.mapFromSource(model.index(0, 0, QModelIndex()));
1449 QCOMPARE(firstIndex.model(), (const QAbstractItemModel *)view.model());
1450 QVERIFY(firstIndex.isValid());
1451 int itemOffset = view.visualRect(firstIndex).width() / 2;
1452 QPoint p(itemOffset, 1);
1453 QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);
1454 QVERIFY(view.selectionModel()->selectedIndexes().size() > 0);
1455 model.removeRows(5, 1, QModelIndex());
1456 QVERIFY(view.selectionModel()->selectedIndexes().size() > 0); // Should still have a selection
1457}
1458
1459void tst_QSortFilterProxyModel::filterCurrent()
1460{
1461 QStandardItemModel model(2, 1);
1462 model.setData(model.index(0, 0), QString("AAA"));
1463 model.setData(model.index(1, 0), QString("BBB"));
1464 QSortFilterProxyModel proxy;
1465 proxy.setSourceModel(&model);
1466 QTreeView view;
1467
1468 view.show();
1469 view.setModel(&proxy);
1470 QSignalSpy spy(view.selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)));
1471 QVERIFY(spy.isValid());
1472
1473 view.setCurrentIndex(proxy.index(0, 0));
1474 QCOMPARE(spy.count(), 1);
1475 proxy.setFilterRegExp(QRegExp("^B"));
1476 QCOMPARE(spy.count(), 2);
1477}
1478
1479void tst_QSortFilterProxyModel::changeSourceLayout()
1480{
1481 QStandardItemModel model(2, 1);
1482 model.setData(model.index(0, 0), QString("b"));
1483 model.setData(model.index(1, 0), QString("a"));
1484 QSortFilterProxyModel proxy;
1485 proxy.setSourceModel(&model);
1486
1487 QList<QPersistentModelIndex> persistentSourceIndexes;
1488 QList<QPersistentModelIndex> persistentProxyIndexes;
1489 for (int row = 0; row < model.rowCount(); ++row) {
1490 persistentSourceIndexes.append(model.index(row, 0));
1491 persistentProxyIndexes.append(proxy.index(row, 0));
1492 }
1493
1494 // change layout of source model
1495 model.sort(0, Qt::AscendingOrder);
1496
1497 for (int row = 0; row < model.rowCount(); ++row) {
1498 QCOMPARE(persistentProxyIndexes.at(row).row(),
1499 persistentSourceIndexes.at(row).row());
1500 }
1501}
1502
1503void tst_QSortFilterProxyModel::removeSourceRows_data()
1504{
1505 QTest::addColumn<QStringList>("sourceItems");
1506 QTest::addColumn<int>("start");
1507 QTest::addColumn<int>("count");
1508 QTest::addColumn<int>("sortOrder");
1509 QTest::addColumn<IntPairList>("expectedRemovedProxyIntervals");
1510 QTest::addColumn<QStringList>("expectedProxyItems");
1511
1512 QTest::newRow("remove one, no sorting")
1513 << (QStringList() << "a" << "b") // sourceItems
1514 << 0 // start
1515 << 1 // count
1516 << -1 // sortOrder (no sorting)
1517 << (IntPairList() << IntPair(0, 0)) // expectedRemovedIntervals
1518 << (QStringList() << "b") // expectedProxyItems
1519 ;
1520 QTest::newRow("remove one, ascending sort (same order)")
1521 << (QStringList() << "a" << "b") // sourceItems
1522 << 0 // start
1523 << 1 // count
1524 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1525 << (IntPairList() << IntPair(0, 0)) // expectedRemovedIntervals
1526 << (QStringList() << "b") // expectedProxyItems
1527 ;
1528 QTest::newRow("remove one, ascending sort (reverse order)")
1529 << (QStringList() << "b" << "a") // sourceItems
1530 << 0 // start
1531 << 1 // count
1532 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1533 << (IntPairList() << IntPair(1, 1)) // expectedRemovedIntervals
1534 << (QStringList() << "a") // expectedProxyItems
1535 ;
1536 QTest::newRow("remove two, multiple proxy intervals")
1537 << (QStringList() << "c" << "d" << "a" << "b") // sourceItems
1538 << 1 // start
1539 << 2 // count
1540 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1541 << (IntPairList() << IntPair(3, 3) << IntPair(0, 0)) // expectedRemovedIntervals
1542 << (QStringList() << "b" << "c") // expectedProxyItems
1543 ;
1544 QTest::newRow("remove three, multiple proxy intervals")
1545 << (QStringList() << "b" << "d" << "f" << "a" << "c" << "e") // sourceItems
1546 << 3 // start
1547 << 3 // count
1548 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1549 << (IntPairList() << IntPair(4, 4) << IntPair(2, 2) << IntPair(0, 0)) // expectedRemovedIntervals
1550 << (QStringList() << "b" << "d" << "f") // expectedProxyItems
1551 ;
1552 QTest::newRow("remove all, single proxy intervals")
1553 << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // sourceItems
1554 << 0 // start
1555 << 6 // count
1556 << static_cast<int>(Qt::DescendingOrder) // sortOrder
1557 << (IntPairList() << IntPair(0, 5)) // expectedRemovedIntervals
1558 << QStringList() // expectedProxyItems
1559 ;
1560}
1561
1562// Check that correct proxy model rows are removed when rows are removed
1563// from the source model
1564void tst_QSortFilterProxyModel::removeSourceRows()
1565{
1566 QFETCH(QStringList, sourceItems);
1567 QFETCH(int, start);
1568 QFETCH(int, count);
1569 QFETCH(int, sortOrder);
1570 QFETCH(IntPairList, expectedRemovedProxyIntervals);
1571 QFETCH(QStringList, expectedProxyItems);
1572
1573 QStandardItemModel model;
1574 QSortFilterProxyModel proxy;
1575
1576 proxy.setSourceModel(&model);
1577 model.insertColumns(0, 1);
1578 model.insertRows(0, sourceItems.count());
1579
1580 for (int i = 0; i < sourceItems.count(); ++i) {
1581 QModelIndex sindex = model.index(i, 0, QModelIndex());
1582 model.setData(sindex, sourceItems.at(i), Qt::DisplayRole);
1583 QModelIndex pindex = proxy.index(i, 0, QModelIndex());
1584 QCOMPARE(proxy.data(pindex, Qt::DisplayRole), model.data(sindex, Qt::DisplayRole));
1585 }
1586
1587 if (sortOrder != -1)
1588 proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
1589 (void)proxy.rowCount(QModelIndex()); // force mapping
1590
1591 QSignalSpy removeSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex,int,int)));
1592 QSignalSpy insertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex,int,int)));
1593 QSignalSpy aboutToRemoveSpy(&proxy, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
1594 QSignalSpy aboutToInsertSpy(&proxy, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)));
1595
1596 QVERIFY(removeSpy.isValid());
1597 QVERIFY(insertSpy.isValid());
1598 QVERIFY(aboutToRemoveSpy.isValid());
1599 QVERIFY(aboutToInsertSpy.isValid());
1600
1601 model.removeRows(start, count, QModelIndex());
1602
1603 QCOMPARE(aboutToRemoveSpy.count(), expectedRemovedProxyIntervals.count());
1604 for (int i = 0; i < aboutToRemoveSpy.count(); ++i) {
1605 QList<QVariant> args = aboutToRemoveSpy.at(i);
1606 QVERIFY(args.at(1).type() == QVariant::Int);
1607 QVERIFY(args.at(2).type() == QVariant::Int);
1608 QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first);
1609 QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second);
1610 }
1611 QCOMPARE(removeSpy.count(), expectedRemovedProxyIntervals.count());
1612 for (int i = 0; i < removeSpy.count(); ++i) {
1613 QList<QVariant> args = removeSpy.at(i);
1614 QVERIFY(args.at(1).type() == QVariant::Int);
1615 QVERIFY(args.at(2).type() == QVariant::Int);
1616 QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first);
1617 QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second);
1618 }
1619
1620 QCOMPARE(insertSpy.count(), 0);
1621 QCOMPARE(aboutToInsertSpy.count(), 0);
1622
1623 QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxyItems.count());
1624 for (int i = 0; i < expectedProxyItems.count(); ++i) {
1625 QModelIndex pindex = proxy.index(i, 0, QModelIndex());
1626 QCOMPARE(proxy.data(pindex, Qt::DisplayRole).toString(), expectedProxyItems.at(i));
1627 }
1628}
1629
1630void tst_QSortFilterProxyModel::insertSourceRows_data()
1631{
1632 QTest::addColumn<QStringList>("sourceItems");
1633 QTest::addColumn<int>("start");
1634 QTest::addColumn<QStringList>("newItems");
1635 QTest::addColumn<int>("sortOrder");
1636 QTest::addColumn<QStringList>("proxyItems");
1637
1638 QTest::newRow("insert (1)")
1639 << (QStringList() << "c" << "b") // sourceItems
1640 << 1 // start
1641 << (QStringList() << "a") // newItems
1642 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1643 << (QStringList() << "a" << "b" << "c") // proxyItems
1644 ;
1645
1646 QTest::newRow("insert (2)")
1647 << (QStringList() << "d" << "b" << "c") // sourceItems
1648 << 3 // start
1649 << (QStringList() << "a") // newItems
1650 << static_cast<int>(Qt::DescendingOrder) // sortOrder
1651 << (QStringList() << "d" << "c" << "b" << "a") // proxyItems
1652 ;
1653}
1654
1655// Check that rows are inserted at correct position in proxy model when
1656// rows are inserted into the source model
1657void tst_QSortFilterProxyModel::insertSourceRows()
1658{
1659 QFETCH(QStringList, sourceItems);
1660 QFETCH(int, start);
1661 QFETCH(QStringList, newItems);
1662 QFETCH(int, sortOrder);
1663 QFETCH(QStringList, proxyItems);
1664
1665 QStandardItemModel model;
1666 QSortFilterProxyModel proxy;
1667 proxy.setDynamicSortFilter(true);
1668
1669 proxy.setSourceModel(&model);
1670 model.insertColumns(0, 1);
1671 model.insertRows(0, sourceItems.count());
1672
1673 for (int i = 0; i < sourceItems.count(); ++i) {
1674 QModelIndex index = model.index(i, 0, QModelIndex());
1675 model.setData(index, sourceItems.at(i), Qt::DisplayRole);
1676 }
1677
1678 proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
1679 (void)proxy.rowCount(QModelIndex()); // force mapping
1680
1681 model.insertRows(start, newItems.size(), QModelIndex());
1682
1683 QCOMPARE(proxy.rowCount(QModelIndex()), proxyItems.count());
1684 for (int i = 0; i < newItems.count(); ++i) {
1685 QModelIndex index = model.index(start + i, 0, QModelIndex());
1686 model.setData(index, newItems.at(i), Qt::DisplayRole);
1687 }
1688
1689 for (int i = 0; i < proxyItems.count(); ++i) {
1690 QModelIndex index = proxy.index(i, 0, QModelIndex());
1691 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), proxyItems.at(i));
1692 }
1693}
1694
1695void tst_QSortFilterProxyModel::changeFilter_data()
1696{
1697 QTest::addColumn<QStringList>("sourceItems");
1698 QTest::addColumn<int>("sortOrder");
1699 QTest::addColumn<QString>("initialFilter");
1700 QTest::addColumn<IntPairList>("initialRemoveIntervals");
1701 QTest::addColumn<QStringList>("initialProxyItems");
1702 QTest::addColumn<QString>("finalFilter");
1703 QTest::addColumn<IntPairList>("finalRemoveIntervals");
1704 QTest::addColumn<IntPairList>("insertIntervals");
1705 QTest::addColumn<QStringList>("finalProxyItems");
1706
1707 QTest::newRow("filter (1)")
1708 << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // sourceItems
1709 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1710 << "a|b|c" // initialFilter
1711 << (IntPairList() << IntPair(3, 5)) // initialRemoveIntervals
1712 << (QStringList() << "a" << "b" << "c") // initialProxyItems
1713 << "b|d|f" // finalFilter
1714 << (IntPairList() << IntPair(2, 2) << IntPair(0, 0)) // finalRemoveIntervals
1715 << (IntPairList() << IntPair(1, 2)) // insertIntervals
1716 << (QStringList() << "b" << "d" << "f") // finalProxyItems
1717 ;
1718
1719 QTest::newRow("filter (2)")
1720 << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // sourceItems
1721 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1722 << "a|c|e" // initialFilter
1723 << (IntPairList() << IntPair(5, 5) << IntPair(3, 3) << IntPair(1, 1)) // initialRemoveIntervals
1724 << (QStringList() << "a" << "c" << "e") // initialProxyItems
1725 << "" // finalFilter
1726 << IntPairList() // finalRemoveIntervals
1727 << (IntPairList() << IntPair(3, 3) << IntPair(2, 2) << IntPair(1, 1)) // insertIntervals
1728 << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // finalProxyItems
1729 ;
1730
1731 QTest::newRow("filter (3)")
1732 << (QStringList() << "a" << "b" << "c") // sourceItems
1733 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1734 << "a" // initialFilter
1735 << (IntPairList() << IntPair(1, 2)) // initialRemoveIntervals
1736 << (QStringList() << "a") // initialProxyItems
1737 << "a" // finalFilter
1738 << IntPairList() // finalRemoveIntervals
1739 << IntPairList() // insertIntervals
1740 << (QStringList() << "a") // finalProxyItems
1741 ;
1742}
1743
1744// Check that rows are added/removed when filter changes
1745void tst_QSortFilterProxyModel::changeFilter()
1746{
1747 QFETCH(QStringList, sourceItems);
1748 QFETCH(int, sortOrder);
1749 QFETCH(QString, initialFilter);
1750 QFETCH(IntPairList, initialRemoveIntervals);
1751 QFETCH(QStringList, initialProxyItems);
1752 QFETCH(QString, finalFilter);
1753 QFETCH(IntPairList, finalRemoveIntervals);
1754 QFETCH(IntPairList, insertIntervals);
1755 QFETCH(QStringList, finalProxyItems);
1756
1757 QStandardItemModel model;
1758 QSortFilterProxyModel proxy;
1759
1760 proxy.setSourceModel(&model);
1761 model.insertColumns(0, 1);
1762 model.insertRows(0, sourceItems.count());
1763
1764 for (int i = 0; i < sourceItems.count(); ++i) {
1765 QModelIndex index = model.index(i, 0, QModelIndex());
1766 model.setData(index, sourceItems.at(i), Qt::DisplayRole);
1767 }
1768
1769 proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
1770 (void)proxy.rowCount(QModelIndex()); // force mapping
1771
1772 QSignalSpy initialRemoveSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex,int,int)));
1773 QSignalSpy initialInsertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex,int,int)));
1774
1775 QVERIFY(initialRemoveSpy.isValid());
1776 QVERIFY(initialInsertSpy.isValid());
1777
1778 proxy.setFilterRegExp(initialFilter);
1779
1780 QCOMPARE(initialRemoveSpy.count(), initialRemoveIntervals.count());
1781 QCOMPARE(initialInsertSpy.count(), 0);
1782 for (int i = 0; i < initialRemoveSpy.count(); ++i) {
1783 QList<QVariant> args = initialRemoveSpy.at(i);
1784 QVERIFY(args.at(1).type() == QVariant::Int);
1785 QVERIFY(args.at(2).type() == QVariant::Int);
1786 QCOMPARE(args.at(1).toInt(), initialRemoveIntervals.at(i).first);
1787 QCOMPARE(args.at(2).toInt(), initialRemoveIntervals.at(i).second);
1788 }
1789
1790 QCOMPARE(proxy.rowCount(QModelIndex()), initialProxyItems.count());
1791 for (int i = 0; i < initialProxyItems.count(); ++i) {
1792 QModelIndex index = proxy.index(i, 0, QModelIndex());
1793 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), initialProxyItems.at(i));
1794 }
1795
1796 QSignalSpy finalRemoveSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex,int,int)));
1797 QSignalSpy finalInsertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex,int,int)));
1798
1799 QVERIFY(finalRemoveSpy.isValid());
1800 QVERIFY(finalInsertSpy.isValid());
1801
1802 proxy.setFilterRegExp(finalFilter);
1803
1804 QCOMPARE(finalRemoveSpy.count(), finalRemoveIntervals.count());
1805 for (int i = 0; i < finalRemoveSpy.count(); ++i) {
1806 QList<QVariant> args = finalRemoveSpy.at(i);
1807 QVERIFY(args.at(1).type() == QVariant::Int);
1808 QVERIFY(args.at(2).type() == QVariant::Int);
1809 QCOMPARE(args.at(1).toInt(), finalRemoveIntervals.at(i).first);
1810 QCOMPARE(args.at(2).toInt(), finalRemoveIntervals.at(i).second);
1811 }
1812
1813#ifdef Q_OS_IRIX
1814 QEXPECT_FAIL("filter (2)", "Not reliable on IRIX", Abort);
1815#endif
1816 QCOMPARE(finalInsertSpy.count(), insertIntervals.count());
1817 for (int i = 0; i < finalInsertSpy.count(); ++i) {
1818 QList<QVariant> args = finalInsertSpy.at(i);
1819 QVERIFY(args.at(1).type() == QVariant::Int);
1820 QVERIFY(args.at(2).type() == QVariant::Int);
1821 QCOMPARE(args.at(1).toInt(), insertIntervals.at(i).first);
1822 QCOMPARE(args.at(2).toInt(), insertIntervals.at(i).second);
1823 }
1824
1825 QCOMPARE(proxy.rowCount(QModelIndex()), finalProxyItems.count());
1826 for (int i = 0; i < finalProxyItems.count(); ++i) {
1827 QModelIndex index = proxy.index(i, 0, QModelIndex());
1828 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), finalProxyItems.at(i));
1829 }
1830}
1831
1832void tst_QSortFilterProxyModel::changeSourceData_data()
1833{
1834 QTest::addColumn<QStringList>("sourceItems");
1835 QTest::addColumn<int>("sortOrder");
1836 QTest::addColumn<QString>("filter");
1837 QTest::addColumn<bool>("dynamic");
1838 QTest::addColumn<int>("row");
1839 QTest::addColumn<QString>("newValue");
1840 QTest::addColumn<IntPairList>("removeIntervals");
1841 QTest::addColumn<IntPairList>("insertIntervals");
1842 QTest::addColumn<QStringList>("proxyItems");
1843
1844 QTest::newRow("changeSourceData (1)")
1845 << (QStringList() << "c" << "b" << "a") // sourceItems
1846 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1847 << "" // filter
1848 << true // dynamic
1849 << 2 // row
1850 << "z" // newValue
1851 << IntPairList() // removeIntervals
1852 << IntPairList() // insertIntervals
1853 << (QStringList() << "b" << "c" << "z") // proxyItems
1854 ;
1855
1856 QTest::newRow("changeSourceData (2)")
1857 << (QStringList() << "b" << "c" << "z") // sourceItems
1858 << static_cast<int>(Qt::DescendingOrder) // sortOrder
1859 << "" // filter
1860 << true // dynamic
1861 << 1 // row
1862 << "a" // newValue
1863 << IntPairList() // removeIntervals
1864 << IntPairList() // insertIntervals
1865 << (QStringList() << "z" << "b" << "a") // proxyItems
1866 ;
1867
1868 QTest::newRow("changeSourceData (3)")
1869 << (QStringList() << "a" << "b") // sourceItems
1870 << static_cast<int>(Qt::DescendingOrder) // sortOrder
1871 << "" // filter
1872 << true // dynamic
1873 << 0 // row
1874 << "a" // newValue
1875 << IntPairList() // removeIntervals
1876 << IntPairList() // insertIntervals
1877 << (QStringList() << "b" << "a") // proxyItems
1878 ;
1879
1880 QTest::newRow("changeSourceData (4)")
1881 << (QStringList() << "a" << "b" << "c" << "d") // sourceItems
1882 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1883 << "a|c" // filter
1884 << true // dynamic
1885 << 1 // row
1886 << "x" // newValue
1887 << IntPairList() // removeIntervals
1888 << IntPairList() // insertIntervals
1889 << (QStringList() << "a" << "c") // proxyItems
1890 ;
1891
1892 QTest::newRow("changeSourceData (5)")
1893 << (QStringList() << "a" << "b" << "c" << "d") // sourceItems
1894 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1895 << "a|c|x" // filter
1896 << true // dynamic
1897 << 1 // row
1898 << "x" // newValue
1899 << IntPairList() // removeIntervals
1900 << (IntPairList() << IntPair(2, 2)) // insertIntervals
1901 << (QStringList() << "a" << "c" << "x") // proxyItems
1902 ;
1903
1904 QTest::newRow("changeSourceData (6)")
1905 << (QStringList() << "c" << "b" << "a") // sourceItems
1906 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1907 << "" // filter
1908 << false // dynamic
1909 << 2 // row
1910 << "x" // newValue
1911 << IntPairList() // removeIntervals
1912 << IntPairList() // insertIntervals
1913 << (QStringList() << "x" << "b" << "c") // proxyItems
1914 ;
1915}
1916
1917void tst_QSortFilterProxyModel::changeSourceData()
1918{
1919 QFETCH(QStringList, sourceItems);
1920 QFETCH(int, sortOrder);
1921 QFETCH(QString, filter);
1922 QFETCH(bool, dynamic);
1923 QFETCH(int, row);
1924 QFETCH(QString, newValue);
1925 QFETCH(IntPairList, removeIntervals);
1926 QFETCH(IntPairList, insertIntervals);
1927 QFETCH(QStringList, proxyItems);
1928
1929 QStandardItemModel model;
1930 QSortFilterProxyModel proxy;
1931
1932 proxy.setDynamicSortFilter(dynamic);
1933 proxy.setSourceModel(&model);
1934 model.insertColumns(0, 1);
1935 model.insertRows(0, sourceItems.count());
1936
1937 for (int i = 0; i < sourceItems.count(); ++i) {
1938 QModelIndex index = model.index(i, 0, QModelIndex());
1939 model.setData(index, sourceItems.at(i), Qt::DisplayRole);
1940 }
1941
1942 proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
1943 (void)proxy.rowCount(QModelIndex()); // force mapping
1944
1945 proxy.setFilterRegExp(filter);
1946
1947 QSignalSpy removeSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex,int,int)));
1948 QSignalSpy insertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex,int,int)));
1949
1950 QVERIFY(removeSpy.isValid());
1951 QVERIFY(insertSpy.isValid());
1952
1953 {
1954 QModelIndex index = model.index(row, 0, QModelIndex());
1955 model.setData(index, newValue, Qt::DisplayRole);
1956 }
1957
1958 QCOMPARE(removeSpy.count(), removeIntervals.count());
1959 for (int i = 0; i < removeSpy.count(); ++i) {
1960 QList<QVariant> args = removeSpy.at(i);
1961 QVERIFY(args.at(1).type() == QVariant::Int);
1962 QVERIFY(args.at(2).type() == QVariant::Int);
1963 QCOMPARE(args.at(1).toInt(), removeIntervals.at(i).first);
1964 QCOMPARE(args.at(2).toInt(), removeIntervals.at(i).second);
1965 }
1966
1967 QCOMPARE(insertSpy.count(), insertIntervals.count());
1968 for (int i = 0; i < insertSpy.count(); ++i) {
1969 QList<QVariant> args = insertSpy.at(i);
1970 QVERIFY(args.at(1).type() == QVariant::Int);
1971 QVERIFY(args.at(2).type() == QVariant::Int);
1972 QCOMPARE(args.at(1).toInt(), insertIntervals.at(i).first);
1973 QCOMPARE(args.at(2).toInt(), insertIntervals.at(i).second);
1974 }
1975
1976 QCOMPARE(proxy.rowCount(QModelIndex()), proxyItems.count());
1977 for (int i = 0; i < proxyItems.count(); ++i) {
1978 QModelIndex index = proxy.index(i, 0, QModelIndex());
1979 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), proxyItems.at(i));
1980 }
1981}
1982
1983void tst_QSortFilterProxyModel::sortFilterRole()
1984{
1985 QStandardItemModel model;
1986 QSortFilterProxyModel proxy;
1987 proxy.setSourceModel(&model);
1988 model.insertColumns(0, 1);
1989
1990 QList<QPair<QVariant, QVariant> > sourceItems;
1991 sourceItems = QList<QPair<QVariant, QVariant> >()
1992 << QPair<QVariant, QVariant>("b", 3)
1993 << QPair<QVariant, QVariant>("c", 2)
1994 << QPair<QVariant, QVariant>("a", 1);
1995
1996 QList<int> orderedItems;
1997 orderedItems = QList<int>()
1998 << 2 << 1;
1999
2000 model.insertRows(0, sourceItems.count());
2001 for (int i = 0; i < sourceItems.count(); ++i) {
2002 QModelIndex index = model.index(i, 0, QModelIndex());
2003 model.setData(index, sourceItems.at(i).first, Qt::DisplayRole);
2004 model.setData(index, sourceItems.at(i).second, Qt::UserRole);
2005 }
2006
2007 proxy.setFilterRegExp("2");
2008 QCOMPARE(proxy.rowCount(), 0); // Qt::DisplayRole is default role
2009
2010 proxy.setFilterRole(Qt::UserRole);
2011 QCOMPARE(proxy.rowCount(), 1);
2012
2013 proxy.setFilterRole(Qt::DisplayRole);
2014 QCOMPARE(proxy.rowCount(), 0);
2015
2016 proxy.setFilterRegExp("1|2|3");
2017 QCOMPARE(proxy.rowCount(), 0);
2018
2019 proxy.setFilterRole(Qt::UserRole);
2020 QCOMPARE(proxy.rowCount(), 3);
2021
2022 proxy.sort(0, Qt::AscendingOrder);
2023 QCOMPARE(proxy.rowCount(), 3);
2024
2025 proxy.setSortRole(Qt::UserRole);
2026 proxy.setFilterRole(Qt::DisplayRole);
2027 proxy.setFilterRegExp("a|c");
2028 QCOMPARE(proxy.rowCount(), orderedItems.count());
2029 for (int i = 0; i < proxy.rowCount(); ++i) {
2030 QModelIndex index = proxy.index(i, 0, QModelIndex());
2031 QCOMPARE(proxy.data(index, Qt::DisplayRole), sourceItems.at(orderedItems.at(i)).first);
2032 }
2033}
2034
2035void tst_QSortFilterProxyModel::selectionFilteredOut()
2036{
2037 QStandardItemModel model(2, 1);
2038 model.setData(model.index(0, 0), QString("AAA"));
2039 model.setData(model.index(1, 0), QString("BBB"));
2040 QSortFilterProxyModel proxy;
2041 proxy.setSourceModel(&model);
2042 QTreeView view;
2043
2044 view.show();
2045 view.setModel(&proxy);
2046 QSignalSpy spy(view.selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)));
2047 QVERIFY(spy.isValid());
2048
2049 view.setCurrentIndex(proxy.index(0, 0));
2050 QCOMPARE(spy.count(), 1);
2051 proxy.setFilterRegExp(QRegExp("^B"));
2052 QCOMPARE(spy.count(), 2);
2053}
2054
2055void tst_QSortFilterProxyModel::match_data()
2056{
2057 QTest::addColumn<QStringList>("sourceItems");
2058 QTest::addColumn<int>("sortOrder");
2059 QTest::addColumn<QString>("filter");
2060 QTest::addColumn<int>("proxyStartRow");
2061 QTest::addColumn<QString>("what");
2062 QTest::addColumn<int>("matchFlags");
2063 QTest::addColumn<IntList>("expectedProxyItems");
2064 QTest::newRow("1")
2065 << (QStringList() << "a") // sourceItems
2066 << static_cast<int>(Qt::AscendingOrder) // sortOrder
2067 << "" // filter
2068 << 0 // proxyStartRow
2069 << "a" // what
2070 << static_cast<int>(Qt::MatchExactly) // matchFlags
2071 << (IntList() << 0); // expectedProxyItems
2072 QTest::newRow("2")
2073 << (QStringList() << "a" << "b") // sourceItems
2074 << static_cast<int>(Qt::AscendingOrder) // sortOrder
2075 << "" // filter
2076 << 0 // proxyStartRow
2077 << "b" // what
2078 << static_cast<int>(Qt::MatchExactly) // matchFlags
2079 << (IntList() << 1); // expectedProxyItems
2080 QTest::newRow("3")
2081 << (QStringList() << "a" << "b") // sourceItems
2082 << static_cast<int>(Qt::DescendingOrder) // sortOrder
2083 << "" // filter
2084 << 0 // proxyStartRow
2085 << "a" // what
2086 << static_cast<int>(Qt::MatchExactly) // matchFlags
2087 << (IntList() << 1); // expectedProxyItems
2088 QTest::newRow("4")
2089 << (QStringList() << "b" << "d" << "a" << "c") // sourceItems
2090 << static_cast<int>(Qt::AscendingOrder) // sortOrder
2091 << "" // filter
2092 << 1 // proxyStartRow
2093 << "a" // what
2094 << static_cast<int>(Qt::MatchExactly) // matchFlags
2095 << IntList(); // expectedProxyItems
2096 QTest::newRow("5")
2097 << (QStringList() << "b" << "d" << "a" << "c") // sourceItems
2098 << static_cast<int>(Qt::AscendingOrder) // sortOrder
2099 << "a|b" // filter
2100 << 0 // proxyStartRow
2101 << "c" // what
2102 << static_cast<int>(Qt::MatchExactly) // matchFlags
2103 << IntList(); // expectedProxyItems
2104 QTest::newRow("6")
2105 << (QStringList() << "b" << "d" << "a" << "c") // sourceItems
2106 << static_cast<int>(Qt::DescendingOrder) // sortOrder
2107 << "a|b" // filter
2108 << 0 // proxyStartRow
2109 << "b" // what
2110 << static_cast<int>(Qt::MatchExactly) // matchFlags
2111 << (IntList() << 0); // expectedProxyItems
2112}
2113
2114void tst_QSortFilterProxyModel::match()
2115{
2116 QFETCH(QStringList, sourceItems);
2117 QFETCH(int, sortOrder);
2118 QFETCH(QString, filter);
2119 QFETCH(int, proxyStartRow);
2120 QFETCH(QString, what);
2121 QFETCH(int, matchFlags);
2122 QFETCH(IntList, expectedProxyItems);
2123
2124 QStandardItemModel model;
2125 QSortFilterProxyModel proxy;
2126
2127 proxy.setSourceModel(&model);
2128 model.insertColumns(0, 1);
2129 model.insertRows(0, sourceItems.count());
2130
2131 for (int i = 0; i < sourceItems.count(); ++i) {
2132 QModelIndex index = model.index(i, 0, QModelIndex());
2133 model.setData(index, sourceItems.at(i), Qt::DisplayRole);
2134 }
2135
2136 proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
2137 proxy.setFilterRegExp(filter);
2138
2139 QModelIndex startIndex = proxy.index(proxyStartRow, 0);
2140 QModelIndexList indexes = proxy.match(startIndex, Qt::DisplayRole, what,
2141 expectedProxyItems.count(),
2142 Qt::MatchFlags(matchFlags));
2143 QCOMPARE(indexes.count(), expectedProxyItems.count());
2144 for (int i = 0; i < indexes.count(); ++i)
2145 QCOMPARE(indexes.at(i).row(), expectedProxyItems.at(i));
2146}
2147
2148void tst_QSortFilterProxyModel::insertIntoChildrenlessItem()
2149{
2150 QStandardItemModel model;
2151 QStandardItem *itemA = new QStandardItem("a");
2152 model.appendRow(itemA);
2153 QStandardItem *itemB = new QStandardItem("b");
2154 model.appendRow(itemB);
2155 QStandardItem *itemC = new QStandardItem("c");
2156 model.appendRow(itemC);
2157
2158 QSortFilterProxyModel proxy;
2159 proxy.setSourceModel(&model);
2160
2161 QSignalSpy colsInsertedSpy(&proxy, SIGNAL(columnsInserted(QModelIndex,int,int)));
2162 QSignalSpy rowsInsertedSpy(&proxy, SIGNAL(rowsInserted(QModelIndex,int,int)));
2163
2164 QVERIFY(colsInsertedSpy.isValid());
2165 QVERIFY(rowsInsertedSpy.isValid());
2166
2167 (void)proxy.rowCount(QModelIndex()); // force mapping of "a", "b", "c"
2168 QCOMPARE(colsInsertedSpy.count(), 0);
2169 QCOMPARE(rowsInsertedSpy.count(), 0);
2170
2171 // now add a child to itemB ==> should get insert notification from the proxy
2172 itemB->appendRow(new QStandardItem("a.0"));
2173 QCOMPARE(colsInsertedSpy.count(), 1);
2174 QCOMPARE(rowsInsertedSpy.count(), 1);
2175
2176 QVariantList args = colsInsertedSpy.takeFirst();
2177 QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), proxy.mapFromSource(itemB->index()));
2178 QCOMPARE(qvariant_cast<int>(args.at(1)), 0);
2179 QCOMPARE(qvariant_cast<int>(args.at(2)), 0);
2180
2181 args = rowsInsertedSpy.takeFirst();
2182 QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), proxy.mapFromSource(itemB->index()));
2183 QCOMPARE(qvariant_cast<int>(args.at(1)), 0);
2184 QCOMPARE(qvariant_cast<int>(args.at(2)), 0);
2185}
2186
2187void tst_QSortFilterProxyModel::invalidateMappedChildren()
2188{
2189 QStandardItemModel model;
2190
2191 QSortFilterProxyModel proxy;
2192 proxy.setSourceModel(&model);
2193
2194 QStandardItem *itemA = new QStandardItem("a");
2195 model.appendRow(itemA);
2196 QStandardItem *itemB = new QStandardItem("b");
2197 itemA->appendRow(itemB);
2198
2199 QStandardItem *itemC = new QStandardItem("c");
2200 itemB->appendRow(itemC);
2201 itemC->appendRow(new QStandardItem("d"));
2202
2203 // force mappings
2204 (void)proxy.hasChildren(QModelIndex());
2205 (void)proxy.hasChildren(proxy.mapFromSource(itemA->index()));
2206 (void)proxy.hasChildren(proxy.mapFromSource(itemB->index()));
2207 (void)proxy.hasChildren(proxy.mapFromSource(itemC->index()));
2208
2209 itemB->removeRow(0); // should invalidate mapping of itemC
2210 itemC = new QStandardItem("c");
2211 itemA->appendRow(itemC);
2212 itemC->appendRow(new QStandardItem("d"));
2213
2214 itemA->removeRow(1); // should invalidate mapping of itemC
2215 itemC = new QStandardItem("c");
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches