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