1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "qsortfilterproxymodel.h"
41 #include "qitemselectionmodel.h"
42 #include <qsize.h>
43 #include <qdebug.h>
44 #include <qdatetime.h>
45 #include <qpair.h>
46 #include <qstringlist.h>
47 #include <private/qabstractitemmodel_p.h>
48 #include <private/qabstractproxymodel_p.h>
49
50 #include <algorithm>
51
52 QT_BEGIN_NAMESPACE
53
54 typedef QVector<QPair<QModelIndex, QPersistentModelIndex> > QModelIndexPairList;
55
56 struct QSortFilterProxyModelDataChanged
57 {
QSortFilterProxyModelDataChangedQSortFilterProxyModelDataChanged58 QSortFilterProxyModelDataChanged(const QModelIndex &tl, const QModelIndex &br)
59 : topLeft(tl), bottomRight(br) { }
60
61 QModelIndex topLeft;
62 QModelIndex bottomRight;
63 };
64
qVectorToSet(const QVector<int> & vector)65 static inline QSet<int> qVectorToSet(const QVector<int> &vector)
66 {
67 return {vector.begin(), vector.end()};
68 }
69
70 class QSortFilterProxyModelLessThan
71 {
72 public:
QSortFilterProxyModelLessThan(int column,const QModelIndex & parent,const QAbstractItemModel * source,const QSortFilterProxyModel * proxy)73 inline QSortFilterProxyModelLessThan(int column, const QModelIndex &parent,
74 const QAbstractItemModel *source,
75 const QSortFilterProxyModel *proxy)
76 : sort_column(column), source_parent(parent), source_model(source), proxy_model(proxy) {}
77
operator ()(int r1,int r2) const78 inline bool operator()(int r1, int r2) const
79 {
80 QModelIndex i1 = source_model->index(r1, sort_column, source_parent);
81 QModelIndex i2 = source_model->index(r2, sort_column, source_parent);
82 return proxy_model->lessThan(i1, i2);
83 }
84
85 private:
86 int sort_column;
87 QModelIndex source_parent;
88 const QAbstractItemModel *source_model;
89 const QSortFilterProxyModel *proxy_model;
90 };
91
92 class QSortFilterProxyModelGreaterThan
93 {
94 public:
QSortFilterProxyModelGreaterThan(int column,const QModelIndex & parent,const QAbstractItemModel * source,const QSortFilterProxyModel * proxy)95 inline QSortFilterProxyModelGreaterThan(int column, const QModelIndex &parent,
96 const QAbstractItemModel *source,
97 const QSortFilterProxyModel *proxy)
98 : sort_column(column), source_parent(parent),
99 source_model(source), proxy_model(proxy) {}
100
operator ()(int r1,int r2) const101 inline bool operator()(int r1, int r2) const
102 {
103 QModelIndex i1 = source_model->index(r1, sort_column, source_parent);
104 QModelIndex i2 = source_model->index(r2, sort_column, source_parent);
105 return proxy_model->lessThan(i2, i1);
106 }
107
108 private:
109 int sort_column;
110 QModelIndex source_parent;
111 const QAbstractItemModel *source_model;
112 const QSortFilterProxyModel *proxy_model;
113 };
114
115
116 //this struct is used to store what are the rows that are removed
117 //between a call to rowsAboutToBeRemoved and rowsRemoved
118 //it avoids readding rows to the mapping that are currently being removed
119 struct QRowsRemoval
120 {
QRowsRemovalQRowsRemoval121 QRowsRemoval(const QModelIndex &parent_source, int start, int end) : parent_source(parent_source), start(start), end(end)
122 {
123 }
124
QRowsRemovalQRowsRemoval125 QRowsRemoval() : start(-1), end(-1)
126 {
127 }
128
containsQRowsRemoval129 bool contains(QModelIndex parent, int row) const
130 {
131 do {
132 if (parent == parent_source)
133 return row >= start && row <= end;
134 row = parent.row();
135 parent = parent.parent();
136 } while (row >= 0);
137 return false;
138 }
139 private:
140 QModelIndex parent_source;
141 int start;
142 int end;
143 };
144
145 class RegularExpressionData {
146
147 private:
148 enum class ExpressionType {
149 RegExp,
150 #if QT_CONFIG(regularexpression)
151 RegularExpression
152 #endif
153 };
154
155 public:
RegularExpressionData()156 RegularExpressionData() :
157 m_type(ExpressionType::RegExp)
158 {}
159
160 #if QT_CONFIG(regularexpression)
regularExpression() const161 QRegularExpression regularExpression() const
162 {
163 if (m_type == ExpressionType::RegularExpression)
164 return m_regularExpression;
165 return QRegularExpression();
166 }
167
setRegularExpression(const QRegularExpression & rx)168 void setRegularExpression(const QRegularExpression &rx)
169 {
170 m_type = ExpressionType::RegularExpression;
171 m_regularExpression = rx;
172 m_regExp = QRegExp();
173 }
174 #endif
175
regExp() const176 QRegExp regExp() const
177 {
178 if (m_type == ExpressionType::RegExp)
179 return m_regExp;
180 return QRegExp();
181 }
182
setRegExp(const QRegExp & rx)183 void setRegExp(const QRegExp &rx)
184 {
185 m_type = ExpressionType::RegExp;
186 m_regExp = rx;
187 #if QT_CONFIG(regularexpression)
188 m_regularExpression = QRegularExpression();
189 #endif
190
191 }
192
isEmpty() const193 bool isEmpty() const
194 {
195 bool result = true;
196 switch (m_type) {
197 case ExpressionType::RegExp:
198 result = m_regExp.isEmpty();
199 break;
200 #if QT_CONFIG(regularexpression)
201 case ExpressionType::RegularExpression:
202 result = m_regularExpression.pattern().isEmpty();
203 break;
204 #endif
205 }
206 return result;
207 }
208
caseSensitivity() const209 Qt::CaseSensitivity caseSensitivity() const
210 {
211 Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive;
212 switch (m_type) {
213 case ExpressionType::RegExp:
214 sensitivity = m_regExp.caseSensitivity();
215 break;
216 #if QT_CONFIG(regularexpression)
217 case ExpressionType::RegularExpression:
218 {
219 QRegularExpression::PatternOptions options = m_regularExpression.patternOptions();
220 if (!(options & QRegularExpression::CaseInsensitiveOption))
221 sensitivity = Qt::CaseSensitive;
222 }
223 break;
224 #endif
225 }
226 return sensitivity;
227 }
228
setCaseSensitivity(Qt::CaseSensitivity cs)229 void setCaseSensitivity(Qt::CaseSensitivity cs)
230 {
231 switch (m_type) {
232 case ExpressionType::RegExp:
233 m_regExp.setCaseSensitivity(cs);
234 break;
235 #if QT_CONFIG(regularexpression)
236 case ExpressionType::RegularExpression:
237 {
238 QRegularExpression::PatternOptions options = m_regularExpression.patternOptions();
239 options.setFlag(QRegularExpression::CaseInsensitiveOption, cs == Qt::CaseInsensitive);
240 m_regularExpression.setPatternOptions(options);
241 }
242 break;
243 #endif
244 }
245 }
246
hasMatch(const QString & str) const247 bool hasMatch(const QString &str) const
248 {
249 bool result = false;
250 switch (m_type) {
251 case ExpressionType::RegExp:
252 result = str.contains(m_regExp);
253 break;
254 #if QT_CONFIG(regularexpression)
255 case ExpressionType::RegularExpression:
256 result = str.contains(m_regularExpression);
257 break;
258 #endif
259 }
260 return result;
261 }
262
263 private:
264 ExpressionType m_type;
265 QRegExp m_regExp;
266 #if QT_CONFIG(regularexpression)
267 QRegularExpression m_regularExpression;
268 #endif
269 };
270
271
272 class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate
273 {
274 Q_DECLARE_PUBLIC(QSortFilterProxyModel)
275
276 public:
277 struct Mapping {
278 QVector<int> source_rows;
279 QVector<int> source_columns;
280 QVector<int> proxy_rows;
281 QVector<int> proxy_columns;
282 QVector<QModelIndex> mapped_children;
283 QHash<QModelIndex, Mapping *>::const_iterator map_iter;
284 };
285
286 mutable QHash<QModelIndex, Mapping*> source_index_mapping;
287
288 int source_sort_column;
289 int proxy_sort_column;
290 Qt::SortOrder sort_order;
291 Qt::CaseSensitivity sort_casesensitivity;
292 int sort_role;
293 bool sort_localeaware;
294
295 int filter_column;
296 int filter_role;
297 RegularExpressionData filter_data;
298 QModelIndex last_top_source;
299
300 bool filter_recursive;
301 bool complete_insert;
302 bool dynamic_sortfilter;
303 QRowsRemoval itemsBeingRemoved;
304
305 QModelIndexPairList saved_persistent_indexes;
306 QList<QPersistentModelIndex> saved_layoutChange_parents;
307
308 QHash<QModelIndex, Mapping *>::const_iterator create_mapping(
309 const QModelIndex &source_parent) const;
310 QHash<QModelIndex, Mapping *>::const_iterator create_mapping_recursive(
311 const QModelIndex &source_parent) const;
312 QModelIndex proxy_to_source(const QModelIndex &proxyIndex) const;
313 QModelIndex source_to_proxy(const QModelIndex &sourceIndex) const;
314 bool can_create_mapping(const QModelIndex &source_parent) const;
315
316 void remove_from_mapping(const QModelIndex &source_parent);
317
index_to_iterator(const QModelIndex & proxy_index) const318 inline QHash<QModelIndex, Mapping *>::const_iterator index_to_iterator(
319 const QModelIndex &proxy_index) const
320 {
321 Q_ASSERT(proxy_index.isValid());
322 Q_ASSERT(proxy_index.model() == q_func());
323 const void *p = proxy_index.internalPointer();
324 Q_ASSERT(p);
325 QHash<QModelIndex, Mapping *>::const_iterator it =
326 static_cast<const Mapping*>(p)->map_iter;
327 Q_ASSERT(it != source_index_mapping.constEnd());
328 Q_ASSERT(it.value());
329 return it;
330 }
331
create_index(int row,int column,QHash<QModelIndex,Mapping * >::const_iterator it) const332 inline QModelIndex create_index(int row, int column,
333 QHash<QModelIndex, Mapping*>::const_iterator it) const
334 {
335 return q_func()->createIndex(row, column, *it);
336 }
337
338 void _q_sourceDataChanged(const QModelIndex &source_top_left,
339 const QModelIndex &source_bottom_right,
340 const QVector<int> &roles);
341 void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int start, int end);
342
343 void _q_sourceAboutToBeReset();
344 void _q_sourceReset();
345
346 void _q_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint);
347 void _q_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint);
348
349 void _q_sourceRowsAboutToBeInserted(const QModelIndex &source_parent,
350 int start, int end);
351 void _q_sourceRowsInserted(const QModelIndex &source_parent,
352 int start, int end);
353 void _q_sourceRowsAboutToBeRemoved(const QModelIndex &source_parent,
354 int start, int end);
355 void _q_sourceRowsRemoved(const QModelIndex &source_parent,
356 int start, int end);
357 void _q_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent,
358 int sourceStart, int sourceEnd,
359 const QModelIndex &destParent, int dest);
360 void _q_sourceRowsMoved(const QModelIndex &sourceParent,
361 int sourceStart, int sourceEnd,
362 const QModelIndex &destParent, int dest);
363 void _q_sourceColumnsAboutToBeInserted(const QModelIndex &source_parent,
364 int start, int end);
365 void _q_sourceColumnsInserted(const QModelIndex &source_parent,
366 int start, int end);
367 void _q_sourceColumnsAboutToBeRemoved(const QModelIndex &source_parent,
368 int start, int end);
369 void _q_sourceColumnsRemoved(const QModelIndex &source_parent,
370 int start, int end);
371 void _q_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent,
372 int sourceStart, int sourceEnd,
373 const QModelIndex &destParent, int dest);
374 void _q_sourceColumnsMoved(const QModelIndex &sourceParent,
375 int sourceStart, int sourceEnd,
376 const QModelIndex &destParent, int dest);
377
378 void _q_clearMapping();
379
380 void sort();
381 bool update_source_sort_column();
382 int find_source_sort_column() const;
383 void sort_source_rows(QVector<int> &source_rows,
384 const QModelIndex &source_parent) const;
385 QVector<QPair<int, QVector<int > > > proxy_intervals_for_source_items_to_add(
386 const QVector<int> &proxy_to_source, const QVector<int> &source_items,
387 const QModelIndex &source_parent, Qt::Orientation orient) const;
388 QVector<QPair<int, int > > proxy_intervals_for_source_items(
389 const QVector<int> &source_to_proxy, const QVector<int> &source_items) const;
390 void insert_source_items(
391 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
392 const QVector<int> &source_items, const QModelIndex &source_parent,
393 Qt::Orientation orient, bool emit_signal = true);
394 void remove_source_items(
395 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
396 const QVector<int> &source_items, const QModelIndex &source_parent,
397 Qt::Orientation orient, bool emit_signal = true);
398 void remove_proxy_interval(
399 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
400 int proxy_start, int proxy_end, const QModelIndex &proxy_parent,
401 Qt::Orientation orient, bool emit_signal = true);
402 void build_source_to_proxy_mapping(
403 const QVector<int> &proxy_to_source, QVector<int> &source_to_proxy) const;
404 void source_items_inserted(const QModelIndex &source_parent,
405 int start, int end, Qt::Orientation orient);
406 void source_items_about_to_be_removed(const QModelIndex &source_parent,
407 int start, int end, Qt::Orientation orient);
408 void source_items_removed(const QModelIndex &source_parent,
409 int start, int end, Qt::Orientation orient);
410 void proxy_item_range(
411 const QVector<int> &source_to_proxy, const QVector<int> &source_items,
412 int &proxy_low, int &proxy_high) const;
413
414 QModelIndexPairList store_persistent_indexes() const;
415 void update_persistent_indexes(const QModelIndexPairList &source_indexes);
416
417 void filter_about_to_be_changed(const QModelIndex &source_parent = QModelIndex());
418 void filter_changed(const QModelIndex &source_parent = QModelIndex());
419 QSet<int> handle_filter_changed(
420 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
421 const QModelIndex &source_parent, Qt::Orientation orient);
422
423 void updateChildrenMapping(const QModelIndex &source_parent, Mapping *parent_mapping,
424 Qt::Orientation orient, int start, int end, int delta_item_count, bool remove);
425
426 void _q_sourceModelDestroyed() override;
427
428 bool needsReorder(const QVector<int> &source_rows, const QModelIndex &source_parent) const;
429
430 bool filterAcceptsRowInternal(int source_row, const QModelIndex &source_parent) const;
431 bool filterRecursiveAcceptsRow(int source_row, const QModelIndex &source_parent) const;
432 };
433
434 typedef QHash<QModelIndex, QSortFilterProxyModelPrivate::Mapping *> IndexMap;
435
_q_sourceModelDestroyed()436 void QSortFilterProxyModelPrivate::_q_sourceModelDestroyed()
437 {
438 QAbstractProxyModelPrivate::_q_sourceModelDestroyed();
439 qDeleteAll(source_index_mapping);
440 source_index_mapping.clear();
441 }
442
filterAcceptsRowInternal(int source_row,const QModelIndex & source_parent) const443 bool QSortFilterProxyModelPrivate::filterAcceptsRowInternal(int source_row, const QModelIndex &source_parent) const
444 {
445 Q_Q(const QSortFilterProxyModel);
446 return filter_recursive
447 ? filterRecursiveAcceptsRow(source_row, source_parent)
448 : q->filterAcceptsRow(source_row, source_parent);
449 }
450
filterRecursiveAcceptsRow(int source_row,const QModelIndex & source_parent) const451 bool QSortFilterProxyModelPrivate::filterRecursiveAcceptsRow(int source_row, const QModelIndex &source_parent) const
452 {
453 Q_Q(const QSortFilterProxyModel);
454
455 if (q->filterAcceptsRow(source_row, source_parent))
456 return true;
457
458 const QModelIndex index = model->index(source_row, 0, source_parent);
459 const int count = model->rowCount(index);
460
461 for (int i = 0; i < count; ++i) {
462 if (filterRecursiveAcceptsRow(i, index))
463 return true;
464 }
465
466 return false;
467 }
468
remove_from_mapping(const QModelIndex & source_parent)469 void QSortFilterProxyModelPrivate::remove_from_mapping(const QModelIndex &source_parent)
470 {
471 if (Mapping *m = source_index_mapping.take(source_parent)) {
472 for (const QModelIndex &mappedIdx : qAsConst(m->mapped_children))
473 remove_from_mapping(mappedIdx);
474 delete m;
475 }
476 }
477
_q_clearMapping()478 void QSortFilterProxyModelPrivate::_q_clearMapping()
479 {
480 // store the persistent indexes
481 QModelIndexPairList source_indexes = store_persistent_indexes();
482
483 qDeleteAll(source_index_mapping);
484 source_index_mapping.clear();
485 if (dynamic_sortfilter)
486 source_sort_column = find_source_sort_column();
487
488 // update the persistent indexes
489 update_persistent_indexes(source_indexes);
490 }
491
create_mapping(const QModelIndex & source_parent) const492 IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping(
493 const QModelIndex &source_parent) const
494 {
495 Q_Q(const QSortFilterProxyModel);
496
497 IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
498 if (it != source_index_mapping.constEnd()) // was mapped already
499 return it;
500
501 Mapping *m = new Mapping;
502
503 int source_rows = model->rowCount(source_parent);
504 m->source_rows.reserve(source_rows);
505 for (int i = 0; i < source_rows; ++i) {
506 if (filterAcceptsRowInternal(i, source_parent))
507 m->source_rows.append(i);
508 }
509 int source_cols = model->columnCount(source_parent);
510 m->source_columns.reserve(source_cols);
511 for (int i = 0; i < source_cols; ++i) {
512 if (q->filterAcceptsColumn(i, source_parent))
513 m->source_columns.append(i);
514 }
515
516 sort_source_rows(m->source_rows, source_parent);
517 m->proxy_rows.resize(source_rows);
518 build_source_to_proxy_mapping(m->source_rows, m->proxy_rows);
519 m->proxy_columns.resize(source_cols);
520 build_source_to_proxy_mapping(m->source_columns, m->proxy_columns);
521
522 it = IndexMap::const_iterator(source_index_mapping.insert(source_parent, m));
523 m->map_iter = it;
524
525 if (source_parent.isValid()) {
526 QModelIndex source_grand_parent = source_parent.parent();
527 IndexMap::const_iterator it2 = create_mapping(source_grand_parent);
528 Q_ASSERT(it2 != source_index_mapping.constEnd());
529 it2.value()->mapped_children.append(source_parent);
530 }
531
532 Q_ASSERT(it != source_index_mapping.constEnd());
533 Q_ASSERT(it.value());
534
535 return it;
536 }
537
538 // Go up the tree, creating mappings, unless of course the parent is filtered out
create_mapping_recursive(const QModelIndex & source_parent) const539 IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping_recursive(const QModelIndex &source_parent) const
540 {
541 if (source_parent.isValid()) {
542 const QModelIndex source_grand_parent = source_parent.parent();
543 IndexMap::const_iterator it = source_index_mapping.constFind(source_grand_parent);
544 IndexMap::const_iterator end = source_index_mapping.constEnd();
545 if (it == end) {
546 it = create_mapping_recursive(source_grand_parent);
547 end = source_index_mapping.constEnd();
548 if (it == end)
549 return end;
550 }
551 Mapping *gm = it.value();
552 if (gm->proxy_rows.at(source_parent.row()) == -1 ||
553 gm->proxy_columns.at(source_parent.column()) == -1) {
554 // Can't do, parent is filtered
555 return end;
556 }
557 }
558 return create_mapping(source_parent);
559 }
560
proxy_to_source(const QModelIndex & proxy_index) const561 QModelIndex QSortFilterProxyModelPrivate::proxy_to_source(const QModelIndex &proxy_index) const
562 {
563 if (!proxy_index.isValid())
564 return QModelIndex(); // for now; we may want to be able to set a root index later
565 if (proxy_index.model() != q_func()) {
566 qWarning("QSortFilterProxyModel: index from wrong model passed to mapToSource");
567 Q_ASSERT(!"QSortFilterProxyModel: index from wrong model passed to mapToSource");
568 return QModelIndex();
569 }
570 IndexMap::const_iterator it = index_to_iterator(proxy_index);
571 Mapping *m = it.value();
572 if ((proxy_index.row() >= m->source_rows.size()) || (proxy_index.column() >= m->source_columns.size()))
573 return QModelIndex();
574 int source_row = m->source_rows.at(proxy_index.row());
575 int source_col = m->source_columns.at(proxy_index.column());
576 return model->index(source_row, source_col, it.key());
577 }
578
source_to_proxy(const QModelIndex & source_index) const579 QModelIndex QSortFilterProxyModelPrivate::source_to_proxy(const QModelIndex &source_index) const
580 {
581 if (!source_index.isValid())
582 return QModelIndex(); // for now; we may want to be able to set a root index later
583 if (source_index.model() != model) {
584 qWarning("QSortFilterProxyModel: index from wrong model passed to mapFromSource");
585 Q_ASSERT(!"QSortFilterProxyModel: index from wrong model passed to mapFromSource");
586 return QModelIndex();
587 }
588 QModelIndex source_parent = source_index.parent();
589 IndexMap::const_iterator it = create_mapping(source_parent);
590 Mapping *m = it.value();
591 if ((source_index.row() >= m->proxy_rows.size()) || (source_index.column() >= m->proxy_columns.size()))
592 return QModelIndex();
593 int proxy_row = m->proxy_rows.at(source_index.row());
594 int proxy_column = m->proxy_columns.at(source_index.column());
595 if (proxy_row == -1 || proxy_column == -1)
596 return QModelIndex();
597 return create_index(proxy_row, proxy_column, it);
598 }
599
can_create_mapping(const QModelIndex & source_parent) const600 bool QSortFilterProxyModelPrivate::can_create_mapping(const QModelIndex &source_parent) const
601 {
602 if (source_parent.isValid()) {
603 QModelIndex source_grand_parent = source_parent.parent();
604 IndexMap::const_iterator it = source_index_mapping.constFind(source_grand_parent);
605 if (it == source_index_mapping.constEnd()) {
606 // Don't care, since we don't have mapping for the grand parent
607 return false;
608 }
609 Mapping *gm = it.value();
610 if (gm->proxy_rows.at(source_parent.row()) == -1 ||
611 gm->proxy_columns.at(source_parent.column()) == -1) {
612 // Don't care, since parent is filtered
613 return false;
614 }
615 }
616 return true;
617 }
618
619 /*!
620 \internal
621
622 Sorts the existing mappings.
623 */
sort()624 void QSortFilterProxyModelPrivate::sort()
625 {
626 Q_Q(QSortFilterProxyModel);
627 emit q->layoutAboutToBeChanged(QList<QPersistentModelIndex>(), QAbstractItemModel::VerticalSortHint);
628 QModelIndexPairList source_indexes = store_persistent_indexes();
629 const auto end = source_index_mapping.constEnd();
630 for (auto it = source_index_mapping.constBegin(); it != end; ++it) {
631 const QModelIndex &source_parent = it.key();
632 Mapping *m = it.value();
633 sort_source_rows(m->source_rows, source_parent);
634 build_source_to_proxy_mapping(m->source_rows, m->proxy_rows);
635 }
636 update_persistent_indexes(source_indexes);
637 emit q->layoutChanged(QList<QPersistentModelIndex>(), QAbstractItemModel::VerticalSortHint);
638 }
639
640 /*!
641 \internal
642
643 update the source_sort_column according to the proxy_sort_column
644 return true if the column was changed
645 */
update_source_sort_column()646 bool QSortFilterProxyModelPrivate::update_source_sort_column()
647 {
648 int old_source_sort_column = source_sort_column;
649
650 if (proxy_sort_column == -1) {
651 source_sort_column = -1;
652 } else {
653 // We cannot use index mapping here because in case of a still-empty
654 // proxy model there's no valid proxy index we could map to source.
655 // So always use the root mapping directly instead.
656 Mapping *m = create_mapping(QModelIndex()).value();
657 if (proxy_sort_column < m->source_columns.size())
658 source_sort_column = m->source_columns.at(proxy_sort_column);
659 else
660 source_sort_column = -1;
661 }
662
663 return old_source_sort_column != source_sort_column;
664 }
665
666 /*!
667 \internal
668
669 Find the source_sort_column without creating a full mapping and
670 without updating anything.
671 */
find_source_sort_column() const672 int QSortFilterProxyModelPrivate::find_source_sort_column() const
673 {
674 if (proxy_sort_column == -1)
675 return -1;
676
677 const QModelIndex rootIndex;
678 const int source_cols = model->columnCount();
679 int accepted_columns = -1;
680
681 Q_Q(const QSortFilterProxyModel);
682 for (int i = 0; i < source_cols; ++i) {
683 if (q->filterAcceptsColumn(i, rootIndex)) {
684 if (++accepted_columns == proxy_sort_column)
685 return i;
686 }
687 }
688
689 return -1;
690 }
691
692 /*!
693 \internal
694
695 Sorts the given \a source_rows according to current sort column and order.
696 */
sort_source_rows(QVector<int> & source_rows,const QModelIndex & source_parent) const697 void QSortFilterProxyModelPrivate::sort_source_rows(
698 QVector<int> &source_rows, const QModelIndex &source_parent) const
699 {
700 Q_Q(const QSortFilterProxyModel);
701 if (source_sort_column >= 0) {
702 if (sort_order == Qt::AscendingOrder) {
703 QSortFilterProxyModelLessThan lt(source_sort_column, source_parent, model, q);
704 std::stable_sort(source_rows.begin(), source_rows.end(), lt);
705 } else {
706 QSortFilterProxyModelGreaterThan gt(source_sort_column, source_parent, model, q);
707 std::stable_sort(source_rows.begin(), source_rows.end(), gt);
708 }
709 } else { // restore the source model order
710 std::stable_sort(source_rows.begin(), source_rows.end());
711 }
712 }
713
714 /*!
715 \internal
716
717 Given source-to-proxy mapping \a source_to_proxy and the set of
718 source items \a source_items (which are part of that mapping),
719 determines the corresponding proxy item intervals that should
720 be removed from the proxy model.
721
722 The result is a vector of pairs, where each pair represents a
723 (start, end) tuple, sorted in ascending order.
724 */
proxy_intervals_for_source_items(const QVector<int> & source_to_proxy,const QVector<int> & source_items) const725 QVector<QPair<int, int > > QSortFilterProxyModelPrivate::proxy_intervals_for_source_items(
726 const QVector<int> &source_to_proxy, const QVector<int> &source_items) const
727 {
728 QVector<QPair<int, int> > proxy_intervals;
729 if (source_items.isEmpty())
730 return proxy_intervals;
731
732 int source_items_index = 0;
733 while (source_items_index < source_items.size()) {
734 int first_proxy_item = source_to_proxy.at(source_items.at(source_items_index));
735 Q_ASSERT(first_proxy_item != -1);
736 int last_proxy_item = first_proxy_item;
737 ++source_items_index;
738 // Find end of interval
739 while ((source_items_index < source_items.size())
740 && (source_to_proxy.at(source_items.at(source_items_index)) == last_proxy_item + 1)) {
741 ++last_proxy_item;
742 ++source_items_index;
743 }
744 // Add interval to result
745 proxy_intervals.append(QPair<int, int>(first_proxy_item, last_proxy_item));
746 }
747 std::stable_sort(proxy_intervals.begin(), proxy_intervals.end());
748 // Consolidate adjacent intervals
749 for (int i = proxy_intervals.size()-1; i > 0; --i) {
750 QPair<int, int> &interval = proxy_intervals[i];
751 QPair<int, int> &preceeding_interval = proxy_intervals[i - 1];
752 if (interval.first == preceeding_interval.second + 1) {
753 preceeding_interval.second = interval.second;
754 interval.first = interval.second = -1;
755 }
756 }
757 proxy_intervals.erase(
758 std::remove_if(proxy_intervals.begin(), proxy_intervals.end(),
759 [](QPair<int, int> &interval) { return interval.first < 0; }),
760 proxy_intervals.end());
761 return proxy_intervals;
762 }
763
764 /*!
765 \internal
766
767 Given source-to-proxy mapping \a src_to_proxy and proxy-to-source mapping
768 \a proxy_to_source, removes \a source_items from this proxy model.
769 The corresponding proxy items are removed in intervals, so that the proper
770 rows/columnsRemoved(start, end) signals will be generated.
771 */
remove_source_items(QVector<int> & source_to_proxy,QVector<int> & proxy_to_source,const QVector<int> & source_items,const QModelIndex & source_parent,Qt::Orientation orient,bool emit_signal)772 void QSortFilterProxyModelPrivate::remove_source_items(
773 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
774 const QVector<int> &source_items, const QModelIndex &source_parent,
775 Qt::Orientation orient, bool emit_signal)
776 {
777 Q_Q(QSortFilterProxyModel);
778 QModelIndex proxy_parent = q->mapFromSource(source_parent);
779 if (!proxy_parent.isValid() && source_parent.isValid()) {
780 proxy_to_source.clear();
781 return; // nothing to do (already removed)
782 }
783
784 const auto proxy_intervals = proxy_intervals_for_source_items(
785 source_to_proxy, source_items);
786
787 const auto end = proxy_intervals.rend();
788 for (auto it = proxy_intervals.rbegin(); it != end; ++it) {
789 const QPair<int, int> &interval = *it;
790 const int proxy_start = interval.first;
791 const int proxy_end = interval.second;
792 remove_proxy_interval(source_to_proxy, proxy_to_source, proxy_start, proxy_end,
793 proxy_parent, orient, emit_signal);
794 }
795 }
796
797 /*!
798 \internal
799
800 Given source-to-proxy mapping \a source_to_proxy and proxy-to-source mapping
801 \a proxy_to_source, removes items from \a proxy_start to \a proxy_end
802 (inclusive) from this proxy model.
803 */
remove_proxy_interval(QVector<int> & source_to_proxy,QVector<int> & proxy_to_source,int proxy_start,int proxy_end,const QModelIndex & proxy_parent,Qt::Orientation orient,bool emit_signal)804 void QSortFilterProxyModelPrivate::remove_proxy_interval(
805 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source, int proxy_start, int proxy_end,
806 const QModelIndex &proxy_parent, Qt::Orientation orient, bool emit_signal)
807 {
808 Q_Q(QSortFilterProxyModel);
809 if (emit_signal) {
810 if (orient == Qt::Vertical)
811 q->beginRemoveRows(proxy_parent, proxy_start, proxy_end);
812 else
813 q->beginRemoveColumns(proxy_parent, proxy_start, proxy_end);
814 }
815
816 // Remove items from proxy-to-source mapping
817 proxy_to_source.remove(proxy_start, proxy_end - proxy_start + 1);
818
819 build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
820
821 if (emit_signal) {
822 if (orient == Qt::Vertical)
823 q->endRemoveRows();
824 else
825 q->endRemoveColumns();
826 }
827 }
828
829 /*!
830 \internal
831
832 Given proxy-to-source mapping \a proxy_to_source and a set of
833 unmapped source items \a source_items, determines the proxy item
834 intervals at which the subsets of source items should be inserted
835 (but does not actually add them to the mapping).
836
837 The result is a vector of pairs, each pair representing a tuple (start,
838 items), where items is a vector containing the (sorted) source items that
839 should be inserted at that proxy model location.
840 */
proxy_intervals_for_source_items_to_add(const QVector<int> & proxy_to_source,const QVector<int> & source_items,const QModelIndex & source_parent,Qt::Orientation orient) const841 QVector<QPair<int, QVector<int > > > QSortFilterProxyModelPrivate::proxy_intervals_for_source_items_to_add(
842 const QVector<int> &proxy_to_source, const QVector<int> &source_items,
843 const QModelIndex &source_parent, Qt::Orientation orient) const
844 {
845 Q_Q(const QSortFilterProxyModel);
846 QVector<QPair<int, QVector<int> > > proxy_intervals;
847 if (source_items.isEmpty())
848 return proxy_intervals;
849
850 int proxy_low = 0;
851 int proxy_item = 0;
852 int source_items_index = 0;
853 QVector<int> source_items_in_interval;
854 bool compare = (orient == Qt::Vertical && source_sort_column >= 0 && dynamic_sortfilter);
855 while (source_items_index < source_items.size()) {
856 source_items_in_interval.clear();
857 int first_new_source_item = source_items.at(source_items_index);
858 source_items_in_interval.append(first_new_source_item);
859 ++source_items_index;
860
861 // Find proxy item at which insertion should be started
862 int proxy_high = proxy_to_source.size() - 1;
863 QModelIndex i1 = compare ? model->index(first_new_source_item, source_sort_column, source_parent) : QModelIndex();
864 while (proxy_low <= proxy_high) {
865 proxy_item = (proxy_low + proxy_high) / 2;
866 if (compare) {
867 QModelIndex i2 = model->index(proxy_to_source.at(proxy_item), source_sort_column, source_parent);
868 if ((sort_order == Qt::AscendingOrder) ? q->lessThan(i1, i2) : q->lessThan(i2, i1))
869 proxy_high = proxy_item - 1;
870 else
871 proxy_low = proxy_item + 1;
872 } else {
873 if (first_new_source_item < proxy_to_source.at(proxy_item))
874 proxy_high = proxy_item - 1;
875 else
876 proxy_low = proxy_item + 1;
877 }
878 }
879 proxy_item = proxy_low;
880
881 // Find the sequence of new source items that should be inserted here
882 if (proxy_item >= proxy_to_source.size()) {
883 for ( ; source_items_index < source_items.size(); ++source_items_index)
884 source_items_in_interval.append(source_items.at(source_items_index));
885 } else {
886 i1 = compare ? model->index(proxy_to_source.at(proxy_item), source_sort_column, source_parent) : QModelIndex();
887 for ( ; source_items_index < source_items.size(); ++source_items_index) {
888 int new_source_item = source_items.at(source_items_index);
889 if (compare) {
890 QModelIndex i2 = model->index(new_source_item, source_sort_column, source_parent);
891 if ((sort_order == Qt::AscendingOrder) ? q->lessThan(i1, i2) : q->lessThan(i2, i1))
892 break;
893 } else {
894 if (proxy_to_source.at(proxy_item) < new_source_item)
895 break;
896 }
897 source_items_in_interval.append(new_source_item);
898 }
899 }
900
901 // Add interval to result
902 proxy_intervals.append(QPair<int, QVector<int> >(proxy_item, source_items_in_interval));
903 }
904 return proxy_intervals;
905 }
906
907 /*!
908 \internal
909
910 Given source-to-proxy mapping \a source_to_proxy and proxy-to-source mapping
911 \a proxy_to_source, inserts the given \a source_items into this proxy model.
912 The source items are inserted in intervals (based on some sorted order), so
913 that the proper rows/columnsInserted(start, end) signals will be generated.
914 */
insert_source_items(QVector<int> & source_to_proxy,QVector<int> & proxy_to_source,const QVector<int> & source_items,const QModelIndex & source_parent,Qt::Orientation orient,bool emit_signal)915 void QSortFilterProxyModelPrivate::insert_source_items(
916 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
917 const QVector<int> &source_items, const QModelIndex &source_parent,
918 Qt::Orientation orient, bool emit_signal)
919 {
920 Q_Q(QSortFilterProxyModel);
921 QModelIndex proxy_parent = q->mapFromSource(source_parent);
922 if (!proxy_parent.isValid() && source_parent.isValid())
923 return; // nothing to do (source_parent is not mapped)
924
925 const auto proxy_intervals = proxy_intervals_for_source_items_to_add(
926 proxy_to_source, source_items, source_parent, orient);
927
928 const auto end = proxy_intervals.rend();
929 for (auto it = proxy_intervals.rbegin(); it != end; ++it) {
930 const QPair<int, QVector<int> > &interval = *it;
931 const int proxy_start = interval.first;
932 const QVector<int> &source_items = interval.second;
933 const int proxy_end = proxy_start + source_items.size() - 1;
934
935 if (emit_signal) {
936 if (orient == Qt::Vertical)
937 q->beginInsertRows(proxy_parent, proxy_start, proxy_end);
938 else
939 q->beginInsertColumns(proxy_parent, proxy_start, proxy_end);
940 }
941
942 // TODO: use the range QList::insert() overload once it is implemented (QTBUG-58633).
943 proxy_to_source.insert(proxy_start, source_items.size(), 0);
944 std::copy(source_items.cbegin(), source_items.cend(), proxy_to_source.begin() + proxy_start);
945
946 build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
947
948 if (emit_signal) {
949 if (orient == Qt::Vertical)
950 q->endInsertRows();
951 else
952 q->endInsertColumns();
953 }
954 }
955 }
956
957 /*!
958 \internal
959
960 Handles source model items insertion (columnsInserted(), rowsInserted()).
961 Determines
962 1) which of the inserted items to also insert into proxy model (filtering),
963 2) where to insert the items into the proxy model (sorting),
964 then inserts those items.
965 The items are inserted into the proxy model in intervals (based on
966 sorted order), so that the proper rows/columnsInserted(start, end)
967 signals will be generated.
968 */
source_items_inserted(const QModelIndex & source_parent,int start,int end,Qt::Orientation orient)969 void QSortFilterProxyModelPrivate::source_items_inserted(
970 const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
971 {
972 Q_Q(QSortFilterProxyModel);
973 if ((start < 0) || (end < 0))
974 return;
975 IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
976 if (it == source_index_mapping.constEnd()) {
977 if (!can_create_mapping(source_parent))
978 return;
979 it = create_mapping(source_parent);
980 Mapping *m = it.value();
981 QModelIndex proxy_parent = q->mapFromSource(source_parent);
982 if (m->source_rows.count() > 0) {
983 q->beginInsertRows(proxy_parent, 0, m->source_rows.count() - 1);
984 q->endInsertRows();
985 }
986 if (m->source_columns.count() > 0) {
987 q->beginInsertColumns(proxy_parent, 0, m->source_columns.count() - 1);
988 q->endInsertColumns();
989 }
990 return;
991 }
992
993 Mapping *m = it.value();
994 QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
995 QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
996
997 int delta_item_count = end - start + 1;
998 int old_item_count = source_to_proxy.size();
999
1000 updateChildrenMapping(source_parent, m, orient, start, end, delta_item_count, false);
1001
1002 // Expand source-to-proxy mapping to account for new items
1003 if (start < 0 || start > source_to_proxy.size()) {
1004 qWarning("QSortFilterProxyModel: invalid inserted rows reported by source model");
1005 remove_from_mapping(source_parent);
1006 return;
1007 }
1008 source_to_proxy.insert(start, delta_item_count, -1);
1009
1010 if (start < old_item_count) {
1011 // Adjust existing "stale" indexes in proxy-to-source mapping
1012 int proxy_count = proxy_to_source.size();
1013 for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
1014 int source_item = proxy_to_source.at(proxy_item);
1015 if (source_item >= start)
1016 proxy_to_source.replace(proxy_item, source_item + delta_item_count);
1017 }
1018 build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
1019 }
1020
1021 // Figure out which items to add to mapping based on filter
1022 QVector<int> source_items;
1023 for (int i = start; i <= end; ++i) {
1024 if ((orient == Qt::Vertical)
1025 ? filterAcceptsRowInternal(i, source_parent)
1026 : q->filterAcceptsColumn(i, source_parent)) {
1027 source_items.append(i);
1028 }
1029 }
1030
1031 if (model->rowCount(source_parent) == delta_item_count) {
1032 // Items were inserted where there were none before.
1033 // If it was new rows make sure to create mappings for columns so that a
1034 // valid mapping can be retrieved later and vice-versa.
1035
1036 QVector<int> &orthogonal_proxy_to_source = (orient == Qt::Horizontal) ? m->source_rows : m->source_columns;
1037 QVector<int> &orthogonal_source_to_proxy = (orient == Qt::Horizontal) ? m->proxy_rows : m->proxy_columns;
1038
1039 if (orthogonal_source_to_proxy.isEmpty()) {
1040 const int ortho_end = (orient == Qt::Horizontal) ? model->rowCount(source_parent) : model->columnCount(source_parent);
1041
1042 orthogonal_source_to_proxy.resize(ortho_end);
1043
1044 for (int ortho_item = 0; ortho_item < ortho_end; ++ortho_item) {
1045 if ((orient == Qt::Horizontal) ? filterAcceptsRowInternal(ortho_item, source_parent)
1046 : q->filterAcceptsColumn(ortho_item, source_parent)) {
1047 orthogonal_proxy_to_source.append(ortho_item);
1048 }
1049 }
1050 if (orient == Qt::Horizontal) {
1051 // We're reacting to columnsInserted, but we've just inserted new rows. Sort them.
1052 sort_source_rows(orthogonal_proxy_to_source, source_parent);
1053 }
1054 build_source_to_proxy_mapping(orthogonal_proxy_to_source, orthogonal_source_to_proxy);
1055 }
1056 }
1057
1058 // Sort and insert the items
1059 if (orient == Qt::Vertical) // Only sort rows
1060 sort_source_rows(source_items, source_parent);
1061 insert_source_items(source_to_proxy, proxy_to_source, source_items, source_parent, orient);
1062 }
1063
1064 /*!
1065 \internal
1066
1067 Handles source model items removal
1068 (columnsAboutToBeRemoved(), rowsAboutToBeRemoved()).
1069 */
source_items_about_to_be_removed(const QModelIndex & source_parent,int start,int end,Qt::Orientation orient)1070 void QSortFilterProxyModelPrivate::source_items_about_to_be_removed(
1071 const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
1072 {
1073 if ((start < 0) || (end < 0))
1074 return;
1075 IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
1076 if (it == source_index_mapping.constEnd()) {
1077 // Don't care, since we don't have mapping for this index
1078 return;
1079 }
1080
1081 Mapping *m = it.value();
1082 QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
1083 QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
1084
1085 // figure out which items to remove
1086 QVector<int> source_items_to_remove;
1087 int proxy_count = proxy_to_source.size();
1088 for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
1089 int source_item = proxy_to_source.at(proxy_item);
1090 if ((source_item >= start) && (source_item <= end))
1091 source_items_to_remove.append(source_item);
1092 }
1093
1094 remove_source_items(source_to_proxy, proxy_to_source, source_items_to_remove,
1095 source_parent, orient);
1096 }
1097
1098 /*!
1099 \internal
1100
1101 Handles source model items removal (columnsRemoved(), rowsRemoved()).
1102 */
source_items_removed(const QModelIndex & source_parent,int start,int end,Qt::Orientation orient)1103 void QSortFilterProxyModelPrivate::source_items_removed(
1104 const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
1105 {
1106 if ((start < 0) || (end < 0))
1107 return;
1108 IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
1109 if (it == source_index_mapping.constEnd()) {
1110 // Don't care, since we don't have mapping for this index
1111 return;
1112 }
1113
1114 Mapping *m = it.value();
1115 QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
1116 QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
1117
1118 if (end >= source_to_proxy.size())
1119 end = source_to_proxy.size() - 1;
1120
1121 // Shrink the source-to-proxy mapping to reflect the new item count
1122 int delta_item_count = end - start + 1;
1123 source_to_proxy.remove(start, delta_item_count);
1124
1125 int proxy_count = proxy_to_source.size();
1126 if (proxy_count > source_to_proxy.size()) {
1127 // mapping is in an inconsistent state -- redo the whole mapping
1128 qWarning("QSortFilterProxyModel: inconsistent changes reported by source model");
1129 Q_Q(QSortFilterProxyModel);
1130 q->beginResetModel();
1131 remove_from_mapping(source_parent);
1132 q->endResetModel();
1133 return;
1134 }
1135
1136 // Adjust "stale" indexes in proxy-to-source mapping
1137 for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
1138 int source_item = proxy_to_source.at(proxy_item);
1139 if (source_item >= start) {
1140 Q_ASSERT(source_item - delta_item_count >= 0);
1141 proxy_to_source.replace(proxy_item, source_item - delta_item_count);
1142 }
1143 }
1144 build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
1145
1146 updateChildrenMapping(source_parent, m, orient, start, end, delta_item_count, true);
1147
1148 }
1149
1150
1151 /*!
1152 \internal
1153 updates the mapping of the children when inserting or removing items
1154 */
updateChildrenMapping(const QModelIndex & source_parent,Mapping * parent_mapping,Qt::Orientation orient,int start,int end,int delta_item_count,bool remove)1155 void QSortFilterProxyModelPrivate::updateChildrenMapping(const QModelIndex &source_parent, Mapping *parent_mapping,
1156 Qt::Orientation orient, int start, int end, int delta_item_count, bool remove)
1157 {
1158 // see if any mapped children should be (re)moved
1159 QVector<QPair<QModelIndex, Mapping*> > moved_source_index_mappings;
1160 QVector<QModelIndex>::iterator it2 = parent_mapping->mapped_children.begin();
1161 for ( ; it2 != parent_mapping->mapped_children.end();) {
1162 const QModelIndex source_child_index = *it2;
1163 const int pos = (orient == Qt::Vertical)
1164 ? source_child_index.row()
1165 : source_child_index.column();
1166 if (pos < start) {
1167 // not affected
1168 ++it2;
1169 } else if (remove && pos <= end) {
1170 // in the removed interval
1171 it2 = parent_mapping->mapped_children.erase(it2);
1172 remove_from_mapping(source_child_index);
1173 } else {
1174 // below the removed items -- recompute the index
1175 QModelIndex new_index;
1176 const int newpos = remove ? pos - delta_item_count : pos + delta_item_count;
1177 if (orient == Qt::Vertical) {
1178 new_index = model->index(newpos,
1179 source_child_index.column(),
1180 source_parent);
1181 } else {
1182 new_index = model->index(source_child_index.row(),
1183 newpos,
1184 source_parent);
1185 }
1186 *it2 = new_index;
1187 ++it2;
1188
1189 // update mapping
1190 Mapping *cm = source_index_mapping.take(source_child_index);
1191 Q_ASSERT(cm);
1192 // we do not reinsert right away, because the new index might be identical with another, old index
1193 moved_source_index_mappings.append(QPair<QModelIndex, Mapping*>(new_index, cm));
1194 }
1195 }
1196
1197 // reinsert moved, mapped indexes
1198 QVector<QPair<QModelIndex, Mapping*> >::iterator it = moved_source_index_mappings.begin();
1199 for (; it != moved_source_index_mappings.end(); ++it) {
1200 (*it).second->map_iter = QHash<QModelIndex, Mapping *>::const_iterator(source_index_mapping.insert((*it).first, (*it).second));
1201 }
1202 }
1203
1204 /*!
1205 \internal
1206 */
proxy_item_range(const QVector<int> & source_to_proxy,const QVector<int> & source_items,int & proxy_low,int & proxy_high) const1207 void QSortFilterProxyModelPrivate::proxy_item_range(
1208 const QVector<int> &source_to_proxy, const QVector<int> &source_items,
1209 int &proxy_low, int &proxy_high) const
1210 {
1211 proxy_low = INT_MAX;
1212 proxy_high = INT_MIN;
1213 for (int i = 0; i < source_items.count(); ++i) {
1214 int proxy_item = source_to_proxy.at(source_items.at(i));
1215 Q_ASSERT(proxy_item != -1);
1216 if (proxy_item < proxy_low)
1217 proxy_low = proxy_item;
1218 if (proxy_item > proxy_high)
1219 proxy_high = proxy_item;
1220 }
1221 }
1222
1223 /*!
1224 \internal
1225 */
build_source_to_proxy_mapping(const QVector<int> & proxy_to_source,QVector<int> & source_to_proxy) const1226 void QSortFilterProxyModelPrivate::build_source_to_proxy_mapping(
1227 const QVector<int> &proxy_to_source, QVector<int> &source_to_proxy) const
1228 {
1229 source_to_proxy.fill(-1);
1230 int proxy_count = proxy_to_source.size();
1231 for (int i = 0; i < proxy_count; ++i)
1232 source_to_proxy[proxy_to_source.at(i)] = i;
1233 }
1234
1235 /*!
1236 \internal
1237
1238 Maps the persistent proxy indexes to source indexes and
1239 returns the list of source indexes.
1240 */
store_persistent_indexes() const1241 QModelIndexPairList QSortFilterProxyModelPrivate::store_persistent_indexes() const
1242 {
1243 Q_Q(const QSortFilterProxyModel);
1244 QModelIndexPairList source_indexes;
1245 source_indexes.reserve(persistent.indexes.count());
1246 for (const QPersistentModelIndexData *data : qAsConst(persistent.indexes)) {
1247 const QModelIndex &proxy_index = data->index;
1248 QModelIndex source_index = q->mapToSource(proxy_index);
1249 source_indexes.append(qMakePair(proxy_index, QPersistentModelIndex(source_index)));
1250 }
1251 return source_indexes;
1252 }
1253
1254 /*!
1255 \internal
1256
1257 Maps \a source_indexes to proxy indexes and stores those
1258 as persistent indexes.
1259 */
update_persistent_indexes(const QModelIndexPairList & source_indexes)1260 void QSortFilterProxyModelPrivate::update_persistent_indexes(
1261 const QModelIndexPairList &source_indexes)
1262 {
1263 Q_Q(QSortFilterProxyModel);
1264 QModelIndexList from, to;
1265 const int numSourceIndexes = source_indexes.count();
1266 from.reserve(numSourceIndexes);
1267 to.reserve(numSourceIndexes);
1268 for (const auto &indexPair : source_indexes) {
1269 const QPersistentModelIndex &source_index = indexPair.second;
1270 const QModelIndex &old_proxy_index = indexPair.first;
1271 create_mapping(source_index.parent());
1272 QModelIndex proxy_index = q->mapFromSource(source_index);
1273 from << old_proxy_index;
1274 to << proxy_index;
1275 }
1276 q->changePersistentIndexList(from, to);
1277 }
1278
1279 /*!
1280 \internal
1281
1282 Updates the source_index mapping in case it's invalid and we
1283 need it because we have a valid filter
1284 */
filter_about_to_be_changed(const QModelIndex & source_parent)1285 void QSortFilterProxyModelPrivate::filter_about_to_be_changed(const QModelIndex &source_parent)
1286 {
1287 if (!filter_data.isEmpty() &&
1288 source_index_mapping.constFind(source_parent) == source_index_mapping.constEnd())
1289 create_mapping(source_parent);
1290 }
1291
1292
1293 /*!
1294 \internal
1295
1296 Updates the proxy model (adds/removes rows) based on the
1297 new filter.
1298 */
filter_changed(const QModelIndex & source_parent)1299 void QSortFilterProxyModelPrivate::filter_changed(const QModelIndex &source_parent)
1300 {
1301 IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
1302 if (it == source_index_mapping.constEnd())
1303 return;
1304 Mapping *m = it.value();
1305 QSet<int> rows_removed = handle_filter_changed(m->proxy_rows, m->source_rows, source_parent, Qt::Vertical);
1306 QSet<int> columns_removed = handle_filter_changed(m->proxy_columns, m->source_columns, source_parent, Qt::Horizontal);
1307
1308 // We need to iterate over a copy of m->mapped_children because otherwise it may be changed by other code, invalidating
1309 // the iterator it2.
1310 // The m->mapped_children vector can be appended to with indexes which are no longer filtered
1311 // out (in create_mapping) when this function recurses for child indexes.
1312 const QVector<QModelIndex> mappedChildren = m->mapped_children;
1313 QVector<int> indexesToRemove;
1314 for (int i = 0; i < mappedChildren.size(); ++i) {
1315 const QModelIndex &source_child_index = mappedChildren.at(i);
1316 if (rows_removed.contains(source_child_index.row()) || columns_removed.contains(source_child_index.column())) {
1317 indexesToRemove.push_back(i);
1318 remove_from_mapping(source_child_index);
1319 } else {
1320 filter_changed(source_child_index);
1321 }
1322 }
1323 QVector<int>::const_iterator removeIt = indexesToRemove.constEnd();
1324 const QVector<int>::const_iterator removeBegin = indexesToRemove.constBegin();
1325
1326 // We can't just remove these items from mappedChildren while iterating above and then
1327 // do something like m->mapped_children = mappedChildren, because mapped_children might
1328 // be appended to in create_mapping, and we would lose those new items.
1329 // Because they are always appended in create_mapping, we can still remove them by
1330 // position here.
1331 while (removeIt != removeBegin) {
1332 --removeIt;
1333 m->mapped_children.remove(*removeIt);
1334 }
1335 }
1336
1337 /*!
1338 \internal
1339 returns the removed items indexes
1340 */
handle_filter_changed(QVector<int> & source_to_proxy,QVector<int> & proxy_to_source,const QModelIndex & source_parent,Qt::Orientation orient)1341 QSet<int> QSortFilterProxyModelPrivate::handle_filter_changed(
1342 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
1343 const QModelIndex &source_parent, Qt::Orientation orient)
1344 {
1345 Q_Q(QSortFilterProxyModel);
1346 // Figure out which mapped items to remove
1347 QVector<int> source_items_remove;
1348 for (int i = 0; i < proxy_to_source.count(); ++i) {
1349 const int source_item = proxy_to_source.at(i);
1350 if ((orient == Qt::Vertical)
1351 ? !filterAcceptsRowInternal(source_item, source_parent)
1352 : !q->filterAcceptsColumn(source_item, source_parent)) {
1353 // This source item does not satisfy the filter, so it must be removed
1354 source_items_remove.append(source_item);
1355 }
1356 }
1357 // Figure out which non-mapped items to insert
1358 QVector<int> source_items_insert;
1359 int source_count = source_to_proxy.size();
1360 for (int source_item = 0; source_item < source_count; ++source_item) {
1361 if (source_to_proxy.at(source_item) == -1) {
1362 if ((orient == Qt::Vertical)
1363 ? filterAcceptsRowInternal(source_item, source_parent)
1364 : q->filterAcceptsColumn(source_item, source_parent)) {
1365 // This source item satisfies the filter, so it must be added
1366 source_items_insert.append(source_item);
1367 }
1368 }
1369 }
1370 if (!source_items_remove.isEmpty() || !source_items_insert.isEmpty()) {
1371 // Do item removal and insertion
1372 remove_source_items(source_to_proxy, proxy_to_source,
1373 source_items_remove, source_parent, orient);
1374 if (orient == Qt::Vertical)
1375 sort_source_rows(source_items_insert, source_parent);
1376 insert_source_items(source_to_proxy, proxy_to_source,
1377 source_items_insert, source_parent, orient);
1378 }
1379 return qVectorToSet(source_items_remove);
1380 }
1381
needsReorder(const QVector<int> & source_rows,const QModelIndex & source_parent) const1382 bool QSortFilterProxyModelPrivate::needsReorder(const QVector<int> &source_rows, const QModelIndex &source_parent) const
1383 {
1384 Q_Q(const QSortFilterProxyModel);
1385 Q_ASSERT(source_sort_column != -1);
1386 const int proxyRowCount = q->rowCount(source_to_proxy(source_parent));
1387 // If any modified proxy row no longer passes lessThan(previous, current) or lessThan(current, next) then we need to reorder.
1388 return std::any_of(source_rows.begin(), source_rows.end(),
1389 [this, q, proxyRowCount, source_parent](int sourceRow) -> bool {
1390 const QModelIndex sourceIndex = model->index(sourceRow, source_sort_column, source_parent);
1391 const QModelIndex proxyIndex = source_to_proxy(sourceIndex);
1392 Q_ASSERT(proxyIndex.isValid()); // caller ensured source_rows were not filtered out
1393 if (proxyIndex.row() > 0) {
1394 const QModelIndex prevProxyIndex = q->sibling(proxyIndex.row() - 1, proxy_sort_column, proxyIndex);
1395 const QModelIndex prevSourceIndex = proxy_to_source(prevProxyIndex);
1396 if (sort_order == Qt::AscendingOrder ? q->lessThan(sourceIndex, prevSourceIndex) : q->lessThan(prevSourceIndex, sourceIndex))
1397 return true;
1398 }
1399 if (proxyIndex.row() < proxyRowCount - 1) {
1400 const QModelIndex nextProxyIndex = q->sibling(proxyIndex.row() + 1, proxy_sort_column, proxyIndex);
1401 const QModelIndex nextSourceIndex = proxy_to_source(nextProxyIndex);
1402 if (sort_order == Qt::AscendingOrder ? q->lessThan(nextSourceIndex, sourceIndex) : q->lessThan(sourceIndex, nextSourceIndex))
1403 return true;
1404 }
1405 return false;
1406 });
1407 }
1408
_q_sourceDataChanged(const QModelIndex & source_top_left,const QModelIndex & source_bottom_right,const QVector<int> & roles)1409 void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &source_top_left,
1410 const QModelIndex &source_bottom_right,
1411 const QVector<int> &roles)
1412 {
1413 Q_Q(QSortFilterProxyModel);
1414 if (!source_top_left.isValid() || !source_bottom_right.isValid())
1415 return;
1416
1417 std::vector<QSortFilterProxyModelDataChanged> data_changed_list;
1418 data_changed_list.emplace_back(source_top_left, source_bottom_right);
1419
1420 // Do check parents if the filter role have changed and we are recursive
1421 if (filter_recursive && (roles.isEmpty() || roles.contains(filter_role))) {
1422 QModelIndex source_parent = source_top_left.parent();
1423
1424 while (source_parent.isValid()) {
1425 data_changed_list.emplace_back(source_parent, source_parent);
1426 source_parent = source_parent.parent();
1427 }
1428 }
1429
1430 for (const QSortFilterProxyModelDataChanged &data_changed : data_changed_list) {
1431 const QModelIndex &source_top_left = data_changed.topLeft;
1432 const QModelIndex &source_bottom_right = data_changed.bottomRight;
1433 const QModelIndex source_parent = source_top_left.parent();
1434
1435 bool change_in_unmapped_parent = false;
1436 IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
1437 if (it == source_index_mapping.constEnd()) {
1438 // We don't have mapping for this index, so we cannot know how things
1439 // changed (in case the change affects filtering) in order to forward
1440 // the change correctly.
1441 // But we can at least forward the signal "as is", if the row isn't
1442 // filtered out, this is better than nothing.
1443 it = create_mapping_recursive(source_parent);
1444 if (it == source_index_mapping.constEnd())
1445 continue;
1446 change_in_unmapped_parent = true;
1447 }
1448
1449 Mapping *m = it.value();
1450
1451 // Figure out how the source changes affect us
1452 QVector<int> source_rows_remove;
1453 QVector<int> source_rows_insert;
1454 QVector<int> source_rows_change;
1455 QVector<int> source_rows_resort;
1456 int end = qMin(source_bottom_right.row(), m->proxy_rows.count() - 1);
1457 for (int source_row = source_top_left.row(); source_row <= end; ++source_row) {
1458 if (dynamic_sortfilter && !change_in_unmapped_parent) {
1459 if (m->proxy_rows.at(source_row) != -1) {
1460 if (!filterAcceptsRowInternal(source_row, source_parent)) {
1461 // This source row no longer satisfies the filter, so it must be removed
1462 source_rows_remove.append(source_row);
1463 } else if (source_sort_column >= source_top_left.column() && source_sort_column <= source_bottom_right.column()) {
1464 // This source row has changed in a way that may affect sorted order
1465 source_rows_resort.append(source_row);
1466 } else {
1467 // This row has simply changed, without affecting filtering nor sorting
1468 source_rows_change.append(source_row);
1469 }
1470 } else {
1471 if (!itemsBeingRemoved.contains(source_parent, source_row) && filterAcceptsRowInternal(source_row, source_parent)) {
1472 // This source row now satisfies the filter, so it must be added
1473 source_rows_insert.append(source_row);
1474 }
1475 }
1476 } else {
1477 if (m->proxy_rows.at(source_row) != -1)
1478 source_rows_change.append(source_row);
1479 }
1480 }
1481
1482 if (!source_rows_remove.isEmpty()) {
1483 remove_source_items(m->proxy_rows, m->source_rows,
1484 source_rows_remove, source_parent, Qt::Vertical);
1485 QSet<int> source_rows_remove_set = qVectorToSet(source_rows_remove);
1486 QVector<QModelIndex>::iterator childIt = m->mapped_children.end();
1487 while (childIt != m->mapped_children.begin()) {
1488 --childIt;
1489 const QModelIndex source_child_index = *childIt;
1490 if (source_rows_remove_set.contains(source_child_index.row())) {
1491 childIt = m->mapped_children.erase(childIt);
1492 remove_from_mapping(source_child_index);
1493 }
1494 }
1495 }
1496
1497 if (!source_rows_resort.isEmpty()) {
1498 if (needsReorder(source_rows_resort, source_parent)) {
1499 // Re-sort the rows of this level
1500 QList<QPersistentModelIndex> parents;
1501 parents << q->mapFromSource(source_parent);
1502 emit q->layoutAboutToBeChanged(parents, QAbstractItemModel::VerticalSortHint);
1503 QModelIndexPairList source_indexes = store_persistent_indexes();
1504 remove_source_items(m->proxy_rows, m->source_rows, source_rows_resort,
1505 source_parent, Qt::Vertical, false);
1506 sort_source_rows(source_rows_resort, source_parent);
1507 insert_source_items(m->proxy_rows, m->source_rows, source_rows_resort,
1508 source_parent, Qt::Vertical, false);
1509 update_persistent_indexes(source_indexes);
1510 emit q->layoutChanged(parents, QAbstractItemModel::VerticalSortHint);
1511 }
1512 // Make sure we also emit dataChanged for the rows
1513 source_rows_change += source_rows_resort;
1514 }
1515
1516 if (!source_rows_change.isEmpty()) {
1517 // Find the proxy row range
1518 int proxy_start_row;
1519 int proxy_end_row;
1520 proxy_item_range(m->proxy_rows, source_rows_change,
1521 proxy_start_row, proxy_end_row);
1522 // ### Find the proxy column range also
1523 if (proxy_end_row >= 0) {
1524 // the row was accepted, but some columns might still be filtered out
1525 int source_left_column = source_top_left.column();
1526 while (source_left_column < source_bottom_right.column()
1527 && m->proxy_columns.at(source_left_column) == -1)
1528 ++source_left_column;
1529 if (m->proxy_columns.at(source_left_column) != -1) {
1530 const QModelIndex proxy_top_left = create_index(
1531 proxy_start_row, m->proxy_columns.at(source_left_column), it);
1532 int source_right_column = source_bottom_right.column();
1533 while (source_right_column > source_top_left.column()
1534 && m->proxy_columns.at(source_right_column) == -1)
1535 --source_right_column;
1536 if (m->proxy_columns.at(source_right_column) != -1) {
1537 const QModelIndex proxy_bottom_right = create_index(
1538 proxy_end_row, m->proxy_columns.at(source_right_column), it);
1539 emit q->dataChanged(proxy_top_left, proxy_bottom_right, roles);
1540 }
1541 }
1542 }
1543 }
1544
1545 if (!source_rows_insert.isEmpty()) {
1546 sort_source_rows(source_rows_insert, source_parent);
1547 insert_source_items(m->proxy_rows, m->source_rows,
1548 source_rows_insert, source_parent, Qt::Vertical);
1549 }
1550 }
1551 }
1552
_q_sourceHeaderDataChanged(Qt::Orientation orientation,int start,int end)1553 void QSortFilterProxyModelPrivate::_q_sourceHeaderDataChanged(Qt::Orientation orientation,
1554 int start, int end)
1555 {
1556 Q_ASSERT(start <= end);
1557
1558 Q_Q(QSortFilterProxyModel);
1559 Mapping *m = create_mapping(QModelIndex()).value();
1560
1561 const QVector<int> &source_to_proxy = (orientation == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
1562
1563 QVector<int> proxy_positions;
1564 proxy_positions.reserve(end - start + 1);
1565 {
1566 Q_ASSERT(source_to_proxy.size() > end);
1567 QVector<int>::const_iterator it = source_to_proxy.constBegin() + start;
1568 const QVector<int>::const_iterator endIt = source_to_proxy.constBegin() + end + 1;
1569 for ( ; it != endIt; ++it) {
1570 if (*it != -1)
1571 proxy_positions.push_back(*it);
1572 }
1573 }
1574
1575 std::sort(proxy_positions.begin(), proxy_positions.end());
1576
1577 int last_index = 0;
1578 const int numItems = proxy_positions.size();
1579 while (last_index < numItems) {
1580 const int proxyStart = proxy_positions.at(last_index);
1581 int proxyEnd = proxyStart;
1582 ++last_index;
1583 for (int i = last_index; i < numItems; ++i) {
1584 if (proxy_positions.at(i) == proxyEnd + 1) {
1585 ++last_index;
1586 ++proxyEnd;
1587 } else {
1588 break;
1589 }
1590 }
1591 emit q->headerDataChanged(orientation, proxyStart, proxyEnd);
1592 }
1593 }
1594
_q_sourceAboutToBeReset()1595 void QSortFilterProxyModelPrivate::_q_sourceAboutToBeReset()
1596 {
1597 Q_Q(QSortFilterProxyModel);
1598 q->beginResetModel();
1599 }
1600
_q_sourceReset()1601 void QSortFilterProxyModelPrivate::_q_sourceReset()
1602 {
1603 Q_Q(QSortFilterProxyModel);
1604 invalidatePersistentIndexes();
1605 _q_clearMapping();
1606 // All internal structures are deleted in clear()
1607 q->endResetModel();
1608 update_source_sort_column();
1609 if (dynamic_sortfilter && update_source_sort_column())
1610 sort();
1611 }
1612
_q_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> & sourceParents,QAbstractItemModel::LayoutChangeHint hint)1613 void QSortFilterProxyModelPrivate::_q_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
1614 {
1615 Q_Q(QSortFilterProxyModel);
1616 Q_UNUSED(hint); // We can't forward Hint because we might filter additional rows or columns
1617 saved_persistent_indexes.clear();
1618
1619 saved_layoutChange_parents.clear();
1620 for (const QPersistentModelIndex &parent : sourceParents) {
1621 if (!parent.isValid()) {
1622 saved_layoutChange_parents << QPersistentModelIndex();
1623 continue;
1624 }
1625 const QModelIndex mappedParent = q->mapFromSource(parent);
1626 // Might be filtered out.
1627 if (mappedParent.isValid())
1628 saved_layoutChange_parents << mappedParent;
1629 }
1630
1631 // All parents filtered out.
1632 if (!sourceParents.isEmpty() && saved_layoutChange_parents.isEmpty())
1633 return;
1634
1635 emit q->layoutAboutToBeChanged(saved_layoutChange_parents);
1636 if (persistent.indexes.isEmpty())
1637 return;
1638
1639 saved_persistent_indexes = store_persistent_indexes();
1640 }
1641
_q_sourceLayoutChanged(const QList<QPersistentModelIndex> & sourceParents,QAbstractItemModel::LayoutChangeHint hint)1642 void QSortFilterProxyModelPrivate::_q_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
1643 {
1644 Q_Q(QSortFilterProxyModel);
1645 Q_UNUSED(hint); // We can't forward Hint because we might filter additional rows or columns
1646
1647 if (!sourceParents.isEmpty() && saved_layoutChange_parents.isEmpty())
1648 return;
1649
1650 // Optimize: We only actually have to clear the mapping related to the contents of
1651 // sourceParents, not everything.
1652 qDeleteAll(source_index_mapping);
1653 source_index_mapping.clear();
1654
1655 update_persistent_indexes(saved_persistent_indexes);
1656 saved_persistent_indexes.clear();
1657
1658 if (dynamic_sortfilter)
1659 source_sort_column = find_source_sort_column();
1660
1661 emit q->layoutChanged(saved_layoutChange_parents);
1662 saved_layoutChange_parents.clear();
1663 }
1664
_q_sourceRowsAboutToBeInserted(const QModelIndex & source_parent,int start,int end)1665 void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeInserted(
1666 const QModelIndex &source_parent, int start, int end)
1667 {
1668 Q_UNUSED(start);
1669 Q_UNUSED(end);
1670
1671 const bool toplevel = !source_parent.isValid();
1672 const bool recursive_accepted = filter_recursive && !toplevel && filterAcceptsRowInternal(source_parent.row(), source_parent.parent());
1673 //Force the creation of a mapping now, even if it's empty.
1674 //We need it because the proxy can be accessed at the moment it emits rowsAboutToBeInserted in insert_source_items
1675 if (!filter_recursive || toplevel || recursive_accepted) {
1676 if (can_create_mapping(source_parent))
1677 create_mapping(source_parent);
1678 if (filter_recursive)
1679 complete_insert = true;
1680 } else {
1681 // The row could have been rejected or the parent might be not yet known... let's try to discover it
1682 QModelIndex top_source_parent = source_parent;
1683 QModelIndex parent = source_parent.parent();
1684 QModelIndex grandParent = parent.parent();
1685
1686 while (parent.isValid() && !filterAcceptsRowInternal(parent.row(), grandParent)) {
1687 top_source_parent = parent;
1688 parent = grandParent;
1689 grandParent = parent.parent();
1690 }
1691
1692 last_top_source = top_source_parent;
1693 }
1694 }
1695
_q_sourceRowsInserted(const QModelIndex & source_parent,int start,int end)1696 void QSortFilterProxyModelPrivate::_q_sourceRowsInserted(
1697 const QModelIndex &source_parent, int start, int end)
1698 {
1699 if (!filter_recursive || complete_insert) {
1700 if (filter_recursive)
1701 complete_insert = false;
1702 source_items_inserted(source_parent, start, end, Qt::Vertical);
1703 if (update_source_sort_column() && dynamic_sortfilter) //previous call to update_source_sort_column may fail if the model has no column.
1704 sort(); // now it should succeed so we need to make sure to sort again
1705 return;
1706 }
1707
1708 if (filter_recursive) {
1709 bool accept = false;
1710
1711 for (int row = start; row <= end; ++row) {
1712 if (filterAcceptsRowInternal(row, source_parent)) {
1713 accept = true;
1714 break;
1715 }
1716 }
1717
1718 if (!accept) // the new rows have no descendants that match the filter, filter them out.
1719 return;
1720
1721 // last_top_source should now become visible
1722 _q_sourceDataChanged(last_top_source, last_top_source, QVector<int>());
1723 }
1724 }
1725
_q_sourceRowsAboutToBeRemoved(const QModelIndex & source_parent,int start,int end)1726 void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeRemoved(
1727 const QModelIndex &source_parent, int start, int end)
1728 {
1729 itemsBeingRemoved = QRowsRemoval(source_parent, start, end);
1730 source_items_about_to_be_removed(source_parent, start, end,
1731 Qt::Vertical);
1732 }
1733
_q_sourceRowsRemoved(const QModelIndex & source_parent,int start,int end)1734 void QSortFilterProxyModelPrivate::_q_sourceRowsRemoved(
1735 const QModelIndex &source_parent, int start, int end)
1736 {
1737 itemsBeingRemoved = QRowsRemoval();
1738 source_items_removed(source_parent, start, end, Qt::Vertical);
1739
1740 if (filter_recursive) {
1741 // Find out if removing this visible row means that some ascendant
1742 // row can now be hidden.
1743 // We go up until we find a row that should still be visible
1744 // and then make QSFPM re-evaluate the last one we saw before that, to hide it.
1745
1746 QModelIndex to_hide;
1747 QModelIndex source_ascendant = source_parent;
1748
1749 while (source_ascendant.isValid()) {
1750 if (filterAcceptsRowInternal(source_ascendant.row(), source_ascendant.parent()))
1751 break;
1752
1753 to_hide = source_ascendant;
1754 source_ascendant = source_ascendant.parent();
1755 }
1756
1757 if (to_hide.isValid())
1758 _q_sourceDataChanged(to_hide, to_hide, QVector<int>());
1759 }
1760 }
1761
_q_sourceRowsAboutToBeMoved(const QModelIndex & sourceParent,int,int,const QModelIndex & destParent,int)1762 void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeMoved(
1763 const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1764 {
1765 // Because rows which are contiguous in the source model might not be contiguous
1766 // in the proxy due to sorting, the best thing we can do here is be specific about what
1767 // parents are having their children changed.
1768 // Optimize: Emit move signals if the proxy is not sorted. Will need to account for rows
1769 // being filtered out though.
1770
1771 QList<QPersistentModelIndex> parents;
1772 parents << sourceParent;
1773 if (sourceParent != destParent)
1774 parents << destParent;
1775 _q_sourceLayoutAboutToBeChanged(parents, QAbstractItemModel::NoLayoutChangeHint);
1776 }
1777
_q_sourceRowsMoved(const QModelIndex & sourceParent,int,int,const QModelIndex & destParent,int)1778 void QSortFilterProxyModelPrivate::_q_sourceRowsMoved(
1779 const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1780 {
1781 QList<QPersistentModelIndex> parents;
1782 parents << sourceParent;
1783 if (sourceParent != destParent)
1784 parents << destParent;
1785 _q_sourceLayoutChanged(parents, QAbstractItemModel::NoLayoutChangeHint);
1786 }
1787
_q_sourceColumnsAboutToBeInserted(const QModelIndex & source_parent,int start,int end)1788 void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeInserted(
1789 const QModelIndex &source_parent, int start, int end)
1790 {
1791 Q_UNUSED(start);
1792 Q_UNUSED(end);
1793 //Force the creation of a mapping now, even if it's empty.
1794 //We need it because the proxy can be accessed at the moment it emits columnsAboutToBeInserted in insert_source_items
1795 if (can_create_mapping(source_parent))
1796 create_mapping(source_parent);
1797 }
1798
_q_sourceColumnsInserted(const QModelIndex & source_parent,int start,int end)1799 void QSortFilterProxyModelPrivate::_q_sourceColumnsInserted(
1800 const QModelIndex &source_parent, int start, int end)
1801 {
1802 Q_Q(const QSortFilterProxyModel);
1803 source_items_inserted(source_parent, start, end, Qt::Horizontal);
1804
1805 if (source_parent.isValid())
1806 return; //we sort according to the root column only
1807 if (source_sort_column == -1) {
1808 //we update the source_sort_column depending on the proxy_sort_column
1809 if (update_source_sort_column() && dynamic_sortfilter)
1810 sort();
1811 } else {
1812 if (start <= source_sort_column)
1813 source_sort_column += end - start + 1;
1814
1815 proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column();
1816 }
1817 }
1818
_q_sourceColumnsAboutToBeRemoved(const QModelIndex & source_parent,int start,int end)1819 void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeRemoved(
1820 const QModelIndex &source_parent, int start, int end)
1821 {
1822 source_items_about_to_be_removed(source_parent, start, end,
1823 Qt::Horizontal);
1824 }
1825
_q_sourceColumnsRemoved(const QModelIndex & source_parent,int start,int end)1826 void QSortFilterProxyModelPrivate::_q_sourceColumnsRemoved(
1827 const QModelIndex &source_parent, int start, int end)
1828 {
1829 Q_Q(const QSortFilterProxyModel);
1830 source_items_removed(source_parent, start, end, Qt::Horizontal);
1831
1832 if (source_parent.isValid())
1833 return; //we sort according to the root column only
1834 if (start <= source_sort_column) {
1835 if (end < source_sort_column)
1836 source_sort_column -= end - start + 1;
1837 else
1838 source_sort_column = -1;
1839 }
1840
1841 if (source_sort_column >= 0)
1842 proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column();
1843 else
1844 proxy_sort_column = -1;
1845 }
1846
_q_sourceColumnsAboutToBeMoved(const QModelIndex & sourceParent,int,int,const QModelIndex & destParent,int)1847 void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeMoved(
1848 const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1849 {
1850 QList<QPersistentModelIndex> parents;
1851 parents << sourceParent;
1852 if (sourceParent != destParent)
1853 parents << destParent;
1854 _q_sourceLayoutAboutToBeChanged(parents, QAbstractItemModel::NoLayoutChangeHint);
1855 }
1856
_q_sourceColumnsMoved(const QModelIndex & sourceParent,int,int,const QModelIndex & destParent,int)1857 void QSortFilterProxyModelPrivate::_q_sourceColumnsMoved(
1858 const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1859 {
1860 QList<QPersistentModelIndex> parents;
1861 parents << sourceParent;
1862 if (sourceParent != destParent)
1863 parents << destParent;
1864 _q_sourceLayoutChanged(parents, QAbstractItemModel::NoLayoutChangeHint);
1865 }
1866
1867 /*!
1868 \since 4.1
1869 \class QSortFilterProxyModel
1870 \inmodule QtCore
1871 \brief The QSortFilterProxyModel class provides support for sorting and
1872 filtering data passed between another model and a view.
1873
1874 \ingroup model-view
1875
1876 QSortFilterProxyModel can be used for sorting items, filtering out items,
1877 or both. The model transforms the structure of a source model by mapping
1878 the model indexes it supplies to new indexes, corresponding to different
1879 locations, for views to use. This approach allows a given source model to
1880 be restructured as far as views are concerned without requiring any
1881 transformations on the underlying data, and without duplicating the data in
1882 memory.
1883
1884 Let's assume that we want to sort and filter the items provided by a custom
1885 model. The code to set up the model and the view, \e without sorting and
1886 filtering, would look like this:
1887
1888 \snippet qsortfilterproxymodel-details/main.cpp 1
1889
1890 To add sorting and filtering support to \c MyItemModel, we need to create
1891 a QSortFilterProxyModel, call setSourceModel() with the \c MyItemModel as
1892 argument, and install the QSortFilterProxyModel on the view:
1893
1894 \snippet qsortfilterproxymodel-details/main.cpp 0
1895 \snippet qsortfilterproxymodel-details/main.cpp 2
1896
1897 At this point, neither sorting nor filtering is enabled; the original data
1898 is displayed in the view. Any changes made through the
1899 QSortFilterProxyModel are applied to the original model.
1900
1901 The QSortFilterProxyModel acts as a wrapper for the original model. If you
1902 need to convert source \l{QModelIndex}es to sorted/filtered model indexes
1903 or vice versa, use mapToSource(), mapFromSource(), mapSelectionToSource(),
1904 and mapSelectionFromSource().
1905
1906 \note By default, the model dynamically re-sorts and re-filters data
1907 whenever the original model changes. This behavior can be changed by
1908 setting the \l{QSortFilterProxyModel::dynamicSortFilter}{dynamicSortFilter}
1909 property.
1910
1911 The \l{itemviews/basicsortfiltermodel}{Basic Sort/Filter Model} and
1912 \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model} examples
1913 illustrate how to use QSortFilterProxyModel to perform basic sorting and
1914 filtering and how to subclass it to implement custom behavior.
1915
1916 \section1 Sorting
1917
1918 QTableView and QTreeView have a
1919 \l{QTreeView::sortingEnabled}{sortingEnabled} property that controls
1920 whether the user can sort the view by clicking the view's horizontal
1921 header. For example:
1922
1923 \snippet qsortfilterproxymodel-details/main.cpp 3
1924
1925 When this feature is on (the default is off), clicking on a header section
1926 sorts the items according to that column. By clicking repeatedly, the user
1927 can alternate between ascending and descending order.
1928
1929 \image qsortfilterproxymodel-sorting.png A sorted QTreeView
1930
1931 Behind the scene, the view calls the sort() virtual function on the model
1932 to reorder the data in the model. To make your data sortable, you can
1933 either implement sort() in your model, or use a QSortFilterProxyModel to
1934 wrap your model -- QSortFilterProxyModel provides a generic sort()
1935 reimplementation that operates on the sortRole() (Qt::DisplayRole by
1936 default) of the items and that understands several data types, including
1937 \c int, QString, and QDateTime. For hierarchical models, sorting is applied
1938 recursively to all child items. String comparisons are case sensitive by
1939 default; this can be changed by setting the \l{QSortFilterProxyModel::}
1940 {sortCaseSensitivity} property.
1941
1942 Custom sorting behavior is achieved by subclassing
1943 QSortFilterProxyModel and reimplementing lessThan(), which is
1944 used to compare items. For example:
1945
1946 \snippet ../widgets/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 5
1947
1948 (This code snippet comes from the
1949 \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model}
1950 example.)
1951
1952 An alternative approach to sorting is to disable sorting on the view and to
1953 impose a certain order to the user. This is done by explicitly calling
1954 sort() with the desired column and order as arguments on the
1955 QSortFilterProxyModel (or on the original model if it implements sort()).
1956 For example:
1957
1958 \snippet qsortfilterproxymodel-details/main.cpp 4
1959
1960 QSortFilterProxyModel can be sorted by column -1, in which case it returns
1961 to the sort order of the underlying source model.
1962
1963 \section1 Filtering
1964
1965 In addition to sorting, QSortFilterProxyModel can be used to hide items
1966 that do not match a certain filter. The filter is specified using a QRegExp
1967 object and is applied to the filterRole() (Qt::DisplayRole by default) of
1968 each item, for a given column. The QRegExp object can be used to match a
1969 regular expression, a wildcard pattern, or a fixed string. For example:
1970
1971 \snippet qsortfilterproxymodel-details/main.cpp 5
1972
1973 For hierarchical models, the filter is applied recursively to all children.
1974 If a parent item doesn't match the filter, none of its children will be
1975 shown.
1976
1977 A common use case is to let the user specify the filter regular expression,
1978 wildcard pattern, or fixed string in a QLineEdit and to connect the
1979 \l{QLineEdit::textChanged()}{textChanged()} signal to setFilterRegularExpression(),
1980 setFilterWildcard(), or setFilterFixedString() to reapply the filter.
1981
1982 Custom filtering behavior can be achieved by reimplementing the
1983 filterAcceptsRow() and filterAcceptsColumn() functions. For
1984 example (from the \l{itemviews/customsortfiltermodel}
1985 {Custom Sort/Filter Model} example), the following implementation ignores
1986 the \l{QSortFilterProxyModel::filterKeyColumn}{filterKeyColumn} property
1987 and performs filtering on columns 0, 1, and 2:
1988
1989 \snippet ../widgets/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 3
1990
1991 (This code snippet comes from the
1992 \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model}
1993 example.)
1994
1995 If you are working with large amounts of filtering and have to invoke
1996 invalidateFilter() repeatedly, using beginResetModel() / endResetModel() may
1997 be more efficient, depending on the implementation of your model. However,
1998 beginResetModel() / endResetModel() returns the
1999 proxy model to its original state, losing selection information, and will
2000 cause the proxy model to be repopulated.
2001
2002 \section1 Subclassing
2003
2004 Since QAbstractProxyModel and its subclasses are derived from
2005 QAbstractItemModel, much of the same advice about subclassing normal models
2006 also applies to proxy models. In addition, it is worth noting that many of
2007 the default implementations of functions in this class are written so that
2008 they call the equivalent functions in the relevant source model. This
2009 simple proxying mechanism may need to be overridden for source models with
2010 more complex behavior; for example, if the source model provides a custom
2011 hasChildren() implementation, you should also provide one in the proxy
2012 model.
2013
2014 \note Some general guidelines for subclassing models are available in the
2015 \l{Model Subclassing Reference}.
2016
2017 \note With Qt 5, regular expression support has been improved through the
2018 QRegularExpression class. QSortFilterProxyModel dating back prior to that
2019 class creation, it originally supported only QRegExp. Since Qt 5.12,
2020 QRegularExpression APIs have been added. Therefore, QRegExp APIs should be
2021 considered deprecated and the QRegularExpression version should be used in
2022 place.
2023
2024 \warning Don't mix calls to the getters and setters of different regexp types
2025 as this will lead to unexpected results. For maximum compatibility, the original
2026 implementation has been kept. Therefore, if, for example, a call to
2027 setFilterRegularExpression is made followed by another one to
2028 setFilterFixedString, the first call will setup a QRegularExpression object
2029 to use as filter while the second will setup a QRegExp in FixedString mode.
2030 However, this is an implementation detail that might change in the future.
2031
2032 \sa QAbstractProxyModel, QAbstractItemModel, {Model/View Programming},
2033 {Basic Sort/Filter Model Example}, {Custom Sort/Filter Model Example}, QIdentityProxyModel
2034 */
2035
2036 /*!
2037 Constructs a sorting filter model with the given \a parent.
2038 */
2039
QSortFilterProxyModel(QObject * parent)2040 QSortFilterProxyModel::QSortFilterProxyModel(QObject *parent)
2041 : QAbstractProxyModel(*new QSortFilterProxyModelPrivate, parent)
2042 {
2043 Q_D(QSortFilterProxyModel);
2044 d->proxy_sort_column = d->source_sort_column = -1;
2045 d->sort_order = Qt::AscendingOrder;
2046 d->sort_casesensitivity = Qt::CaseSensitive;
2047 d->sort_role = Qt::DisplayRole;
2048 d->sort_localeaware = false;
2049 d->filter_column = 0;
2050 d->filter_role = Qt::DisplayRole;
2051 d->filter_recursive = false;
2052 d->dynamic_sortfilter = true;
2053 d->complete_insert = false;
2054 connect(this, SIGNAL(modelReset()), this, SLOT(_q_clearMapping()));
2055 }
2056
2057 /*!
2058 Destroys this sorting filter model.
2059 */
~QSortFilterProxyModel()2060 QSortFilterProxyModel::~QSortFilterProxyModel()
2061 {
2062 Q_D(QSortFilterProxyModel);
2063 qDeleteAll(d->source_index_mapping);
2064 d->source_index_mapping.clear();
2065 }
2066
2067 /*!
2068 \reimp
2069 */
setSourceModel(QAbstractItemModel * sourceModel)2070 void QSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
2071 {
2072 Q_D(QSortFilterProxyModel);
2073
2074 if (sourceModel == d->model)
2075 return;
2076
2077 beginResetModel();
2078
2079 disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
2080 this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QVector<int>)));
2081
2082 disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
2083 this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int)));
2084
2085 disconnect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
2086 this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int)));
2087
2088 disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
2089 this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int)));
2090
2091 disconnect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
2092 this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int)));
2093
2094 disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
2095 this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int)));
2096
2097 disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
2098 this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
2099
2100 disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
2101 this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int)));
2102
2103 disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
2104 this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int)));
2105
2106 disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
2107 this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int)));
2108
2109 disconnect(d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
2110 this, SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
2111
2112 disconnect(d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
2113 this, SLOT(_q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)));
2114
2115 disconnect(d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
2116 this, SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
2117
2118 disconnect(d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
2119 this, SLOT(_q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int)));
2120
2121 disconnect(d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
2122 this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
2123
2124 disconnect(d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
2125 this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
2126
2127 disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_sourceAboutToBeReset()));
2128 disconnect(d->model, SIGNAL(modelReset()), this, SLOT(_q_sourceReset()));
2129
2130 // same as in _q_sourceReset()
2131 d->invalidatePersistentIndexes();
2132 d->_q_clearMapping();
2133
2134 QAbstractProxyModel::setSourceModel(sourceModel);
2135
2136 connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
2137 this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QVector<int>)));
2138
2139 connect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
2140 this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int)));
2141
2142 connect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
2143 this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int)));
2144
2145 connect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
2146 this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int)));
2147
2148 connect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
2149 this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int)));
2150
2151 connect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
2152 this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int)));
2153
2154 connect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
2155 this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
2156
2157 connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
2158 this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int)));
2159
2160 connect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
2161 this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int)));
2162
2163 connect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
2164 this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int)));
2165
2166 connect(d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
2167 this, SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
2168
2169 connect(d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
2170 this, SLOT(_q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)));
2171
2172 connect(d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
2173 this, SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
2174
2175 connect(d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
2176 this, SLOT(_q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int)));
2177
2178 connect(d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
2179 this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
2180
2181 connect(d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
2182 this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
2183
2184 connect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_sourceAboutToBeReset()));
2185 connect(d->model, SIGNAL(modelReset()), this, SLOT(_q_sourceReset()));
2186
2187 endResetModel();
2188 if (d->update_source_sort_column() && d->dynamic_sortfilter)
2189 d->sort();
2190 }
2191
2192 /*!
2193 \reimp
2194 */
index(int row,int column,const QModelIndex & parent) const2195 QModelIndex QSortFilterProxyModel::index(int row, int column, const QModelIndex &parent) const
2196 {
2197 Q_D(const QSortFilterProxyModel);
2198 if (row < 0 || column < 0)
2199 return QModelIndex();
2200
2201 QModelIndex source_parent = mapToSource(parent); // parent is already mapped at this point
2202 IndexMap::const_iterator it = d->create_mapping(source_parent); // but make sure that the children are mapped
2203 if (it.value()->source_rows.count() <= row || it.value()->source_columns.count() <= column)
2204 return QModelIndex();
2205
2206 return d->create_index(row, column, it);
2207 }
2208
2209 /*!
2210 \reimp
2211 */
parent(const QModelIndex & child) const2212 QModelIndex QSortFilterProxyModel::parent(const QModelIndex &child) const
2213 {
2214 Q_D(const QSortFilterProxyModel);
2215 if (!d->indexValid(child))
2216 return QModelIndex();
2217 IndexMap::const_iterator it = d->index_to_iterator(child);
2218 Q_ASSERT(it != d->source_index_mapping.constEnd());
2219 QModelIndex source_parent = it.key();
2220 QModelIndex proxy_parent = mapFromSource(source_parent);
2221 return proxy_parent;
2222 }
2223
2224 /*!
2225 \reimp
2226 */
sibling(int row,int column,const QModelIndex & idx) const2227 QModelIndex QSortFilterProxyModel::sibling(int row, int column, const QModelIndex &idx) const
2228 {
2229 Q_D(const QSortFilterProxyModel);
2230 if (!d->indexValid(idx))
2231 return QModelIndex();
2232
2233 const IndexMap::const_iterator it = d->index_to_iterator(idx);
2234 if (it.value()->source_rows.count() <= row || it.value()->source_columns.count() <= column)
2235 return QModelIndex();
2236
2237 return d->create_index(row, column, it);
2238 }
2239
2240 /*!
2241 \reimp
2242 */
rowCount(const QModelIndex & parent) const2243 int QSortFilterProxyModel::rowCount(const QModelIndex &parent) const
2244 {
2245 Q_D(const QSortFilterProxyModel);
2246 QModelIndex source_parent = mapToSource(parent);
2247 if (parent.isValid() && !source_parent.isValid())
2248 return 0;
2249 IndexMap::const_iterator it = d->create_mapping(source_parent);
2250 return it.value()->source_rows.count();
2251 }
2252
2253 /*!
2254 \reimp
2255 */
columnCount(const QModelIndex & parent) const2256 int QSortFilterProxyModel::columnCount(const QModelIndex &parent) const
2257 {
2258 Q_D(const QSortFilterProxyModel);
2259 QModelIndex source_parent = mapToSource(parent);
2260 if (parent.isValid() && !source_parent.isValid())
2261 return 0;
2262 IndexMap::const_iterator it = d->create_mapping(source_parent);
2263 return it.value()->source_columns.count();
2264 }
2265
2266 /*!
2267 \reimp
2268 */
hasChildren(const QModelIndex & parent) const2269 bool QSortFilterProxyModel::hasChildren(const QModelIndex &parent) const
2270 {
2271 Q_D(const QSortFilterProxyModel);
2272 QModelIndex source_parent = mapToSource(parent);
2273 if (parent.isValid() && !source_parent.isValid())
2274 return false;
2275 if (!d->model->hasChildren(source_parent))
2276 return false;
2277
2278 if (d->model->canFetchMore(source_parent))
2279 return true; //we assume we might have children that can be fetched
2280
2281 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2282 return m->source_rows.count() != 0 && m->source_columns.count() != 0;
2283 }
2284
2285 /*!
2286 \reimp
2287 */
data(const QModelIndex & index,int role) const2288 QVariant QSortFilterProxyModel::data(const QModelIndex &index, int role) const
2289 {
2290 Q_D(const QSortFilterProxyModel);
2291 QModelIndex source_index = mapToSource(index);
2292 if (index.isValid() && !source_index.isValid())
2293 return QVariant();
2294 return d->model->data(source_index, role);
2295 }
2296
2297 /*!
2298 \reimp
2299 */
setData(const QModelIndex & index,const QVariant & value,int role)2300 bool QSortFilterProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
2301 {
2302 Q_D(QSortFilterProxyModel);
2303 QModelIndex source_index = mapToSource(index);
2304 if (index.isValid() && !source_index.isValid())
2305 return false;
2306 return d->model->setData(source_index, value, role);
2307 }
2308
2309 /*!
2310 \reimp
2311 */
headerData(int section,Qt::Orientation orientation,int role) const2312 QVariant QSortFilterProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
2313 {
2314 Q_D(const QSortFilterProxyModel);
2315 IndexMap::const_iterator it = d->create_mapping(QModelIndex());
2316 if (it.value()->source_rows.count() * it.value()->source_columns.count() > 0)
2317 return QAbstractProxyModel::headerData(section, orientation, role);
2318 int source_section;
2319 if (orientation == Qt::Vertical) {
2320 if (section < 0 || section >= it.value()->source_rows.count())
2321 return QVariant();
2322 source_section = it.value()->source_rows.at(section);
2323 } else {
2324 if (section < 0 || section >= it.value()->source_columns.count())
2325 return QVariant();
2326 source_section = it.value()->source_columns.at(section);
2327 }
2328 return d->model->headerData(source_section, orientation, role);
2329 }
2330
2331 /*!
2332 \reimp
2333 */
setHeaderData(int section,Qt::Orientation orientation,const QVariant & value,int role)2334 bool QSortFilterProxyModel::setHeaderData(int section, Qt::Orientation orientation,
2335 const QVariant &value, int role)
2336 {
2337 Q_D(QSortFilterProxyModel);
2338 IndexMap::const_iterator it = d->create_mapping(QModelIndex());
2339 if (it.value()->source_rows.count() * it.value()->source_columns.count() > 0)
2340 return QAbstractProxyModel::setHeaderData(section, orientation, value, role);
2341 int source_section;
2342 if (orientation == Qt::Vertical) {
2343 if (section < 0 || section >= it.value()->source_rows.count())
2344 return false;
2345 source_section = it.value()->source_rows.at(section);
2346 } else {
2347 if (section < 0 || section >= it.value()->source_columns.count())
2348 return false;
2349 source_section = it.value()->source_columns.at(section);
2350 }
2351 return d->model->setHeaderData(source_section, orientation, value, role);
2352 }
2353
2354 /*!
2355 \reimp
2356 */
mimeData(const QModelIndexList & indexes) const2357 QMimeData *QSortFilterProxyModel::mimeData(const QModelIndexList &indexes) const
2358 {
2359 Q_D(const QSortFilterProxyModel);
2360 QModelIndexList source_indexes;
2361 source_indexes.reserve(indexes.count());
2362 for (const QModelIndex &idx : indexes)
2363 source_indexes << mapToSource(idx);
2364 return d->model->mimeData(source_indexes);
2365 }
2366
2367 /*!
2368 \reimp
2369 */
mimeTypes() const2370 QStringList QSortFilterProxyModel::mimeTypes() const
2371 {
2372 Q_D(const QSortFilterProxyModel);
2373 return d->model->mimeTypes();
2374 }
2375
2376 /*!
2377 \reimp
2378 */
supportedDropActions() const2379 Qt::DropActions QSortFilterProxyModel::supportedDropActions() const
2380 {
2381 Q_D(const QSortFilterProxyModel);
2382 return d->model->supportedDropActions();
2383 }
2384
2385 // Qt6: remove unnecessary reimplementation
2386 /*!
2387 \reimp
2388 */
dropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent)2389 bool QSortFilterProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
2390 int row, int column, const QModelIndex &parent)
2391 {
2392 return QAbstractProxyModel::dropMimeData(data, action, row, column, parent);
2393 }
2394
2395 /*!
2396 \reimp
2397 */
insertRows(int row,int count,const QModelIndex & parent)2398 bool QSortFilterProxyModel::insertRows(int row, int count, const QModelIndex &parent)
2399 {
2400 Q_D(QSortFilterProxyModel);
2401 if (row < 0 || count <= 0)
2402 return false;
2403 QModelIndex source_parent = mapToSource(parent);
2404 if (parent.isValid() && !source_parent.isValid())
2405 return false;
2406 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2407 if (row > m->source_rows.count())
2408 return false;
2409 int source_row = (row >= m->source_rows.count()
2410 ? m->proxy_rows.count()
2411 : m->source_rows.at(row));
2412 return d->model->insertRows(source_row, count, source_parent);
2413 }
2414
2415 /*!
2416 \reimp
2417 */
insertColumns(int column,int count,const QModelIndex & parent)2418 bool QSortFilterProxyModel::insertColumns(int column, int count, const QModelIndex &parent)
2419 {
2420 Q_D(QSortFilterProxyModel);
2421 if (column < 0|| count <= 0)
2422 return false;
2423 QModelIndex source_parent = mapToSource(parent);
2424 if (parent.isValid() && !source_parent.isValid())
2425 return false;
2426 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2427 if (column > m->source_columns.count())
2428 return false;
2429 int source_column = (column >= m->source_columns.count()
2430 ? m->proxy_columns.count()
2431 : m->source_columns.at(column));
2432 return d->model->insertColumns(source_column, count, source_parent);
2433 }
2434
2435 /*!
2436 \reimp
2437 */
removeRows(int row,int count,const QModelIndex & parent)2438 bool QSortFilterProxyModel::removeRows(int row, int count, const QModelIndex &parent)
2439 {
2440 Q_D(QSortFilterProxyModel);
2441 if (row < 0 || count <= 0)
2442 return false;
2443 QModelIndex source_parent = mapToSource(parent);
2444 if (parent.isValid() && !source_parent.isValid())
2445 return false;
2446 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2447 if (row + count > m->source_rows.count())
2448 return false;
2449 if ((count == 1)
2450 || ((d->source_sort_column < 0) && (m->proxy_rows.count() == m->source_rows.count()))) {
2451 int source_row = m->source_rows.at(row);
2452 return d->model->removeRows(source_row, count, source_parent);
2453 }
2454 // remove corresponding source intervals
2455 // ### if this proves to be slow, we can switch to single-row removal
2456 QVector<int> rows;
2457 rows.reserve(count);
2458 for (int i = row; i < row + count; ++i)
2459 rows.append(m->source_rows.at(i));
2460 std::sort(rows.begin(), rows.end());
2461
2462 int pos = rows.count() - 1;
2463 bool ok = true;
2464 while (pos >= 0) {
2465 const int source_end = rows.at(pos--);
2466 int source_start = source_end;
2467 while ((pos >= 0) && (rows.at(pos) == (source_start - 1))) {
2468 --source_start;
2469 --pos;
2470 }
2471 ok = ok && d->model->removeRows(source_start, source_end - source_start + 1,
2472 source_parent);
2473 }
2474 return ok;
2475 }
2476
2477 /*!
2478 \reimp
2479 */
removeColumns(int column,int count,const QModelIndex & parent)2480 bool QSortFilterProxyModel::removeColumns(int column, int count, const QModelIndex &parent)
2481 {
2482 Q_D(QSortFilterProxyModel);
2483 if (column < 0 || count <= 0)
2484 return false;
2485 QModelIndex source_parent = mapToSource(parent);
2486 if (parent.isValid() && !source_parent.isValid())
2487 return false;
2488 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2489 if (column + count > m->source_columns.count())
2490 return false;
2491 if ((count == 1) || (m->proxy_columns.count() == m->source_columns.count())) {
2492 int source_column = m->source_columns.at(column);
2493 return d->model->removeColumns(source_column, count, source_parent);
2494 }
2495 // remove corresponding source intervals
2496 QVector<int> columns;
2497 columns.reserve(count);
2498 for (int i = column; i < column + count; ++i)
2499 columns.append(m->source_columns.at(i));
2500
2501 int pos = columns.count() - 1;
2502 bool ok = true;
2503 while (pos >= 0) {
2504 const int source_end = columns.at(pos--);
2505 int source_start = source_end;
2506 while ((pos >= 0) && (columns.at(pos) == (source_start - 1))) {
2507 --source_start;
2508 --pos;
2509 }
2510 ok = ok && d->model->removeColumns(source_start, source_end - source_start + 1,
2511 source_parent);
2512 }
2513 return ok;
2514 }
2515
2516 /*!
2517 \reimp
2518 */
fetchMore(const QModelIndex & parent)2519 void QSortFilterProxyModel::fetchMore(const QModelIndex &parent)
2520 {
2521 Q_D(QSortFilterProxyModel);
2522 QModelIndex source_parent;
2523 if (d->indexValid(parent))
2524 source_parent = mapToSource(parent);
2525 d->model->fetchMore(source_parent);
2526 }
2527
2528 /*!
2529 \reimp
2530 */
canFetchMore(const QModelIndex & parent) const2531 bool QSortFilterProxyModel::canFetchMore(const QModelIndex &parent) const
2532 {
2533 Q_D(const QSortFilterProxyModel);
2534 QModelIndex source_parent;
2535 if (d->indexValid(parent))
2536 source_parent = mapToSource(parent);
2537 return d->model->canFetchMore(source_parent);
2538 }
2539
2540 /*!
2541 \reimp
2542 */
flags(const QModelIndex & index) const2543 Qt::ItemFlags QSortFilterProxyModel::flags(const QModelIndex &index) const
2544 {
2545 Q_D(const QSortFilterProxyModel);
2546 QModelIndex source_index;
2547 if (d->indexValid(index))
2548 source_index = mapToSource(index);
2549 return d->model->flags(source_index);
2550 }
2551
2552 /*!
2553 \reimp
2554 */
buddy(const QModelIndex & index) const2555 QModelIndex QSortFilterProxyModel::buddy(const QModelIndex &index) const
2556 {
2557 Q_D(const QSortFilterProxyModel);
2558 if (!d->indexValid(index))
2559 return QModelIndex();
2560 QModelIndex source_index = mapToSource(index);
2561 QModelIndex source_buddy = d->model->buddy(source_index);
2562 if (source_index == source_buddy)
2563 return index;
2564 return mapFromSource(source_buddy);
2565 }
2566
2567 /*!
2568 \reimp
2569 */
match(const QModelIndex & start,int role,const QVariant & value,int hits,Qt::MatchFlags flags) const2570 QModelIndexList QSortFilterProxyModel::match(const QModelIndex &start, int role,
2571 const QVariant &value, int hits,
2572 Qt::MatchFlags flags) const
2573 {
2574 return QAbstractProxyModel::match(start, role, value, hits, flags);
2575 }
2576
2577 /*!
2578 \reimp
2579 */
span(const QModelIndex & index) const2580 QSize QSortFilterProxyModel::span(const QModelIndex &index) const
2581 {
2582 Q_D(const QSortFilterProxyModel);
2583 QModelIndex source_index = mapToSource(index);
2584 if (index.isValid() && !source_index.isValid())
2585 return QSize();
2586 return d->model->span(source_index);
2587 }
2588
2589 /*!
2590 \reimp
2591 */
sort(int column,Qt::SortOrder order)2592 void QSortFilterProxyModel::sort(int column, Qt::SortOrder order)
2593 {
2594 Q_D(QSortFilterProxyModel);
2595 if (d->dynamic_sortfilter && d->proxy_sort_column == column && d->sort_order == order)
2596 return;
2597 d->sort_order = order;
2598 d->proxy_sort_column = column;
2599 d->update_source_sort_column();
2600 d->sort();
2601 }
2602
2603 /*!
2604 \since 4.5
2605 \brief the column currently used for sorting
2606
2607 This returns the most recently used sort column.
2608 */
sortColumn() const2609 int QSortFilterProxyModel::sortColumn() const
2610 {
2611 Q_D(const QSortFilterProxyModel);
2612 return d->proxy_sort_column;
2613 }
2614
2615 /*!
2616 \since 4.5
2617 \brief the order currently used for sorting
2618
2619 This returns the most recently used sort order.
2620 */
sortOrder() const2621 Qt::SortOrder QSortFilterProxyModel::sortOrder() const
2622 {
2623 Q_D(const QSortFilterProxyModel);
2624 return d->sort_order;
2625 }
2626
2627 /*!
2628 \property QSortFilterProxyModel::filterRegExp
2629 \brief the QRegExp used to filter the contents of the source model
2630
2631 Setting this property overwrites the current
2632 \l{QSortFilterProxyModel::filterCaseSensitivity}{filterCaseSensitivity}.
2633 By default, the QRegExp is an empty string matching all contents.
2634
2635 If no QRegExp or an empty string is set, everything in the source model
2636 will be accepted.
2637
2638 \sa filterCaseSensitivity, setFilterWildcard(), setFilterFixedString()
2639 */
filterRegExp() const2640 QRegExp QSortFilterProxyModel::filterRegExp() const
2641 {
2642 Q_D(const QSortFilterProxyModel);
2643 return d->filter_data.regExp();
2644 }
2645
setFilterRegExp(const QRegExp & regExp)2646 void QSortFilterProxyModel::setFilterRegExp(const QRegExp ®Exp)
2647 {
2648 Q_D(QSortFilterProxyModel);
2649 d->filter_about_to_be_changed();
2650 d->filter_data.setRegExp(regExp);
2651 d->filter_changed();
2652 }
2653
2654 #if QT_CONFIG(regularexpression)
2655 /*!
2656 \since 5.12
2657 \property QSortFilterProxyModel::filterRegularExpression
2658 \brief the QRegularExpression used to filter the contents of the source model
2659
2660 Setting this property overwrites the current
2661 \l{QSortFilterProxyModel::filterCaseSensitivity}{filterCaseSensitivity}.
2662 By default, the QRegularExpression is an empty string matching all contents.
2663
2664 If no QRegularExpression or an empty string is set, everything in the source
2665 model will be accepted.
2666
2667 \sa filterCaseSensitivity, setFilterWildcard(), setFilterFixedString()
2668 */
filterRegularExpression() const2669 QRegularExpression QSortFilterProxyModel::filterRegularExpression() const
2670 {
2671 Q_D(const QSortFilterProxyModel);
2672 return d->filter_data.regularExpression();
2673 }
2674
setFilterRegularExpression(const QRegularExpression & regularExpression)2675 void QSortFilterProxyModel::setFilterRegularExpression(const QRegularExpression ®ularExpression)
2676 {
2677 Q_D(QSortFilterProxyModel);
2678 d->filter_about_to_be_changed();
2679 d->filter_data.setRegularExpression(regularExpression);
2680 d->filter_changed();
2681 }
2682 #endif
2683
2684 /*!
2685 \property QSortFilterProxyModel::filterKeyColumn
2686 \brief the column where the key used to filter the contents of the
2687 source model is read from.
2688
2689 The default value is 0. If the value is -1, the keys will be read
2690 from all columns.
2691 */
filterKeyColumn() const2692 int QSortFilterProxyModel::filterKeyColumn() const
2693 {
2694 Q_D(const QSortFilterProxyModel);
2695 return d->filter_column;
2696 }
2697
setFilterKeyColumn(int column)2698 void QSortFilterProxyModel::setFilterKeyColumn(int column)
2699 {
2700 Q_D(QSortFilterProxyModel);
2701 d->filter_about_to_be_changed();
2702 d->filter_column = column;
2703 d->filter_changed();
2704 }
2705
2706 /*!
2707 \property QSortFilterProxyModel::filterCaseSensitivity
2708
2709 \brief the case sensitivity of the QRegExp pattern used to filter the
2710 contents of the source model.
2711
2712 By default, the filter is case sensitive.
2713
2714 \sa filterRegExp, sortCaseSensitivity
2715 */
2716
2717 /*!
2718 \since 5.15
2719 \fn void QSortFilterProxyModel::filterCaseSensitivityChanged(Qt::CaseSensitivity filterCaseSensitivity)
2720 \brief This signal is emitted when the case sensitivity of the filter
2721 changes to \a filterCaseSensitivity.
2722 */
filterCaseSensitivity() const2723 Qt::CaseSensitivity QSortFilterProxyModel::filterCaseSensitivity() const
2724 {
2725 Q_D(const QSortFilterProxyModel);
2726 return d->filter_data.caseSensitivity();
2727 }
2728
setFilterCaseSensitivity(Qt::CaseSensitivity cs)2729 void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs)
2730 {
2731 Q_D(QSortFilterProxyModel);
2732 if (cs == d->filter_data.caseSensitivity())
2733 return;
2734 d->filter_about_to_be_changed();
2735 d->filter_data.setCaseSensitivity(cs);
2736 d->filter_changed();
2737 emit filterCaseSensitivityChanged(cs);
2738 }
2739
2740 /*!
2741 \since 4.2
2742 \property QSortFilterProxyModel::sortCaseSensitivity
2743 \brief the case sensitivity setting used for comparing strings when sorting
2744
2745 By default, sorting is case sensitive.
2746
2747 \sa filterCaseSensitivity, lessThan()
2748 */
2749
2750 /*!
2751 \since 5.15
2752 \fn void QSortFilterProxyModel::sortCaseSensitivityChanged(Qt::CaseSensitivity sortCaseSensitivity)
2753 \brief This signal is emitted when the case sensitivity for sorting
2754 changes to \a sortCaseSensitivity.
2755 */
sortCaseSensitivity() const2756 Qt::CaseSensitivity QSortFilterProxyModel::sortCaseSensitivity() const
2757 {
2758 Q_D(const QSortFilterProxyModel);
2759 return d->sort_casesensitivity;
2760 }
2761
setSortCaseSensitivity(Qt::CaseSensitivity cs)2762 void QSortFilterProxyModel::setSortCaseSensitivity(Qt::CaseSensitivity cs)
2763 {
2764 Q_D(QSortFilterProxyModel);
2765 if (d->sort_casesensitivity == cs)
2766 return;
2767
2768 d->sort_casesensitivity = cs;
2769 d->sort();
2770 emit sortCaseSensitivityChanged(cs);
2771 }
2772
2773 /*!
2774 \since 4.3
2775 \property QSortFilterProxyModel::isSortLocaleAware
2776 \brief the local aware setting used for comparing strings when sorting
2777
2778 By default, sorting is not local aware.
2779
2780 \sa sortCaseSensitivity, lessThan()
2781 */
2782
2783 /*!
2784 \since 5.15
2785 \fn void QSortFilterProxyModel::sortLocaleAwareChanged(bool sortLocaleAware)
2786 \brief This signal is emitted when the locale aware setting
2787 changes to \a sortLocaleAware.
2788 */
isSortLocaleAware() const2789 bool QSortFilterProxyModel::isSortLocaleAware() const
2790 {
2791 Q_D(const QSortFilterProxyModel);
2792 return d->sort_localeaware;
2793 }
2794
setSortLocaleAware(bool on)2795 void QSortFilterProxyModel::setSortLocaleAware(bool on)
2796 {
2797 Q_D(QSortFilterProxyModel);
2798 if (d->sort_localeaware == on)
2799 return;
2800
2801 d->sort_localeaware = on;
2802 d->sort();
2803 emit sortLocaleAwareChanged(on);
2804 }
2805
2806 /*!
2807 \overload
2808
2809 Sets the regular expression used to filter the contents
2810 of the source model to \a pattern.
2811
2812 \sa setFilterCaseSensitivity(), setFilterWildcard(), setFilterFixedString(), filterRegExp()
2813 */
setFilterRegExp(const QString & pattern)2814 void QSortFilterProxyModel::setFilterRegExp(const QString &pattern)
2815 {
2816 Q_D(QSortFilterProxyModel);
2817 d->filter_about_to_be_changed();
2818 QRegExp rx(pattern);
2819 rx.setCaseSensitivity(d->filter_data.caseSensitivity());
2820 d->filter_data.setRegExp(rx);
2821 d->filter_changed();
2822 }
2823
2824 #if QT_CONFIG(regularexpression)
2825 /*!
2826 \since 5.12
2827
2828 Sets the regular expression used to filter the contents
2829 of the source model to \a pattern.
2830
2831 This method should be preferred for new code as it will use
2832 QRegularExpression internally.
2833
2834 \sa setFilterCaseSensitivity(), setFilterWildcard(), setFilterFixedString(), filterRegularExpression()
2835 */
setFilterRegularExpression(const QString & pattern)2836 void QSortFilterProxyModel::setFilterRegularExpression(const QString &pattern)
2837 {
2838 Q_D(QSortFilterProxyModel);
2839 d->filter_about_to_be_changed();
2840 QRegularExpression rx(pattern,
2841 d->filter_data.caseSensitivity()
2842 ? QRegularExpression::NoPatternOption
2843 : QRegularExpression::CaseInsensitiveOption);
2844 d->filter_data.setRegularExpression(rx);
2845 d->filter_changed();
2846 }
2847 #endif
2848
2849 /*!
2850 Sets the wildcard expression used to filter the contents
2851 of the source model to the given \a pattern.
2852
2853 \sa setFilterCaseSensitivity(), setFilterRegExp(), setFilterFixedString(), filterRegExp()
2854 */
setFilterWildcard(const QString & pattern)2855 void QSortFilterProxyModel::setFilterWildcard(const QString &pattern)
2856 {
2857 Q_D(QSortFilterProxyModel);
2858 d->filter_about_to_be_changed();
2859 QRegExp rx(pattern, d->filter_data.caseSensitivity(), QRegExp::Wildcard);
2860 d->filter_data.setRegExp(rx);
2861 d->filter_changed();
2862 }
2863
2864 /*!
2865 Sets the fixed string used to filter the contents
2866 of the source model to the given \a pattern.
2867
2868 \sa setFilterCaseSensitivity(), setFilterRegExp(), setFilterWildcard(), filterRegExp()
2869 */
setFilterFixedString(const QString & pattern)2870 void QSortFilterProxyModel::setFilterFixedString(const QString &pattern)
2871 {
2872 Q_D(QSortFilterProxyModel);
2873 d->filter_about_to_be_changed();
2874 QRegExp rx(pattern, d->filter_data.caseSensitivity(), QRegExp::FixedString);
2875 d->filter_data.setRegExp(rx);
2876 d->filter_changed();
2877 }
2878
2879 /*!
2880 \since 4.2
2881 \property QSortFilterProxyModel::dynamicSortFilter
2882 \brief whether the proxy model is dynamically sorted and filtered
2883 whenever the contents of the source model change
2884
2885 Note that you should not update the source model through the proxy
2886 model when dynamicSortFilter is true. For instance, if you set the
2887 proxy model on a QComboBox, then using functions that update the
2888 model, e.g., \l{QComboBox::}{addItem()}, will not work as
2889 expected. An alternative is to set dynamicSortFilter to false and
2890 call \l{QSortFilterProxyModel::}{sort()} after adding items to the
2891 QComboBox.
2892
2893 The default value is true.
2894 */
dynamicSortFilter() const2895 bool QSortFilterProxyModel::dynamicSortFilter() const
2896 {
2897 Q_D(const QSortFilterProxyModel);
2898 return d->dynamic_sortfilter;
2899 }
2900
setDynamicSortFilter(bool enable)2901 void QSortFilterProxyModel::setDynamicSortFilter(bool enable)
2902 {
2903 Q_D(QSortFilterProxyModel);
2904 d->dynamic_sortfilter = enable;
2905 if (enable)
2906 d->sort();
2907 }
2908
2909 /*!
2910 \since 4.2
2911 \property QSortFilterProxyModel::sortRole
2912 \brief the item role that is used to query the source model's data when
2913 sorting items.
2914
2915 The default value is Qt::DisplayRole.
2916
2917 \sa lessThan()
2918 */
2919
2920 /*!
2921 \since 5.15
2922 \fn void QSortFilterProxyModel::sortRoleChanged(int sortRole)
2923 \brief This signal is emitted when the sort role changes to \a sortRole.
2924 */
sortRole() const2925 int QSortFilterProxyModel::sortRole() const
2926 {
2927 Q_D(const QSortFilterProxyModel);
2928 return d->sort_role;
2929 }
2930
setSortRole(int role)2931 void QSortFilterProxyModel::setSortRole(int role)
2932 {
2933 Q_D(QSortFilterProxyModel);
2934 if (d->sort_role == role)
2935 return;
2936 d->sort_role = role;
2937 d->sort();
2938 emit sortRoleChanged(role);
2939 }
2940
2941 /*!
2942 \since 4.2
2943 \property QSortFilterProxyModel::filterRole
2944 \brief the item role that is used to query the source model's data when
2945 filtering items.
2946
2947 The default value is Qt::DisplayRole.
2948
2949 \sa filterAcceptsRow()
2950 */
2951
2952 /*!
2953 \since 5.15
2954 \fn void QSortFilterProxyModel::filterRoleChanged(int filterRole)
2955 \brief This signal is emitted when the filter role changes to \a filterRole.
2956 */
filterRole() const2957 int QSortFilterProxyModel::filterRole() const
2958 {
2959 Q_D(const QSortFilterProxyModel);
2960 return d->filter_role;
2961 }
2962
setFilterRole(int role)2963 void QSortFilterProxyModel::setFilterRole(int role)
2964 {
2965 Q_D(QSortFilterProxyModel);
2966 if (d->filter_role == role)
2967 return;
2968 d->filter_about_to_be_changed();
2969 d->filter_role = role;
2970 d->filter_changed();
2971 emit filterRoleChanged(role);
2972 }
2973
2974 /*!
2975 \since 5.10
2976 \property QSortFilterProxyModel::recursiveFilteringEnabled
2977 \brief whether the filter to be applied recursively on children, and for
2978 any matching child, its parents will be visible as well.
2979
2980 The default value is false.
2981
2982 \sa filterAcceptsRow()
2983 */
2984
2985 /*!
2986 \since 5.15
2987 \fn void QSortFilterProxyModel::recursiveFilteringEnabledChanged(bool recursiveFilteringEnabled)
2988 \brief This signal is emitted when the recursive filter setting is changed
2989 to \a recursiveFilteringEnabled.
2990 */
isRecursiveFilteringEnabled() const2991 bool QSortFilterProxyModel::isRecursiveFilteringEnabled() const
2992 {
2993 Q_D(const QSortFilterProxyModel);
2994 return d->filter_recursive;
2995 }
2996
setRecursiveFilteringEnabled(bool recursive)2997 void QSortFilterProxyModel::setRecursiveFilteringEnabled(bool recursive)
2998 {
2999 Q_D(QSortFilterProxyModel);
3000 if (d->filter_recursive == recursive)
3001 return;
3002 d->filter_about_to_be_changed();
3003 d->filter_recursive = recursive;
3004 d->filter_changed();
3005 emit recursiveFilteringEnabledChanged(recursive);
3006 }
3007
3008 #if QT_DEPRECATED_SINCE(5, 11)
3009 /*!
3010 \obsolete
3011
3012 This function is obsolete. Use invalidate() instead.
3013 */
clear()3014 void QSortFilterProxyModel::clear()
3015 {
3016 invalidate();
3017 }
3018 #endif
3019 /*!
3020 \since 4.3
3021
3022 Invalidates the current sorting and filtering.
3023
3024 \sa invalidateFilter()
3025 */
invalidate()3026 void QSortFilterProxyModel::invalidate()
3027 {
3028 Q_D(QSortFilterProxyModel);
3029 emit layoutAboutToBeChanged();
3030 d->_q_clearMapping();
3031 emit layoutChanged();
3032 }
3033
3034 #if QT_DEPRECATED_SINCE(5, 11)
3035 /*!
3036 \obsolete
3037
3038 This function is obsolete. Use invalidateFilter() instead.
3039 */
filterChanged()3040 void QSortFilterProxyModel::filterChanged()
3041 {
3042 invalidateFilter();
3043 }
3044 #endif
3045
3046 /*!
3047 \since 4.3
3048
3049 Invalidates the current filtering.
3050
3051 This function should be called if you are implementing custom filtering
3052 (e.g. filterAcceptsRow()), and your filter parameters have changed.
3053
3054 \sa invalidate()
3055 */
invalidateFilter()3056 void QSortFilterProxyModel::invalidateFilter()
3057 {
3058 Q_D(QSortFilterProxyModel);
3059 d->filter_changed();
3060 }
3061
3062 /*!
3063 Returns \c true if the value of the item referred to by the given
3064 index \a source_left is less than the value of the item referred to by
3065 the given index \a source_right, otherwise returns \c false.
3066
3067 This function is used as the < operator when sorting, and handles
3068 the following QVariant types:
3069
3070 \list
3071 \li QMetaType::Int
3072 \li QMetaType::UInt
3073 \li QMetaType::LongLong
3074 \li QMetaType::ULongLong
3075 \li QMetaType::Float
3076 \li QMetaType::Double
3077 \li QMetaType::QChar
3078 \li QMetaType::QDate
3079 \li QMetaType::QTime
3080 \li QMetaType::QDateTime
3081 \li QMetaType::QString
3082 \endlist
3083
3084 Any other type will be converted to a QString using
3085 QVariant::toString().
3086
3087 Comparison of \l{QString}s is case sensitive by default; this can
3088 be changed using the \l {QSortFilterProxyModel::sortCaseSensitivity}
3089 {sortCaseSensitivity} property.
3090
3091 By default, the Qt::DisplayRole associated with the
3092 \l{QModelIndex}es is used for comparisons. This can be changed by
3093 setting the \l {QSortFilterProxyModel::sortRole} {sortRole} property.
3094
3095 \note The indices passed in correspond to the source model.
3096
3097 \sa sortRole, sortCaseSensitivity, dynamicSortFilter
3098 */
lessThan(const QModelIndex & source_left,const QModelIndex & source_right) const3099 bool QSortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
3100 {
3101 Q_D(const QSortFilterProxyModel);
3102 QVariant l = (source_left.model() ? source_left.model()->data(source_left, d->sort_role) : QVariant());
3103 QVariant r = (source_right.model() ? source_right.model()->data(source_right, d->sort_role) : QVariant());
3104 return QAbstractItemModelPrivate::isVariantLessThan(l, r, d->sort_casesensitivity, d->sort_localeaware);
3105 }
3106
3107 /*!
3108 Returns \c true if the item in the row indicated by the given \a source_row
3109 and \a source_parent should be included in the model; otherwise returns
3110 false.
3111
3112 The default implementation returns \c true if the value held by the relevant item
3113 matches the filter string, wildcard string or regular expression.
3114
3115 \note By default, the Qt::DisplayRole is used to determine if the row
3116 should be accepted or not. This can be changed by setting the
3117 \l{QSortFilterProxyModel::filterRole}{filterRole} property.
3118
3119 \sa filterAcceptsColumn(), setFilterFixedString(), setFilterRegExp(), setFilterWildcard()
3120 */
filterAcceptsRow(int source_row,const QModelIndex & source_parent) const3121 bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
3122 {
3123 Q_D(const QSortFilterProxyModel);
3124
3125 if (d->filter_data.isEmpty())
3126 return true;
3127
3128 int column_count = d->model->columnCount(source_parent);
3129 if (d->filter_column == -1) {
3130 for (int column = 0; column < column_count; ++column) {
3131 QModelIndex source_index = d->model->index(source_row, column, source_parent);
3132 QString key = d->model->data(source_index, d->filter_role).toString();
3133 if (d->filter_data.hasMatch(key))
3134 return true;
3135 }
3136 return false;
3137 }
3138
3139 if (d->filter_column >= column_count) // the column may not exist
3140 return true;
3141 QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent);
3142 QString key = d->model->data(source_index, d->filter_role).toString();
3143 return d->filter_data.hasMatch(key);
3144 }
3145
3146 /*!
3147 Returns \c true if the item in the column indicated by the given \a source_column
3148 and \a source_parent should be included in the model; otherwise returns \c false.
3149
3150 \note The default implementation always returns \c true. You must reimplement this
3151 method to get the described behavior.
3152
3153 \sa filterAcceptsRow(), setFilterFixedString(), setFilterRegExp(), setFilterWildcard()
3154 */
filterAcceptsColumn(int source_column,const QModelIndex & source_parent) const3155 bool QSortFilterProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
3156 {
3157 Q_UNUSED(source_column);
3158 Q_UNUSED(source_parent);
3159 return true;
3160 }
3161
3162 /*!
3163 Returns the source model index corresponding to the given \a
3164 proxyIndex from the sorting filter model.
3165
3166 \sa mapFromSource()
3167 */
mapToSource(const QModelIndex & proxyIndex) const3168 QModelIndex QSortFilterProxyModel::mapToSource(const QModelIndex &proxyIndex) const
3169 {
3170 Q_D(const QSortFilterProxyModel);
3171 return d->proxy_to_source(proxyIndex);
3172 }
3173
3174 /*!
3175 Returns the model index in the QSortFilterProxyModel given the \a
3176 sourceIndex from the source model.
3177
3178 \sa mapToSource()
3179 */
mapFromSource(const QModelIndex & sourceIndex) const3180 QModelIndex QSortFilterProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
3181 {
3182 Q_D(const QSortFilterProxyModel);
3183 return d->source_to_proxy(sourceIndex);
3184 }
3185
3186 /*!
3187 \reimp
3188 */
mapSelectionToSource(const QItemSelection & proxySelection) const3189 QItemSelection QSortFilterProxyModel::mapSelectionToSource(const QItemSelection &proxySelection) const
3190 {
3191 return QAbstractProxyModel::mapSelectionToSource(proxySelection);
3192 }
3193
3194 /*!
3195 \reimp
3196 */
mapSelectionFromSource(const QItemSelection & sourceSelection) const3197 QItemSelection QSortFilterProxyModel::mapSelectionFromSource(const QItemSelection &sourceSelection) const
3198 {
3199 return QAbstractProxyModel::mapSelectionFromSource(sourceSelection);
3200 }
3201
3202 QT_END_NAMESPACE
3203
3204 #include "moc_qsortfilterproxymodel.cpp"
3205