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
52QT_BEGIN_NAMESPACE
53
54typedef QVector<QPair<QModelIndex, QPersistentModelIndex> > QModelIndexPairList;
55
56struct QSortFilterProxyModelDataChanged
57{
58 QSortFilterProxyModelDataChanged(const QModelIndex &tl, const QModelIndex &br)
59 : topLeft(tl), bottomRight(br) { }
60
61 QModelIndex topLeft;
62 QModelIndex bottomRight;
63};
64
65static inline QSet<int> qVectorToSet(const QVector<int> &vector)
66{
67 return {vector.begin(), vector.end()};
68}
69
70class QSortFilterProxyModelLessThan
71{
72public:
73 inline QSortFilterProxyModelLessThan(int column, const QModelIndex &parent,
74 const QAbstractItemModel *source,
75 const QSortFilterProxyModel *proxy)
76 : sort_column(column), source_parent(parent), source_model(source), proxy_model(proxy) {}
77
78 inline bool operator()(int r1, int r2) const
79 {
80 QModelIndex i1 = source_model->index(row: r1, column: sort_column, parent: source_parent);
81 QModelIndex i2 = source_model->index(row: r2, column: sort_column, parent: source_parent);
82 return proxy_model->lessThan(source_left: i1, source_right: i2);
83 }
84
85private:
86 int sort_column;
87 QModelIndex source_parent;
88 const QAbstractItemModel *source_model;
89 const QSortFilterProxyModel *proxy_model;
90};
91
92class QSortFilterProxyModelGreaterThan
93{
94public:
95 inline QSortFilterProxyModelGreaterThan(int column, const QModelIndex &parent,
96 const QAbstractItemModel *source,
97 const QSortFilterProxyModel *proxy)
98 : sort_column(column), source_parent(parent),
99 source_model(source), proxy_model(proxy) {}
100
101 inline bool operator()(int r1, int r2) const
102 {
103 QModelIndex i1 = source_model->index(row: r1, column: sort_column, parent: source_parent);
104 QModelIndex i2 = source_model->index(row: r2, column: sort_column, parent: source_parent);
105 return proxy_model->lessThan(source_left: i2, source_right: i1);
106 }
107
108private:
109 int sort_column;
110 QModelIndex source_parent;
111 const QAbstractItemModel *source_model;
112 const QSortFilterProxyModel *proxy_model;
113};
114
115
116//this struct is used to store what are the rows that are removed
117//between a call to rowsAboutToBeRemoved and rowsRemoved
118//it avoids readding rows to the mapping that are currently being removed
119struct QRowsRemoval
120{
121 QRowsRemoval(const QModelIndex &parent_source, int start, int end) : parent_source(parent_source), start(start), end(end)
122 {
123 }
124
125 QRowsRemoval() : start(-1), end(-1)
126 {
127 }
128
129 bool contains(QModelIndex parent, int row) 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 }
139private:
140 QModelIndex parent_source;
141 int start;
142 int end;
143};
144
145class RegularExpressionData {
146
147private:
148 enum class ExpressionType {
149 RegExp,
150#if QT_CONFIG(regularexpression)
151 RegularExpression
152#endif
153 };
154
155public:
156 RegularExpressionData() :
157 m_type(ExpressionType::RegExp)
158 {}
159
160#if QT_CONFIG(regularexpression)
161 QRegularExpression regularExpression() const
162 {
163 if (m_type == ExpressionType::RegularExpression)
164 return m_regularExpression;
165 return QRegularExpression();
166 }
167
168 void setRegularExpression(const QRegularExpression &rx)
169 {
170 m_type = ExpressionType::RegularExpression;
171 m_regularExpression = rx;
172 m_regExp = QRegExp();
173 }
174#endif
175
176 QRegExp regExp() const
177 {
178 if (m_type == ExpressionType::RegExp)
179 return m_regExp;
180 return QRegExp();
181 }
182
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
193 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
209 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
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(flag: QRegularExpression::CaseInsensitiveOption, on: cs == Qt::CaseInsensitive);
240 m_regularExpression.setPatternOptions(options);
241 }
242 break;
243#endif
244 }
245 }
246
247 bool hasMatch(const QString &str) const
248 {
249 bool result = false;
250 switch (m_type) {
251 case ExpressionType::RegExp:
252 result = str.contains(rx: m_regExp);
253 break;
254#if QT_CONFIG(regularexpression)
255 case ExpressionType::RegularExpression:
256 result = str.contains(re: m_regularExpression);
257 break;
258#endif
259 }
260 return result;
261 }
262
263private:
264 ExpressionType m_type;
265 QRegExp m_regExp;
266#if QT_CONFIG(regularexpression)
267 QRegularExpression m_regularExpression;
268#endif
269};
270
271
272class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate
273{
274 Q_DECLARE_PUBLIC(QSortFilterProxyModel)
275
276public:
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
318 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
332 inline QModelIndex create_index(int row, int column,
333 QHash<QModelIndex, Mapping*>::const_iterator it) const
334 {
335 return q_func()->createIndex(arow: row, acolumn: column, adata: *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
434typedef QHash<QModelIndex, QSortFilterProxyModelPrivate::Mapping *> IndexMap;
435
436void QSortFilterProxyModelPrivate::_q_sourceModelDestroyed()
437{
438 QAbstractProxyModelPrivate::_q_sourceModelDestroyed();
439 qDeleteAll(c: source_index_mapping);
440 source_index_mapping.clear();
441}
442
443bool 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
451bool 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(row: source_row, column: 0, parent: source_parent);
459 const int count = model->rowCount(parent: index);
460
461 for (int i = 0; i < count; ++i) {
462 if (filterRecursiveAcceptsRow(source_row: i, source_parent: index))
463 return true;
464 }
465
466 return false;
467}
468
469void QSortFilterProxyModelPrivate::remove_from_mapping(const QModelIndex &source_parent)
470{
471 if (Mapping *m = source_index_mapping.take(akey: source_parent)) {
472 for (const QModelIndex &mappedIdx : qAsConst(t&: m->mapped_children))
473 remove_from_mapping(source_parent: mappedIdx);
474 delete m;
475 }
476}
477
478void QSortFilterProxyModelPrivate::_q_clearMapping()
479{
480 // store the persistent indexes
481 QModelIndexPairList source_indexes = store_persistent_indexes();
482
483 qDeleteAll(c: 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
492IndexMap::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(akey: 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(parent: source_parent);
504 m->source_rows.reserve(asize: source_rows);
505 for (int i = 0; i < source_rows; ++i) {
506 if (filterAcceptsRowInternal(source_row: i, source_parent))
507 m->source_rows.append(t: i);
508 }
509 int source_cols = model->columnCount(parent: source_parent);
510 m->source_columns.reserve(asize: source_cols);
511 for (int i = 0; i < source_cols; ++i) {
512 if (q->filterAcceptsColumn(source_column: i, source_parent))
513 m->source_columns.append(t: i);
514 }
515
516 sort_source_rows(source_rows&: m->source_rows, source_parent);
517 m->proxy_rows.resize(asize: source_rows);
518 build_source_to_proxy_mapping(proxy_to_source: m->source_rows, source_to_proxy&: m->proxy_rows);
519 m->proxy_columns.resize(asize: source_cols);
520 build_source_to_proxy_mapping(proxy_to_source: m->source_columns, source_to_proxy&: m->proxy_columns);
521
522 it = IndexMap::const_iterator(source_index_mapping.insert(akey: source_parent, avalue: 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_parent: source_grand_parent);
528 Q_ASSERT(it2 != source_index_mapping.constEnd());
529 it2.value()->mapped_children.append(t: 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
539IndexMap::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(akey: source_grand_parent);
544 IndexMap::const_iterator end = source_index_mapping.constEnd();
545 if (it == end) {
546 it = create_mapping_recursive(source_parent: 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(i: source_parent.row()) == -1 ||
553 gm->proxy_columns.at(i: source_parent.column()) == -1) {
554 // Can't do, parent is filtered
555 return end;
556 }
557 }
558 return create_mapping(source_parent);
559}
560
561QModelIndex 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(msg: "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(i: proxy_index.row());
575 int source_col = m->source_columns.at(i: proxy_index.column());
576 return model->index(row: source_row, column: source_col, parent: it.key());
577}
578
579QModelIndex 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(msg: "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(i: source_index.row());
594 int proxy_column = m->proxy_columns.at(i: source_index.column());
595 if (proxy_row == -1 || proxy_column == -1)
596 return QModelIndex();
597 return create_index(row: proxy_row, column: proxy_column, it);
598}
599
600bool 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(akey: 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(i: source_parent.row()) == -1 ||
611 gm->proxy_columns.at(i: 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*/
624void QSortFilterProxyModelPrivate::sort()
625{
626 Q_Q(QSortFilterProxyModel);
627 emit q->layoutAboutToBeChanged(parents: QList<QPersistentModelIndex>(), hint: 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(source_rows&: m->source_rows, source_parent);
634 build_source_to_proxy_mapping(proxy_to_source: m->source_rows, source_to_proxy&: m->proxy_rows);
635 }
636 update_persistent_indexes(source_indexes);
637 emit q->layoutChanged(parents: QList<QPersistentModelIndex>(), hint: 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*/
646bool 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(source_parent: QModelIndex()).value();
657 if (proxy_sort_column < m->source_columns.size())
658 source_sort_column = m->source_columns.at(i: 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*/
672int 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(source_column: i, source_parent: 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*/
697void 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(first: source_rows.begin(), last: source_rows.end(), comp: lt);
705 } else {
706 QSortFilterProxyModelGreaterThan gt(source_sort_column, source_parent, model, q);
707 std::stable_sort(first: source_rows.begin(), last: source_rows.end(), comp: gt);
708 }
709 } else { // restore the source model order
710 std::stable_sort(first: source_rows.begin(), last: 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*/
725QVector<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(i: source_items.at(i: 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(i: source_items.at(i: 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(t: QPair<int, int>(first_proxy_item, last_proxy_item));
746 }
747 std::stable_sort(first: proxy_intervals.begin(), last: 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 abegin: std::remove_if(first: proxy_intervals.begin(), last: proxy_intervals.end(),
759 pred: [](QPair<int, int> &interval) { return interval.first < 0; }),
760 aend: 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*/
772void 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(sourceIndex: 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*/
804void 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(parent: proxy_parent, first: proxy_start, last: proxy_end);
812 else
813 q->beginRemoveColumns(parent: proxy_parent, first: proxy_start, last: proxy_end);
814 }
815
816 // Remove items from proxy-to-source mapping
817 proxy_to_source.remove(i: proxy_start, n: 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*/
841QVector<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(i: source_items_index);
858 source_items_in_interval.append(t: 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(row: first_new_source_item, column: source_sort_column, parent: 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(row: proxy_to_source.at(i: proxy_item), column: source_sort_column, parent: source_parent);
868 if ((sort_order == Qt::AscendingOrder) ? q->lessThan(source_left: i1, source_right: i2) : q->lessThan(source_left: i2, source_right: 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(i: 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(t: source_items.at(i: source_items_index));
885 } else {
886 i1 = compare ? model->index(row: proxy_to_source.at(i: proxy_item), column: source_sort_column, parent: source_parent) : QModelIndex();
887 for ( ; source_items_index < source_items.size(); ++source_items_index) {
888 int new_source_item = source_items.at(i: source_items_index);
889 if (compare) {
890 QModelIndex i2 = model->index(row: new_source_item, column: source_sort_column, parent: source_parent);
891 if ((sort_order == Qt::AscendingOrder) ? q->lessThan(source_left: i1, source_right: i2) : q->lessThan(source_left: i2, source_right: i1))
892 break;
893 } else {
894 if (proxy_to_source.at(i: proxy_item) < new_source_item)
895 break;
896 }
897 source_items_in_interval.append(t: new_source_item);
898 }
899 }
900
901 // Add interval to result
902 proxy_intervals.append(t: 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*/
915void 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(sourceIndex: 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(parent: proxy_parent, first: proxy_start, last: proxy_end);
938 else
939 q->beginInsertColumns(parent: proxy_parent, first: proxy_start, last: proxy_end);
940 }
941
942 for (int i = 0; i < source_items.size(); ++i)
943 proxy_to_source.insert(i: proxy_start + i, t: source_items.at(i));
944
945 build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
946
947 if (emit_signal) {
948 if (orient == Qt::Vertical)
949 q->endInsertRows();
950 else
951 q->endInsertColumns();
952 }
953 }
954}
955
956/*!
957 \internal
958
959 Handles source model items insertion (columnsInserted(), rowsInserted()).
960 Determines
961 1) which of the inserted items to also insert into proxy model (filtering),
962 2) where to insert the items into the proxy model (sorting),
963 then inserts those items.
964 The items are inserted into the proxy model in intervals (based on
965 sorted order), so that the proper rows/columnsInserted(start, end)
966 signals will be generated.
967*/
968void QSortFilterProxyModelPrivate::source_items_inserted(
969 const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
970{
971 Q_Q(QSortFilterProxyModel);
972 if ((start < 0) || (end < 0))
973 return;
974 IndexMap::const_iterator it = source_index_mapping.constFind(akey: source_parent);
975 if (it == source_index_mapping.constEnd()) {
976 if (!can_create_mapping(source_parent))
977 return;
978 it = create_mapping(source_parent);
979 Mapping *m = it.value();
980 QModelIndex proxy_parent = q->mapFromSource(sourceIndex: source_parent);
981 if (m->source_rows.count() > 0) {
982 q->beginInsertRows(parent: proxy_parent, first: 0, last: m->source_rows.count() - 1);
983 q->endInsertRows();
984 }
985 if (m->source_columns.count() > 0) {
986 q->beginInsertColumns(parent: proxy_parent, first: 0, last: m->source_columns.count() - 1);
987 q->endInsertColumns();
988 }
989 return;
990 }
991
992 Mapping *m = it.value();
993 QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
994 QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
995
996 int delta_item_count = end - start + 1;
997 int old_item_count = source_to_proxy.size();
998
999 updateChildrenMapping(source_parent, parent_mapping: m, orient, start, end, delta_item_count, remove: false);
1000
1001 // Expand source-to-proxy mapping to account for new items
1002 if (start < 0 || start > source_to_proxy.size()) {
1003 qWarning(msg: "QSortFilterProxyModel: invalid inserted rows reported by source model");
1004 remove_from_mapping(source_parent);
1005 return;
1006 }
1007 source_to_proxy.insert(i: start, n: delta_item_count, t: -1);
1008
1009 if (start < old_item_count) {
1010 // Adjust existing "stale" indexes in proxy-to-source mapping
1011 int proxy_count = proxy_to_source.size();
1012 for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
1013 int source_item = proxy_to_source.at(i: proxy_item);
1014 if (source_item >= start)
1015 proxy_to_source.replace(i: proxy_item, t: source_item + delta_item_count);
1016 }
1017 build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
1018 }
1019
1020 // Figure out which items to add to mapping based on filter
1021 QVector<int> source_items;
1022 for (int i = start; i <= end; ++i) {
1023 if ((orient == Qt::Vertical)
1024 ? filterAcceptsRowInternal(source_row: i, source_parent)
1025 : q->filterAcceptsColumn(source_column: i, source_parent)) {
1026 source_items.append(t: i);
1027 }
1028 }
1029
1030 if (model->rowCount(parent: source_parent) == delta_item_count) {
1031 // Items were inserted where there were none before.
1032 // If it was new rows make sure to create mappings for columns so that a
1033 // valid mapping can be retrieved later and vice-versa.
1034
1035 QVector<int> &orthogonal_proxy_to_source = (orient == Qt::Horizontal) ? m->source_rows : m->source_columns;
1036 QVector<int> &orthogonal_source_to_proxy = (orient == Qt::Horizontal) ? m->proxy_rows : m->proxy_columns;
1037
1038 if (orthogonal_source_to_proxy.isEmpty()) {
1039 const int ortho_end = (orient == Qt::Horizontal) ? model->rowCount(parent: source_parent) : model->columnCount(parent: source_parent);
1040
1041 orthogonal_source_to_proxy.resize(asize: ortho_end);
1042
1043 for (int ortho_item = 0; ortho_item < ortho_end; ++ortho_item) {
1044 if ((orient == Qt::Horizontal) ? filterAcceptsRowInternal(source_row: ortho_item, source_parent)
1045 : q->filterAcceptsColumn(source_column: ortho_item, source_parent)) {
1046 orthogonal_proxy_to_source.append(t: ortho_item);
1047 }
1048 }
1049 if (orient == Qt::Horizontal) {
1050 // We're reacting to columnsInserted, but we've just inserted new rows. Sort them.
1051 sort_source_rows(source_rows&: orthogonal_proxy_to_source, source_parent);
1052 }
1053 build_source_to_proxy_mapping(proxy_to_source: orthogonal_proxy_to_source, source_to_proxy&: orthogonal_source_to_proxy);
1054 }
1055 }
1056
1057 // Sort and insert the items
1058 if (orient == Qt::Vertical) // Only sort rows
1059 sort_source_rows(source_rows&: source_items, source_parent);
1060 insert_source_items(source_to_proxy, proxy_to_source, source_items, source_parent, orient);
1061}
1062
1063/*!
1064 \internal
1065
1066 Handles source model items removal
1067 (columnsAboutToBeRemoved(), rowsAboutToBeRemoved()).
1068*/
1069void QSortFilterProxyModelPrivate::source_items_about_to_be_removed(
1070 const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
1071{
1072 if ((start < 0) || (end < 0))
1073 return;
1074 IndexMap::const_iterator it = source_index_mapping.constFind(akey: source_parent);
1075 if (it == source_index_mapping.constEnd()) {
1076 // Don't care, since we don't have mapping for this index
1077 return;
1078 }
1079
1080 Mapping *m = it.value();
1081 QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
1082 QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
1083
1084 // figure out which items to remove
1085 QVector<int> source_items_to_remove;
1086 int proxy_count = proxy_to_source.size();
1087 for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
1088 int source_item = proxy_to_source.at(i: proxy_item);
1089 if ((source_item >= start) && (source_item <= end))
1090 source_items_to_remove.append(t: source_item);
1091 }
1092
1093 remove_source_items(source_to_proxy, proxy_to_source, source_items: source_items_to_remove,
1094 source_parent, orient);
1095}
1096
1097/*!
1098 \internal
1099
1100 Handles source model items removal (columnsRemoved(), rowsRemoved()).
1101*/
1102void QSortFilterProxyModelPrivate::source_items_removed(
1103 const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
1104{
1105 if ((start < 0) || (end < 0))
1106 return;
1107 IndexMap::const_iterator it = source_index_mapping.constFind(akey: source_parent);
1108 if (it == source_index_mapping.constEnd()) {
1109 // Don't care, since we don't have mapping for this index
1110 return;
1111 }
1112
1113 Mapping *m = it.value();
1114 QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
1115 QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
1116
1117 if (end >= source_to_proxy.size())
1118 end = source_to_proxy.size() - 1;
1119
1120 // Shrink the source-to-proxy mapping to reflect the new item count
1121 int delta_item_count = end - start + 1;
1122 source_to_proxy.remove(i: start, n: delta_item_count);
1123
1124 int proxy_count = proxy_to_source.size();
1125 if (proxy_count > source_to_proxy.size()) {
1126 // mapping is in an inconsistent state -- redo the whole mapping
1127 qWarning(msg: "QSortFilterProxyModel: inconsistent changes reported by source model");
1128 Q_Q(QSortFilterProxyModel);
1129 q->beginResetModel();
1130 remove_from_mapping(source_parent);
1131 q->endResetModel();
1132 return;
1133 }
1134
1135 // Adjust "stale" indexes in proxy-to-source mapping
1136 for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
1137 int source_item = proxy_to_source.at(i: proxy_item);
1138 if (source_item >= start) {
1139 Q_ASSERT(source_item - delta_item_count >= 0);
1140 proxy_to_source.replace(i: proxy_item, t: source_item - delta_item_count);
1141 }
1142 }
1143 build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
1144
1145 updateChildrenMapping(source_parent, parent_mapping: m, orient, start, end, delta_item_count, remove: true);
1146
1147}
1148
1149
1150/*!
1151 \internal
1152 updates the mapping of the children when inserting or removing items
1153*/
1154void QSortFilterProxyModelPrivate::updateChildrenMapping(const QModelIndex &source_parent, Mapping *parent_mapping,
1155 Qt::Orientation orient, int start, int end, int delta_item_count, bool remove)
1156{
1157 // see if any mapped children should be (re)moved
1158 QVector<QPair<QModelIndex, Mapping*> > moved_source_index_mappings;
1159 QVector<QModelIndex>::iterator it2 = parent_mapping->mapped_children.begin();
1160 for ( ; it2 != parent_mapping->mapped_children.end();) {
1161 const QModelIndex source_child_index = *it2;
1162 const int pos = (orient == Qt::Vertical)
1163 ? source_child_index.row()
1164 : source_child_index.column();
1165 if (pos < start) {
1166 // not affected
1167 ++it2;
1168 } else if (remove && pos <= end) {
1169 // in the removed interval
1170 it2 = parent_mapping->mapped_children.erase(pos: it2);
1171 remove_from_mapping(source_parent: source_child_index);
1172 } else {
1173 // below the removed items -- recompute the index
1174 QModelIndex new_index;
1175 const int newpos = remove ? pos - delta_item_count : pos + delta_item_count;
1176 if (orient == Qt::Vertical) {
1177 new_index = model->index(row: newpos,
1178 column: source_child_index.column(),
1179 parent: source_parent);
1180 } else {
1181 new_index = model->index(row: source_child_index.row(),
1182 column: newpos,
1183 parent: source_parent);
1184 }
1185 *it2 = new_index;
1186 ++it2;
1187
1188 // update mapping
1189 Mapping *cm = source_index_mapping.take(akey: source_child_index);
1190 Q_ASSERT(cm);
1191 // we do not reinsert right away, because the new index might be identical with another, old index
1192 moved_source_index_mappings.append(t: QPair<QModelIndex, Mapping*>(new_index, cm));
1193 }
1194 }
1195
1196 // reinsert moved, mapped indexes
1197 QVector<QPair<QModelIndex, Mapping*> >::iterator it = moved_source_index_mappings.begin();
1198 for (; it != moved_source_index_mappings.end(); ++it) {
1199 (*it).second->map_iter = QHash<QModelIndex, Mapping *>::const_iterator(source_index_mapping.insert(akey: (*it).first, avalue: (*it).second));
1200 }
1201}
1202
1203/*!
1204 \internal
1205*/
1206void QSortFilterProxyModelPrivate::proxy_item_range(
1207 const QVector<int> &source_to_proxy, const QVector<int> &source_items,
1208 int &proxy_low, int &proxy_high) const
1209{
1210 proxy_low = INT_MAX;
1211 proxy_high = INT_MIN;
1212 for (int i = 0; i < source_items.count(); ++i) {
1213 int proxy_item = source_to_proxy.at(i: source_items.at(i));
1214 Q_ASSERT(proxy_item != -1);
1215 if (proxy_item < proxy_low)
1216 proxy_low = proxy_item;
1217 if (proxy_item > proxy_high)
1218 proxy_high = proxy_item;
1219 }
1220}
1221
1222/*!
1223 \internal
1224*/
1225void QSortFilterProxyModelPrivate::build_source_to_proxy_mapping(
1226 const QVector<int> &proxy_to_source, QVector<int> &source_to_proxy) const
1227{
1228 source_to_proxy.fill(from: -1);
1229 int proxy_count = proxy_to_source.size();
1230 for (int i = 0; i < proxy_count; ++i)
1231 source_to_proxy[proxy_to_source.at(i)] = i;
1232}
1233
1234/*!
1235 \internal
1236
1237 Maps the persistent proxy indexes to source indexes and
1238 returns the list of source indexes.
1239*/
1240QModelIndexPairList QSortFilterProxyModelPrivate::store_persistent_indexes() const
1241{
1242 Q_Q(const QSortFilterProxyModel);
1243 QModelIndexPairList source_indexes;
1244 source_indexes.reserve(asize: persistent.indexes.count());
1245 for (const QPersistentModelIndexData *data : qAsConst(t: persistent.indexes)) {
1246 const QModelIndex &proxy_index = data->index;
1247 QModelIndex source_index = q->mapToSource(proxyIndex: proxy_index);
1248 source_indexes.append(t: qMakePair(x: proxy_index, y: QPersistentModelIndex(source_index)));
1249 }
1250 return source_indexes;
1251}
1252
1253/*!
1254 \internal
1255
1256 Maps \a source_indexes to proxy indexes and stores those
1257 as persistent indexes.
1258*/
1259void QSortFilterProxyModelPrivate::update_persistent_indexes(
1260 const QModelIndexPairList &source_indexes)
1261{
1262 Q_Q(QSortFilterProxyModel);
1263 QModelIndexList from, to;
1264 const int numSourceIndexes = source_indexes.count();
1265 from.reserve(alloc: numSourceIndexes);
1266 to.reserve(alloc: numSourceIndexes);
1267 for (const auto &indexPair : source_indexes) {
1268 const QPersistentModelIndex &source_index = indexPair.second;
1269 const QModelIndex &old_proxy_index = indexPair.first;
1270 create_mapping(source_parent: source_index.parent());
1271 QModelIndex proxy_index = q->mapFromSource(sourceIndex: source_index);
1272 from << old_proxy_index;
1273 to << proxy_index;
1274 }
1275 q->changePersistentIndexList(from, to);
1276}
1277
1278/*!
1279 \internal
1280
1281 Updates the source_index mapping in case it's invalid and we
1282 need it because we have a valid filter
1283*/
1284void QSortFilterProxyModelPrivate::filter_about_to_be_changed(const QModelIndex &source_parent)
1285{
1286 if (!filter_data.isEmpty() &&
1287 source_index_mapping.constFind(akey: source_parent) == source_index_mapping.constEnd())
1288 create_mapping(source_parent);
1289}
1290
1291
1292/*!
1293 \internal
1294
1295 Updates the proxy model (adds/removes rows) based on the
1296 new filter.
1297*/
1298void QSortFilterProxyModelPrivate::filter_changed(const QModelIndex &source_parent)
1299{
1300 IndexMap::const_iterator it = source_index_mapping.constFind(akey: source_parent);
1301 if (it == source_index_mapping.constEnd())
1302 return;
1303 Mapping *m = it.value();
1304 QSet<int> rows_removed = handle_filter_changed(source_to_proxy&: m->proxy_rows, proxy_to_source&: m->source_rows, source_parent, orient: Qt::Vertical);
1305 QSet<int> columns_removed = handle_filter_changed(source_to_proxy&: m->proxy_columns, proxy_to_source&: m->source_columns, source_parent, orient: Qt::Horizontal);
1306
1307 // We need to iterate over a copy of m->mapped_children because otherwise it may be changed by other code, invalidating
1308 // the iterator it2.
1309 // The m->mapped_children vector can be appended to with indexes which are no longer filtered
1310 // out (in create_mapping) when this function recurses for child indexes.
1311 const QVector<QModelIndex> mappedChildren = m->mapped_children;
1312 QVector<int> indexesToRemove;
1313 for (int i = 0; i < mappedChildren.size(); ++i) {
1314 const QModelIndex &source_child_index = mappedChildren.at(i);
1315 if (rows_removed.contains(value: source_child_index.row()) || columns_removed.contains(value: source_child_index.column())) {
1316 indexesToRemove.push_back(t: i);
1317 remove_from_mapping(source_parent: source_child_index);
1318 } else {
1319 filter_changed(source_parent: source_child_index);
1320 }
1321 }
1322 QVector<int>::const_iterator removeIt = indexesToRemove.constEnd();
1323 const QVector<int>::const_iterator removeBegin = indexesToRemove.constBegin();
1324
1325 // We can't just remove these items from mappedChildren while iterating above and then
1326 // do something like m->mapped_children = mappedChildren, because mapped_children might
1327 // be appended to in create_mapping, and we would lose those new items.
1328 // Because they are always appended in create_mapping, we can still remove them by
1329 // position here.
1330 while (removeIt != removeBegin) {
1331 --removeIt;
1332 m->mapped_children.remove(i: *removeIt);
1333 }
1334}
1335
1336/*!
1337 \internal
1338 returns the removed items indexes
1339*/
1340QSet<int> QSortFilterProxyModelPrivate::handle_filter_changed(
1341 QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
1342 const QModelIndex &source_parent, Qt::Orientation orient)
1343{
1344 Q_Q(QSortFilterProxyModel);
1345 // Figure out which mapped items to remove
1346 QVector<int> source_items_remove;
1347 for (int i = 0; i < proxy_to_source.count(); ++i) {
1348 const int source_item = proxy_to_source.at(i);
1349 if ((orient == Qt::Vertical)
1350 ? !filterAcceptsRowInternal(source_row: source_item, source_parent)
1351 : !q->filterAcceptsColumn(source_column: source_item, source_parent)) {
1352 // This source item does not satisfy the filter, so it must be removed
1353 source_items_remove.append(t: source_item);
1354 }
1355 }
1356 // Figure out which non-mapped items to insert
1357 QVector<int> source_items_insert;
1358 int source_count = source_to_proxy.size();
1359 for (int source_item = 0; source_item < source_count; ++source_item) {
1360 if (source_to_proxy.at(i: source_item) == -1) {
1361 if ((orient == Qt::Vertical)
1362 ? filterAcceptsRowInternal(source_row: source_item, source_parent)
1363 : q->filterAcceptsColumn(source_column: source_item, source_parent)) {
1364 // This source item satisfies the filter, so it must be added
1365 source_items_insert.append(t: source_item);
1366 }
1367 }
1368 }
1369 if (!source_items_remove.isEmpty() || !source_items_insert.isEmpty()) {
1370 // Do item removal and insertion
1371 remove_source_items(source_to_proxy, proxy_to_source,
1372 source_items: source_items_remove, source_parent, orient);
1373 if (orient == Qt::Vertical)
1374 sort_source_rows(source_rows&: source_items_insert, source_parent);
1375 insert_source_items(source_to_proxy, proxy_to_source,
1376 source_items: source_items_insert, source_parent, orient);
1377 }
1378 return qVectorToSet(vector: source_items_remove);
1379}
1380
1381bool QSortFilterProxyModelPrivate::needsReorder(const QVector<int> &source_rows, const QModelIndex &source_parent) const
1382{
1383 Q_Q(const QSortFilterProxyModel);
1384 Q_ASSERT(source_sort_column != -1);
1385 const int proxyRowCount = q->rowCount(parent: source_to_proxy(source_index: source_parent));
1386 // If any modified proxy row no longer passes lessThan(previous, current) or lessThan(current, next) then we need to reorder.
1387 return std::any_of(first: source_rows.begin(), last: source_rows.end(),
1388 pred: [this, q, proxyRowCount, source_parent](int sourceRow) -> bool {
1389 const QModelIndex sourceIndex = model->index(row: sourceRow, column: source_sort_column, parent: source_parent);
1390 const QModelIndex proxyIndex = source_to_proxy(source_index: sourceIndex);
1391 Q_ASSERT(proxyIndex.isValid()); // caller ensured source_rows were not filtered out
1392 if (proxyIndex.row() > 0) {
1393 const QModelIndex prevProxyIndex = q->sibling(row: proxyIndex.row() - 1, column: proxy_sort_column, idx: proxyIndex);
1394 const QModelIndex prevSourceIndex = proxy_to_source(proxy_index: prevProxyIndex);
1395 if (sort_order == Qt::AscendingOrder ? q->lessThan(source_left: sourceIndex, source_right: prevSourceIndex) : q->lessThan(source_left: prevSourceIndex, source_right: sourceIndex))
1396 return true;
1397 }
1398 if (proxyIndex.row() < proxyRowCount - 1) {
1399 const QModelIndex nextProxyIndex = q->sibling(row: proxyIndex.row() + 1, column: proxy_sort_column, idx: proxyIndex);
1400 const QModelIndex nextSourceIndex = proxy_to_source(proxy_index: nextProxyIndex);
1401 if (sort_order == Qt::AscendingOrder ? q->lessThan(source_left: nextSourceIndex, source_right: sourceIndex) : q->lessThan(source_left: sourceIndex, source_right: nextSourceIndex))
1402 return true;
1403 }
1404 return false;
1405 });
1406}
1407
1408void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &source_top_left,
1409 const QModelIndex &source_bottom_right,
1410 const QVector<int> &roles)
1411{
1412 Q_Q(QSortFilterProxyModel);
1413 if (!source_top_left.isValid() || !source_bottom_right.isValid())
1414 return;
1415
1416 std::vector<QSortFilterProxyModelDataChanged> data_changed_list;
1417 data_changed_list.emplace_back(args: source_top_left, args: source_bottom_right);
1418
1419 // Do check parents if the filter role have changed and we are recursive
1420 if (filter_recursive && (roles.isEmpty() || roles.contains(t: filter_role))) {
1421 QModelIndex source_parent = source_top_left.parent();
1422
1423 while (source_parent.isValid()) {
1424 data_changed_list.emplace_back(args&: source_parent, args&: source_parent);
1425 source_parent = source_parent.parent();
1426 }
1427 }
1428
1429 for (const QSortFilterProxyModelDataChanged &data_changed : data_changed_list) {
1430 const QModelIndex &source_top_left = data_changed.topLeft;
1431 const QModelIndex &source_bottom_right = data_changed.bottomRight;
1432 const QModelIndex source_parent = source_top_left.parent();
1433
1434 bool change_in_unmapped_parent = false;
1435 IndexMap::const_iterator it = source_index_mapping.constFind(akey: source_parent);
1436 if (it == source_index_mapping.constEnd()) {
1437 // We don't have mapping for this index, so we cannot know how things
1438 // changed (in case the change affects filtering) in order to forward
1439 // the change correctly.
1440 // But we can at least forward the signal "as is", if the row isn't
1441 // filtered out, this is better than nothing.
1442 it = create_mapping_recursive(source_parent);
1443 if (it == source_index_mapping.constEnd())
1444 continue;
1445 change_in_unmapped_parent = true;
1446 }
1447
1448 Mapping *m = it.value();
1449
1450 // Figure out how the source changes affect us
1451 QVector<int> source_rows_remove;
1452 QVector<int> source_rows_insert;
1453 QVector<int> source_rows_change;
1454 QVector<int> source_rows_resort;
1455 int end = qMin(a: source_bottom_right.row(), b: m->proxy_rows.count() - 1);
1456 for (int source_row = source_top_left.row(); source_row <= end; ++source_row) {
1457 if (dynamic_sortfilter && !change_in_unmapped_parent) {
1458 if (m->proxy_rows.at(i: source_row) != -1) {
1459 if (!filterAcceptsRowInternal(source_row, source_parent)) {
1460 // This source row no longer satisfies the filter, so it must be removed
1461 source_rows_remove.append(t: source_row);
1462 } else if (source_sort_column >= source_top_left.column() && source_sort_column <= source_bottom_right.column()) {
1463 // This source row has changed in a way that may affect sorted order
1464 source_rows_resort.append(t: source_row);
1465 } else {
1466 // This row has simply changed, without affecting filtering nor sorting
1467 source_rows_change.append(t: source_row);
1468 }
1469 } else {
1470 if (!itemsBeingRemoved.contains(parent: source_parent, row: source_row) && filterAcceptsRowInternal(source_row, source_parent)) {
1471 // This source row now satisfies the filter, so it must be added
1472 source_rows_insert.append(t: source_row);
1473 }
1474 }
1475 } else {
1476 if (m->proxy_rows.at(i: source_row) != -1)
1477 source_rows_change.append(t: source_row);
1478 }
1479 }
1480
1481 if (!source_rows_remove.isEmpty()) {
1482 remove_source_items(source_to_proxy&: m->proxy_rows, proxy_to_source&: m->source_rows,
1483 source_items: source_rows_remove, source_parent, orient: Qt::Vertical);
1484 QSet<int> source_rows_remove_set = qVectorToSet(vector: source_rows_remove);
1485 QVector<QModelIndex>::iterator childIt = m->mapped_children.end();
1486 while (childIt != m->mapped_children.begin()) {
1487 --childIt;
1488 const QModelIndex source_child_index = *childIt;
1489 if (source_rows_remove_set.contains(value: source_child_index.row())) {
1490 childIt = m->mapped_children.erase(pos: childIt);
1491 remove_from_mapping(source_parent: source_child_index);
1492 }
1493 }
1494 }
1495
1496 if (!source_rows_resort.isEmpty()) {
1497 if (needsReorder(source_rows: source_rows_resort, source_parent)) {
1498 // Re-sort the rows of this level
1499 QList<QPersistentModelIndex> parents;
1500 parents << q->mapFromSource(sourceIndex: source_parent);
1501 emit q->layoutAboutToBeChanged(parents, hint: QAbstractItemModel::VerticalSortHint);
1502 QModelIndexPairList source_indexes = store_persistent_indexes();
1503 remove_source_items(source_to_proxy&: m->proxy_rows, proxy_to_source&: m->source_rows, source_items: source_rows_resort,
1504 source_parent, orient: Qt::Vertical, emit_signal: false);
1505 sort_source_rows(source_rows&: source_rows_resort, source_parent);
1506 insert_source_items(source_to_proxy&: m->proxy_rows, proxy_to_source&: m->source_rows, source_items: source_rows_resort,
1507 source_parent, orient: Qt::Vertical, emit_signal: false);
1508 update_persistent_indexes(source_indexes);
1509 emit q->layoutChanged(parents, hint: QAbstractItemModel::VerticalSortHint);
1510 }
1511 // Make sure we also emit dataChanged for the rows
1512 source_rows_change += source_rows_resort;
1513 }
1514
1515 if (!source_rows_change.isEmpty()) {
1516 // Find the proxy row range
1517 int proxy_start_row;
1518 int proxy_end_row;
1519 proxy_item_range(source_to_proxy: m->proxy_rows, source_items: source_rows_change,
1520 proxy_low&: proxy_start_row, proxy_high&: proxy_end_row);
1521 // ### Find the proxy column range also
1522 if (proxy_end_row >= 0) {
1523 // the row was accepted, but some columns might still be filtered out
1524 int source_left_column = source_top_left.column();
1525 while (source_left_column < source_bottom_right.column()
1526 && m->proxy_columns.at(i: source_left_column) == -1)
1527 ++source_left_column;
1528 if (m->proxy_columns.at(i: source_left_column) != -1) {
1529 const QModelIndex proxy_top_left = create_index(
1530 row: proxy_start_row, column: m->proxy_columns.at(i: source_left_column), it);
1531 int source_right_column = source_bottom_right.column();
1532 while (source_right_column > source_top_left.column()
1533 && m->proxy_columns.at(i: source_right_column) == -1)
1534 --source_right_column;
1535 if (m->proxy_columns.at(i: source_right_column) != -1) {
1536 const QModelIndex proxy_bottom_right = create_index(
1537 row: proxy_end_row, column: m->proxy_columns.at(i: source_right_column), it);
1538 emit q->dataChanged(topLeft: proxy_top_left, bottomRight: proxy_bottom_right, roles);
1539 }
1540 }
1541 }
1542 }
1543
1544 if (!source_rows_insert.isEmpty()) {
1545 sort_source_rows(source_rows&: source_rows_insert, source_parent);
1546 insert_source_items(source_to_proxy&: m->proxy_rows, proxy_to_source&: m->source_rows,
1547 source_items: source_rows_insert, source_parent, orient: Qt::Vertical);
1548 }
1549 }
1550}
1551
1552void QSortFilterProxyModelPrivate::_q_sourceHeaderDataChanged(Qt::Orientation orientation,
1553 int start, int end)
1554{
1555 Q_ASSERT(start <= end);
1556
1557 Q_Q(QSortFilterProxyModel);
1558 Mapping *m = create_mapping(source_parent: QModelIndex()).value();
1559
1560 const QVector<int> &source_to_proxy = (orientation == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
1561
1562 QVector<int> proxy_positions;
1563 proxy_positions.reserve(asize: end - start + 1);
1564 {
1565 Q_ASSERT(source_to_proxy.size() > end);
1566 QVector<int>::const_iterator it = source_to_proxy.constBegin() + start;
1567 const QVector<int>::const_iterator endIt = source_to_proxy.constBegin() + end + 1;
1568 for ( ; it != endIt; ++it) {
1569 if (*it != -1)
1570 proxy_positions.push_back(t: *it);
1571 }
1572 }
1573
1574 std::sort(first: proxy_positions.begin(), last: proxy_positions.end());
1575
1576 int last_index = 0;
1577 const int numItems = proxy_positions.size();
1578 while (last_index < numItems) {
1579 const int proxyStart = proxy_positions.at(i: last_index);
1580 int proxyEnd = proxyStart;
1581 ++last_index;
1582 for (int i = last_index; i < numItems; ++i) {
1583 if (proxy_positions.at(i) == proxyEnd + 1) {
1584 ++last_index;
1585 ++proxyEnd;
1586 } else {
1587 break;
1588 }
1589 }
1590 emit q->headerDataChanged(orientation, first: proxyStart, last: proxyEnd);
1591 }
1592}
1593
1594void QSortFilterProxyModelPrivate::_q_sourceAboutToBeReset()
1595{
1596 Q_Q(QSortFilterProxyModel);
1597 q->beginResetModel();
1598}
1599
1600void QSortFilterProxyModelPrivate::_q_sourceReset()
1601{
1602 Q_Q(QSortFilterProxyModel);
1603 invalidatePersistentIndexes();
1604 _q_clearMapping();
1605 // All internal structures are deleted in clear()
1606 q->endResetModel();
1607 update_source_sort_column();
1608 if (dynamic_sortfilter && update_source_sort_column())
1609 sort();
1610}
1611
1612void QSortFilterProxyModelPrivate::_q_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
1613{
1614 Q_Q(QSortFilterProxyModel);
1615 Q_UNUSED(hint); // We can't forward Hint because we might filter additional rows or columns
1616 saved_persistent_indexes.clear();
1617
1618 saved_layoutChange_parents.clear();
1619 for (const QPersistentModelIndex &parent : sourceParents) {
1620 if (!parent.isValid()) {
1621 saved_layoutChange_parents << QPersistentModelIndex();
1622 continue;
1623 }
1624 const QModelIndex mappedParent = q->mapFromSource(sourceIndex: parent);
1625 // Might be filtered out.
1626 if (mappedParent.isValid())
1627 saved_layoutChange_parents << mappedParent;
1628 }
1629
1630 // All parents filtered out.
1631 if (!sourceParents.isEmpty() && saved_layoutChange_parents.isEmpty())
1632 return;
1633
1634 emit q->layoutAboutToBeChanged(parents: saved_layoutChange_parents);
1635 if (persistent.indexes.isEmpty())
1636 return;
1637
1638 saved_persistent_indexes = store_persistent_indexes();
1639}
1640
1641void QSortFilterProxyModelPrivate::_q_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
1642{
1643 Q_Q(QSortFilterProxyModel);
1644 Q_UNUSED(hint); // We can't forward Hint because we might filter additional rows or columns
1645
1646 if (!sourceParents.isEmpty() && saved_layoutChange_parents.isEmpty())
1647 return;
1648
1649 // Optimize: We only actually have to clear the mapping related to the contents of
1650 // sourceParents, not everything.
1651 qDeleteAll(c: source_index_mapping);
1652 source_index_mapping.clear();
1653
1654 update_persistent_indexes(source_indexes: saved_persistent_indexes);
1655 saved_persistent_indexes.clear();
1656
1657 if (dynamic_sortfilter)
1658 source_sort_column = find_source_sort_column();
1659
1660 emit q->layoutChanged(parents: saved_layoutChange_parents);
1661 saved_layoutChange_parents.clear();
1662}
1663
1664void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeInserted(
1665 const QModelIndex &source_parent, int start, int end)
1666{
1667 Q_UNUSED(start);
1668 Q_UNUSED(end);
1669
1670 const bool toplevel = !source_parent.isValid();
1671 const bool recursive_accepted = filter_recursive && !toplevel && filterAcceptsRowInternal(source_row: source_parent.row(), source_parent: source_parent.parent());
1672 //Force the creation of a mapping now, even if it's empty.
1673 //We need it because the proxy can be accessed at the moment it emits rowsAboutToBeInserted in insert_source_items
1674 if (!filter_recursive || toplevel || recursive_accepted) {
1675 if (can_create_mapping(source_parent))
1676 create_mapping(source_parent);
1677 if (filter_recursive)
1678 complete_insert = true;
1679 } else {
1680 // The row could have been rejected or the parent might be not yet known... let's try to discover it
1681 QModelIndex top_source_parent = source_parent;
1682 QModelIndex parent = source_parent.parent();
1683 QModelIndex grandParent = parent.parent();
1684
1685 while (parent.isValid() && !filterAcceptsRowInternal(source_row: parent.row(), source_parent: grandParent)) {
1686 top_source_parent = parent;
1687 parent = grandParent;
1688 grandParent = parent.parent();
1689 }
1690
1691 last_top_source = top_source_parent;
1692 }
1693}
1694
1695void QSortFilterProxyModelPrivate::_q_sourceRowsInserted(
1696 const QModelIndex &source_parent, int start, int end)
1697{
1698 if (!filter_recursive || complete_insert) {
1699 if (filter_recursive)
1700 complete_insert = false;
1701 source_items_inserted(source_parent, start, end, orient: Qt::Vertical);
1702 if (update_source_sort_column() && dynamic_sortfilter) //previous call to update_source_sort_column may fail if the model has no column.
1703 sort(); // now it should succeed so we need to make sure to sort again
1704 return;
1705 }
1706
1707 if (filter_recursive) {
1708 bool accept = false;
1709
1710 for (int row = start; row <= end; ++row) {
1711 if (filterAcceptsRowInternal(source_row: row, source_parent)) {
1712 accept = true;
1713 break;
1714 }
1715 }
1716
1717 if (!accept) // the new rows have no descendants that match the filter, filter them out.
1718 return;
1719
1720 // last_top_source should now become visible
1721 _q_sourceDataChanged(source_top_left: last_top_source, source_bottom_right: last_top_source, roles: QVector<int>());
1722 }
1723}
1724
1725void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeRemoved(
1726 const QModelIndex &source_parent, int start, int end)
1727{
1728 itemsBeingRemoved = QRowsRemoval(source_parent, start, end);
1729 source_items_about_to_be_removed(source_parent, start, end,
1730 orient: Qt::Vertical);
1731}
1732
1733void QSortFilterProxyModelPrivate::_q_sourceRowsRemoved(
1734 const QModelIndex &source_parent, int start, int end)
1735{
1736 itemsBeingRemoved = QRowsRemoval();
1737 source_items_removed(source_parent, start, end, orient: Qt::Vertical);
1738
1739 if (filter_recursive) {
1740 // Find out if removing this visible row means that some ascendant
1741 // row can now be hidden.
1742 // We go up until we find a row that should still be visible
1743 // and then make QSFPM re-evaluate the last one we saw before that, to hide it.
1744
1745 QModelIndex to_hide;
1746 QModelIndex source_ascendant = source_parent;
1747
1748 while (source_ascendant.isValid()) {
1749 if (filterAcceptsRowInternal(source_row: source_ascendant.row(), source_parent: source_ascendant.parent()))
1750 break;
1751
1752 to_hide = source_ascendant;
1753 source_ascendant = source_ascendant.parent();
1754 }
1755
1756 if (to_hide.isValid())
1757 _q_sourceDataChanged(source_top_left: to_hide, source_bottom_right: to_hide, roles: QVector<int>());
1758 }
1759}
1760
1761void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeMoved(
1762 const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1763{
1764 // Because rows which are contiguous in the source model might not be contiguous
1765 // in the proxy due to sorting, the best thing we can do here is be specific about what
1766 // parents are having their children changed.
1767 // Optimize: Emit move signals if the proxy is not sorted. Will need to account for rows
1768 // being filtered out though.
1769
1770 QList<QPersistentModelIndex> parents;
1771 parents << sourceParent;
1772 if (sourceParent != destParent)
1773 parents << destParent;
1774 _q_sourceLayoutAboutToBeChanged(sourceParents: parents, hint: QAbstractItemModel::NoLayoutChangeHint);
1775}
1776
1777void QSortFilterProxyModelPrivate::_q_sourceRowsMoved(
1778 const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1779{
1780 QList<QPersistentModelIndex> parents;
1781 parents << sourceParent;
1782 if (sourceParent != destParent)
1783 parents << destParent;
1784 _q_sourceLayoutChanged(sourceParents: parents, hint: QAbstractItemModel::NoLayoutChangeHint);
1785}
1786
1787void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeInserted(
1788 const QModelIndex &source_parent, int start, int end)
1789{
1790 Q_UNUSED(start);
1791 Q_UNUSED(end);
1792 //Force the creation of a mapping now, even if it's empty.
1793 //We need it because the proxy can be accessed at the moment it emits columnsAboutToBeInserted in insert_source_items
1794 if (can_create_mapping(source_parent))
1795 create_mapping(source_parent);
1796}
1797
1798void QSortFilterProxyModelPrivate::_q_sourceColumnsInserted(
1799 const QModelIndex &source_parent, int start, int end)
1800{
1801 Q_Q(const QSortFilterProxyModel);
1802 source_items_inserted(source_parent, start, end, orient: Qt::Horizontal);
1803
1804 if (source_parent.isValid())
1805 return; //we sort according to the root column only
1806 if (source_sort_column == -1) {
1807 //we update the source_sort_column depending on the proxy_sort_column
1808 if (update_source_sort_column() && dynamic_sortfilter)
1809 sort();
1810 } else {
1811 if (start <= source_sort_column)
1812 source_sort_column += end - start + 1;
1813
1814 proxy_sort_column = q->mapFromSource(sourceIndex: model->index(row: 0,column: source_sort_column, parent: source_parent)).column();
1815 }
1816}
1817
1818void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeRemoved(
1819 const QModelIndex &source_parent, int start, int end)
1820{
1821 source_items_about_to_be_removed(source_parent, start, end,
1822 orient: Qt::Horizontal);
1823}
1824
1825void QSortFilterProxyModelPrivate::_q_sourceColumnsRemoved(
1826 const QModelIndex &source_parent, int start, int end)
1827{
1828 Q_Q(const QSortFilterProxyModel);
1829 source_items_removed(source_parent, start, end, orient: Qt::Horizontal);
1830
1831 if (source_parent.isValid())
1832 return; //we sort according to the root column only
1833 if (start <= source_sort_column) {
1834 if (end < source_sort_column)
1835 source_sort_column -= end - start + 1;
1836 else
1837 source_sort_column = -1;
1838 }
1839
1840 if (source_sort_column >= 0)
1841 proxy_sort_column = q->mapFromSource(sourceIndex: model->index(row: 0,column: source_sort_column, parent: source_parent)).column();
1842 else
1843 proxy_sort_column = -1;
1844}
1845
1846void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeMoved(
1847 const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1848{
1849 QList<QPersistentModelIndex> parents;
1850 parents << sourceParent;
1851 if (sourceParent != destParent)
1852 parents << destParent;
1853 _q_sourceLayoutAboutToBeChanged(sourceParents: parents, hint: QAbstractItemModel::NoLayoutChangeHint);
1854}
1855
1856void QSortFilterProxyModelPrivate::_q_sourceColumnsMoved(
1857 const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
1858{
1859 QList<QPersistentModelIndex> parents;
1860 parents << sourceParent;
1861 if (sourceParent != destParent)
1862 parents << destParent;
1863 _q_sourceLayoutChanged(sourceParents: parents, hint: QAbstractItemModel::NoLayoutChangeHint);
1864}
1865
1866/*!
1867 \since 4.1
1868 \class QSortFilterProxyModel
1869 \inmodule QtCore
1870 \brief The QSortFilterProxyModel class provides support for sorting and
1871 filtering data passed between another model and a view.
1872
1873 \ingroup model-view
1874
1875 QSortFilterProxyModel can be used for sorting items, filtering out items,
1876 or both. The model transforms the structure of a source model by mapping
1877 the model indexes it supplies to new indexes, corresponding to different
1878 locations, for views to use. This approach allows a given source model to
1879 be restructured as far as views are concerned without requiring any
1880 transformations on the underlying data, and without duplicating the data in
1881 memory.
1882
1883 Let's assume that we want to sort and filter the items provided by a custom
1884 model. The code to set up the model and the view, \e without sorting and
1885 filtering, would look like this:
1886
1887 \snippet qsortfilterproxymodel-details/main.cpp 1
1888
1889 To add sorting and filtering support to \c MyItemModel, we need to create
1890 a QSortFilterProxyModel, call setSourceModel() with the \c MyItemModel as
1891 argument, and install the QSortFilterProxyModel on the view:
1892
1893 \snippet qsortfilterproxymodel-details/main.cpp 0
1894 \snippet qsortfilterproxymodel-details/main.cpp 2
1895
1896 At this point, neither sorting nor filtering is enabled; the original data
1897 is displayed in the view. Any changes made through the
1898 QSortFilterProxyModel are applied to the original model.
1899
1900 The QSortFilterProxyModel acts as a wrapper for the original model. If you
1901 need to convert source \l{QModelIndex}es to sorted/filtered model indexes
1902 or vice versa, use mapToSource(), mapFromSource(), mapSelectionToSource(),
1903 and mapSelectionFromSource().
1904
1905 \note By default, the model dynamically re-sorts and re-filters data
1906 whenever the original model changes. This behavior can be changed by
1907 setting the \l{QSortFilterProxyModel::dynamicSortFilter}{dynamicSortFilter}
1908 property.
1909
1910 The \l{itemviews/basicsortfiltermodel}{Basic Sort/Filter Model} and
1911 \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model} examples
1912 illustrate how to use QSortFilterProxyModel to perform basic sorting and
1913 filtering and how to subclass it to implement custom behavior.
1914
1915 \section1 Sorting
1916
1917 QTableView and QTreeView have a
1918 \l{QTreeView::sortingEnabled}{sortingEnabled} property that controls
1919 whether the user can sort the view by clicking the view's horizontal
1920 header. For example:
1921
1922 \snippet qsortfilterproxymodel-details/main.cpp 3
1923
1924 When this feature is on (the default is off), clicking on a header section
1925 sorts the items according to that column. By clicking repeatedly, the user
1926 can alternate between ascending and descending order.
1927
1928 \image qsortfilterproxymodel-sorting.png A sorted QTreeView
1929
1930 Behind the scene, the view calls the sort() virtual function on the model
1931 to reorder the data in the model. To make your data sortable, you can
1932 either implement sort() in your model, or use a QSortFilterProxyModel to
1933 wrap your model -- QSortFilterProxyModel provides a generic sort()
1934 reimplementation that operates on the sortRole() (Qt::DisplayRole by
1935 default) of the items and that understands several data types, including
1936 \c int, QString, and QDateTime. For hierarchical models, sorting is applied
1937 recursively to all child items. String comparisons are case sensitive by
1938 default; this can be changed by setting the \l{QSortFilterProxyModel::}
1939 {sortCaseSensitivity} property.
1940
1941 Custom sorting behavior is achieved by subclassing
1942 QSortFilterProxyModel and reimplementing lessThan(), which is
1943 used to compare items. For example:
1944
1945 \snippet ../widgets/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 5
1946
1947 (This code snippet comes from the
1948 \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model}
1949 example.)
1950
1951 An alternative approach to sorting is to disable sorting on the view and to
1952 impose a certain order to the user. This is done by explicitly calling
1953 sort() with the desired column and order as arguments on the
1954 QSortFilterProxyModel (or on the original model if it implements sort()).
1955 For example:
1956
1957 \snippet qsortfilterproxymodel-details/main.cpp 4
1958
1959 QSortFilterProxyModel can be sorted by column -1, in which case it returns
1960 to the sort order of the underlying source model.
1961
1962 \section1 Filtering
1963
1964 In addition to sorting, QSortFilterProxyModel can be used to hide items
1965 that do not match a certain filter. The filter is specified using a QRegExp
1966 object and is applied to the filterRole() (Qt::DisplayRole by default) of
1967 each item, for a given column. The QRegExp object can be used to match a
1968 regular expression, a wildcard pattern, or a fixed string. For example:
1969
1970 \snippet qsortfilterproxymodel-details/main.cpp 5
1971
1972 For hierarchical models, the filter is applied recursively to all children.
1973 If a parent item doesn't match the filter, none of its children will be
1974 shown.
1975
1976 A common use case is to let the user specify the filter regular expression,
1977 wildcard pattern, or fixed string in a QLineEdit and to connect the
1978 \l{QLineEdit::textChanged()}{textChanged()} signal to setFilterRegularExpression(),
1979 setFilterWildcard(), or setFilterFixedString() to reapply the filter.
1980
1981 Custom filtering behavior can be achieved by reimplementing the
1982 filterAcceptsRow() and filterAcceptsColumn() functions. For
1983 example (from the \l{itemviews/customsortfiltermodel}
1984 {Custom Sort/Filter Model} example), the following implementation ignores
1985 the \l{QSortFilterProxyModel::filterKeyColumn}{filterKeyColumn} property
1986 and performs filtering on columns 0, 1, and 2:
1987
1988 \snippet ../widgets/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 3
1989
1990 (This code snippet comes from the
1991 \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model}
1992 example.)
1993
1994 If you are working with large amounts of filtering and have to invoke
1995 invalidateFilter() repeatedly, using beginResetModel() / endResetModel() may
1996 be more efficient, depending on the implementation of your model. However,
1997 beginResetModel() / endResetModel() returns the
1998 proxy model to its original state, losing selection information, and will
1999 cause the proxy model to be repopulated.
2000
2001 \section1 Subclassing
2002
2003 Since QAbstractProxyModel and its subclasses are derived from
2004 QAbstractItemModel, much of the same advice about subclassing normal models
2005 also applies to proxy models. In addition, it is worth noting that many of
2006 the default implementations of functions in this class are written so that
2007 they call the equivalent functions in the relevant source model. This
2008 simple proxying mechanism may need to be overridden for source models with
2009 more complex behavior; for example, if the source model provides a custom
2010 hasChildren() implementation, you should also provide one in the proxy
2011 model.
2012
2013 \note Some general guidelines for subclassing models are available in the
2014 \l{Model Subclassing Reference}.
2015
2016 \note With Qt 5, regular expression support has been improved through the
2017 QRegularExpression class. QSortFilterProxyModel dating back prior to that
2018 class creation, it originally supported only QRegExp. Since Qt 5.12,
2019 QRegularExpression APIs have been added. Therefore, QRegExp APIs should be
2020 considered deprecated and the QRegularExpression version should be used in
2021 place.
2022
2023 \warning Don't mix calls to the getters and setters of different regexp types
2024 as this will lead to unexpected results. For maximum compatibility, the original
2025 implementation has been kept. Therefore, if, for example, a call to
2026 setFilterRegularExpression is made followed by another one to
2027 setFilterFixedString, the first call will setup a QRegularExpression object
2028 to use as filter while the second will setup a QRegExp in FixedString mode.
2029 However, this is an implementation detail that might change in the future.
2030
2031 \sa QAbstractProxyModel, QAbstractItemModel, {Model/View Programming},
2032 {Basic Sort/Filter Model Example}, {Custom Sort/Filter Model Example}, QIdentityProxyModel
2033*/
2034
2035/*!
2036 Constructs a sorting filter model with the given \a parent.
2037*/
2038
2039QSortFilterProxyModel::QSortFilterProxyModel(QObject *parent)
2040 : QAbstractProxyModel(*new QSortFilterProxyModelPrivate, parent)
2041{
2042 Q_D(QSortFilterProxyModel);
2043 d->proxy_sort_column = d->source_sort_column = -1;
2044 d->sort_order = Qt::AscendingOrder;
2045 d->sort_casesensitivity = Qt::CaseSensitive;
2046 d->sort_role = Qt::DisplayRole;
2047 d->sort_localeaware = false;
2048 d->filter_column = 0;
2049 d->filter_role = Qt::DisplayRole;
2050 d->filter_recursive = false;
2051 d->dynamic_sortfilter = true;
2052 d->complete_insert = false;
2053 connect(sender: this, SIGNAL(modelReset()), receiver: this, SLOT(_q_clearMapping()));
2054}
2055
2056/*!
2057 Destroys this sorting filter model.
2058*/
2059QSortFilterProxyModel::~QSortFilterProxyModel()
2060{
2061 Q_D(QSortFilterProxyModel);
2062 qDeleteAll(c: d->source_index_mapping);
2063 d->source_index_mapping.clear();
2064}
2065
2066/*!
2067 \reimp
2068*/
2069void QSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
2070{
2071 Q_D(QSortFilterProxyModel);
2072
2073 if (sourceModel == d->model)
2074 return;
2075
2076 beginResetModel();
2077
2078 disconnect(sender: d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
2079 receiver: this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QVector<int>)));
2080
2081 disconnect(sender: d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
2082 receiver: this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int)));
2083
2084 disconnect(sender: d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
2085 receiver: this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int)));
2086
2087 disconnect(sender: d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
2088 receiver: this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int)));
2089
2090 disconnect(sender: d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
2091 receiver: this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int)));
2092
2093 disconnect(sender: d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
2094 receiver: this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int)));
2095
2096 disconnect(sender: d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
2097 receiver: this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
2098
2099 disconnect(sender: d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
2100 receiver: this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int)));
2101
2102 disconnect(sender: d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
2103 receiver: this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int)));
2104
2105 disconnect(sender: d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
2106 receiver: this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int)));
2107
2108 disconnect(sender: d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
2109 receiver: this, SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
2110
2111 disconnect(sender: d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
2112 receiver: this, SLOT(_q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)));
2113
2114 disconnect(sender: d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
2115 receiver: this, SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
2116
2117 disconnect(sender: d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
2118 receiver: this, SLOT(_q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int)));
2119
2120 disconnect(sender: d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
2121 receiver: this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
2122
2123 disconnect(sender: d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
2124 receiver: this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
2125
2126 disconnect(sender: d->model, SIGNAL(modelAboutToBeReset()), receiver: this, SLOT(_q_sourceAboutToBeReset()));
2127 disconnect(sender: d->model, SIGNAL(modelReset()), receiver: this, SLOT(_q_sourceReset()));
2128
2129 // same as in _q_sourceReset()
2130 d->invalidatePersistentIndexes();
2131 d->_q_clearMapping();
2132
2133 QAbstractProxyModel::setSourceModel(sourceModel);
2134
2135 connect(sender: d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
2136 receiver: this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QVector<int>)));
2137
2138 connect(sender: d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
2139 receiver: this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int)));
2140
2141 connect(sender: d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
2142 receiver: this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int)));
2143
2144 connect(sender: d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
2145 receiver: this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int)));
2146
2147 connect(sender: d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
2148 receiver: this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int)));
2149
2150 connect(sender: d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
2151 receiver: this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int)));
2152
2153 connect(sender: d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
2154 receiver: this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
2155
2156 connect(sender: d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
2157 receiver: this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int)));
2158
2159 connect(sender: d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
2160 receiver: this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int)));
2161
2162 connect(sender: d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
2163 receiver: this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int)));
2164
2165 connect(sender: d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
2166 receiver: this, SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
2167
2168 connect(sender: d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
2169 receiver: this, SLOT(_q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)));
2170
2171 connect(sender: d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
2172 receiver: this, SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
2173
2174 connect(sender: d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
2175 receiver: this, SLOT(_q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int)));
2176
2177 connect(sender: d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
2178 receiver: this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
2179
2180 connect(sender: d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
2181 receiver: this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
2182
2183 connect(sender: d->model, SIGNAL(modelAboutToBeReset()), receiver: this, SLOT(_q_sourceAboutToBeReset()));
2184 connect(sender: d->model, SIGNAL(modelReset()), receiver: this, SLOT(_q_sourceReset()));
2185
2186 endResetModel();
2187 if (d->update_source_sort_column() && d->dynamic_sortfilter)
2188 d->sort();
2189}
2190
2191/*!
2192 \reimp
2193*/
2194QModelIndex QSortFilterProxyModel::index(int row, int column, const QModelIndex &parent) const
2195{
2196 Q_D(const QSortFilterProxyModel);
2197 if (row < 0 || column < 0)
2198 return QModelIndex();
2199
2200 QModelIndex source_parent = mapToSource(proxyIndex: parent); // parent is already mapped at this point
2201 IndexMap::const_iterator it = d->create_mapping(source_parent); // but make sure that the children are mapped
2202 if (it.value()->source_rows.count() <= row || it.value()->source_columns.count() <= column)
2203 return QModelIndex();
2204
2205 return d->create_index(row, column, it);
2206}
2207
2208/*!
2209 \reimp
2210*/
2211QModelIndex QSortFilterProxyModel::parent(const QModelIndex &child) const
2212{
2213 Q_D(const QSortFilterProxyModel);
2214 if (!d->indexValid(index: child))
2215 return QModelIndex();
2216 IndexMap::const_iterator it = d->index_to_iterator(proxy_index: child);
2217 Q_ASSERT(it != d->source_index_mapping.constEnd());
2218 QModelIndex source_parent = it.key();
2219 QModelIndex proxy_parent = mapFromSource(sourceIndex: source_parent);
2220 return proxy_parent;
2221}
2222
2223/*!
2224 \reimp
2225*/
2226QModelIndex QSortFilterProxyModel::sibling(int row, int column, const QModelIndex &idx) const
2227{
2228 Q_D(const QSortFilterProxyModel);
2229 if (!d->indexValid(index: idx))
2230 return QModelIndex();
2231
2232 const IndexMap::const_iterator it = d->index_to_iterator(proxy_index: idx);
2233 if (it.value()->source_rows.count() <= row || it.value()->source_columns.count() <= column)
2234 return QModelIndex();
2235
2236 return d->create_index(row, column, it);
2237}
2238
2239/*!
2240 \reimp
2241*/
2242int QSortFilterProxyModel::rowCount(const QModelIndex &parent) const
2243{
2244 Q_D(const QSortFilterProxyModel);
2245 QModelIndex source_parent = mapToSource(proxyIndex: parent);
2246 if (parent.isValid() && !source_parent.isValid())
2247 return 0;
2248 IndexMap::const_iterator it = d->create_mapping(source_parent);
2249 return it.value()->source_rows.count();
2250}
2251
2252/*!
2253 \reimp
2254*/
2255int QSortFilterProxyModel::columnCount(const QModelIndex &parent) const
2256{
2257 Q_D(const QSortFilterProxyModel);
2258 QModelIndex source_parent = mapToSource(proxyIndex: parent);
2259 if (parent.isValid() && !source_parent.isValid())
2260 return 0;
2261 IndexMap::const_iterator it = d->create_mapping(source_parent);
2262 return it.value()->source_columns.count();
2263}
2264
2265/*!
2266 \reimp
2267*/
2268bool QSortFilterProxyModel::hasChildren(const QModelIndex &parent) const
2269{
2270 Q_D(const QSortFilterProxyModel);
2271 QModelIndex source_parent = mapToSource(proxyIndex: parent);
2272 if (parent.isValid() && !source_parent.isValid())
2273 return false;
2274 if (!d->model->hasChildren(parent: source_parent))
2275 return false;
2276
2277 if (d->model->canFetchMore(parent: source_parent))
2278 return true; //we assume we might have children that can be fetched
2279
2280 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2281 return m->source_rows.count() != 0 && m->source_columns.count() != 0;
2282}
2283
2284/*!
2285 \reimp
2286*/
2287QVariant QSortFilterProxyModel::data(const QModelIndex &index, int role) const
2288{
2289 Q_D(const QSortFilterProxyModel);
2290 QModelIndex source_index = mapToSource(proxyIndex: index);
2291 if (index.isValid() && !source_index.isValid())
2292 return QVariant();
2293 return d->model->data(index: source_index, role);
2294}
2295
2296/*!
2297 \reimp
2298*/
2299bool QSortFilterProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
2300{
2301 Q_D(QSortFilterProxyModel);
2302 QModelIndex source_index = mapToSource(proxyIndex: index);
2303 if (index.isValid() && !source_index.isValid())
2304 return false;
2305 return d->model->setData(index: source_index, value, role);
2306}
2307
2308/*!
2309 \reimp
2310*/
2311QVariant QSortFilterProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
2312{
2313 Q_D(const QSortFilterProxyModel);
2314 IndexMap::const_iterator it = d->create_mapping(source_parent: QModelIndex());
2315 if (it.value()->source_rows.count() * it.value()->source_columns.count() > 0)
2316 return QAbstractProxyModel::headerData(section, orientation, role);
2317 int source_section;
2318 if (orientation == Qt::Vertical) {
2319 if (section < 0 || section >= it.value()->source_rows.count())
2320 return QVariant();
2321 source_section = it.value()->source_rows.at(i: section);
2322 } else {
2323 if (section < 0 || section >= it.value()->source_columns.count())
2324 return QVariant();
2325 source_section = it.value()->source_columns.at(i: section);
2326 }
2327 return d->model->headerData(section: source_section, orientation, role);
2328}
2329
2330/*!
2331 \reimp
2332*/
2333bool QSortFilterProxyModel::setHeaderData(int section, Qt::Orientation orientation,
2334 const QVariant &value, int role)
2335{
2336 Q_D(QSortFilterProxyModel);
2337 IndexMap::const_iterator it = d->create_mapping(source_parent: QModelIndex());
2338 if (it.value()->source_rows.count() * it.value()->source_columns.count() > 0)
2339 return QAbstractProxyModel::setHeaderData(section, orientation, value, role);
2340 int source_section;
2341 if (orientation == Qt::Vertical) {
2342 if (section < 0 || section >= it.value()->source_rows.count())
2343 return false;
2344 source_section = it.value()->source_rows.at(i: section);
2345 } else {
2346 if (section < 0 || section >= it.value()->source_columns.count())
2347 return false;
2348 source_section = it.value()->source_columns.at(i: section);
2349 }
2350 return d->model->setHeaderData(section: source_section, orientation, value, role);
2351}
2352
2353/*!
2354 \reimp
2355*/
2356QMimeData *QSortFilterProxyModel::mimeData(const QModelIndexList &indexes) const
2357{
2358 Q_D(const QSortFilterProxyModel);
2359 QModelIndexList source_indexes;
2360 source_indexes.reserve(alloc: indexes.count());
2361 for (const QModelIndex &idx : indexes)
2362 source_indexes << mapToSource(proxyIndex: idx);
2363 return d->model->mimeData(indexes: source_indexes);
2364}
2365
2366/*!
2367 \reimp
2368*/
2369QStringList QSortFilterProxyModel::mimeTypes() const
2370{
2371 Q_D(const QSortFilterProxyModel);
2372 return d->model->mimeTypes();
2373}
2374
2375/*!
2376 \reimp
2377*/
2378Qt::DropActions QSortFilterProxyModel::supportedDropActions() const
2379{
2380 Q_D(const QSortFilterProxyModel);
2381 return d->model->supportedDropActions();
2382}
2383
2384// Qt6: remove unnecessary reimplementation
2385/*!
2386 \reimp
2387*/
2388bool QSortFilterProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
2389 int row, int column, const QModelIndex &parent)
2390{
2391 return QAbstractProxyModel::dropMimeData(data, action, row, column, parent);
2392}
2393
2394/*!
2395 \reimp
2396*/
2397bool QSortFilterProxyModel::insertRows(int row, int count, const QModelIndex &parent)
2398{
2399 Q_D(QSortFilterProxyModel);
2400 if (row < 0 || count <= 0)
2401 return false;
2402 QModelIndex source_parent = mapToSource(proxyIndex: parent);
2403 if (parent.isValid() && !source_parent.isValid())
2404 return false;
2405 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2406 if (row > m->source_rows.count())
2407 return false;
2408 int source_row = (row >= m->source_rows.count()
2409 ? m->proxy_rows.count()
2410 : m->source_rows.at(i: row));
2411 return d->model->insertRows(row: source_row, count, parent: source_parent);
2412}
2413
2414/*!
2415 \reimp
2416*/
2417bool QSortFilterProxyModel::insertColumns(int column, int count, const QModelIndex &parent)
2418{
2419 Q_D(QSortFilterProxyModel);
2420 if (column < 0|| count <= 0)
2421 return false;
2422 QModelIndex source_parent = mapToSource(proxyIndex: parent);
2423 if (parent.isValid() && !source_parent.isValid())
2424 return false;
2425 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2426 if (column > m->source_columns.count())
2427 return false;
2428 int source_column = (column >= m->source_columns.count()
2429 ? m->proxy_columns.count()
2430 : m->source_columns.at(i: column));
2431 return d->model->insertColumns(column: source_column, count, parent: source_parent);
2432}
2433
2434/*!
2435 \reimp
2436*/
2437bool QSortFilterProxyModel::removeRows(int row, int count, const QModelIndex &parent)
2438{
2439 Q_D(QSortFilterProxyModel);
2440 if (row < 0 || count <= 0)
2441 return false;
2442 QModelIndex source_parent = mapToSource(proxyIndex: parent);
2443 if (parent.isValid() && !source_parent.isValid())
2444 return false;
2445 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2446 if (row + count > m->source_rows.count())
2447 return false;
2448 if ((count == 1)
2449 || ((d->source_sort_column < 0) && (m->proxy_rows.count() == m->source_rows.count()))) {
2450 int source_row = m->source_rows.at(i: row);
2451 return d->model->removeRows(row: source_row, count, parent: source_parent);
2452 }
2453 // remove corresponding source intervals
2454 // ### if this proves to be slow, we can switch to single-row removal
2455 QVector<int> rows;
2456 rows.reserve(asize: count);
2457 for (int i = row; i < row + count; ++i)
2458 rows.append(t: m->source_rows.at(i));
2459 std::sort(first: rows.begin(), last: rows.end());
2460
2461 int pos = rows.count() - 1;
2462 bool ok = true;
2463 while (pos >= 0) {
2464 const int source_end = rows.at(i: pos--);
2465 int source_start = source_end;
2466 while ((pos >= 0) && (rows.at(i: pos) == (source_start - 1))) {
2467 --source_start;
2468 --pos;
2469 }
2470 ok = ok && d->model->removeRows(row: source_start, count: source_end - source_start + 1,
2471 parent: source_parent);
2472 }
2473 return ok;
2474}
2475
2476/*!
2477 \reimp
2478*/
2479bool QSortFilterProxyModel::removeColumns(int column, int count, const QModelIndex &parent)
2480{
2481 Q_D(QSortFilterProxyModel);
2482 if (column < 0 || count <= 0)
2483 return false;
2484 QModelIndex source_parent = mapToSource(proxyIndex: parent);
2485 if (parent.isValid() && !source_parent.isValid())
2486 return false;
2487 QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
2488 if (column + count > m->source_columns.count())
2489 return false;
2490 if ((count == 1) || (m->proxy_columns.count() == m->source_columns.count())) {
2491 int source_column = m->source_columns.at(i: column);
2492 return d->model->removeColumns(column: source_column, count, parent: source_parent);
2493 }
2494 // remove corresponding source intervals
2495 QVector<int> columns;
2496 columns.reserve(asize: count);
2497 for (int i = column; i < column + count; ++i)
2498 columns.append(t: m->source_columns.at(i));
2499
2500 int pos = columns.count() - 1;
2501 bool ok = true;
2502 while (pos >= 0) {
2503 const int source_end = columns.at(i: pos--);
2504 int source_start = source_end;
2505 while ((pos >= 0) && (columns.at(i: pos) == (source_start - 1))) {
2506 --source_start;
2507 --pos;
2508 }
2509 ok = ok && d->model->removeColumns(column: source_start, count: source_end - source_start + 1,
2510 parent: source_parent);
2511 }
2512 return ok;
2513}
2514
2515/*!
2516 \reimp
2517*/
2518void QSortFilterProxyModel::fetchMore(const QModelIndex &parent)
2519{
2520 Q_D(QSortFilterProxyModel);
2521 QModelIndex source_parent;
2522 if (d->indexValid(index: parent))
2523 source_parent = mapToSource(proxyIndex: parent);
2524 d->model->fetchMore(parent: source_parent);
2525}
2526
2527/*!
2528 \reimp
2529*/
2530bool QSortFilterProxyModel::canFetchMore(const QModelIndex &parent) const
2531{
2532 Q_D(const QSortFilterProxyModel);
2533 QModelIndex source_parent;
2534 if (d->indexValid(index: parent))
2535 source_parent = mapToSource(proxyIndex: parent);
2536 return d->model->canFetchMore(parent: source_parent);
2537}
2538
2539/*!
2540 \reimp
2541*/
2542Qt::ItemFlags QSortFilterProxyModel::flags(const QModelIndex &index) const
2543{
2544 Q_D(const QSortFilterProxyModel);
2545 QModelIndex source_index;
2546 if (d->indexValid(index))
2547 source_index = mapToSource(proxyIndex: index);
2548 return d->model->flags(index: source_index);
2549}
2550
2551/*!
2552 \reimp
2553*/
2554QModelIndex QSortFilterProxyModel::buddy(const QModelIndex &index) const
2555{
2556 Q_D(const QSortFilterProxyModel);
2557 if (!d->indexValid(index))
2558 return QModelIndex();
2559 QModelIndex source_index = mapToSource(proxyIndex: index);
2560 QModelIndex source_buddy = d->model->buddy(index: source_index);
2561 if (source_index == source_buddy)
2562 return index;
2563 return mapFromSource(sourceIndex: source_buddy);
2564}
2565
2566/*!
2567 \reimp
2568*/
2569QModelIndexList QSortFilterProxyModel::match(const QModelIndex &start, int role,
2570 const QVariant &value, int hits,
2571 Qt::MatchFlags flags) const
2572{
2573 return QAbstractProxyModel::match(start, role, value, hits, flags);
2574}
2575
2576/*!
2577 \reimp
2578*/
2579QSize QSortFilterProxyModel::span(const QModelIndex &index) const
2580{
2581 Q_D(const QSortFilterProxyModel);
2582 QModelIndex source_index = mapToSource(proxyIndex: index);
2583 if (index.isValid() && !source_index.isValid())
2584 return QSize();
2585 return d->model->span(index: source_index);
2586}
2587
2588/*!
2589 \reimp
2590*/
2591void QSortFilterProxyModel::sort(int column, Qt::SortOrder order)
2592{
2593 Q_D(QSortFilterProxyModel);
2594 if (d->dynamic_sortfilter && d->proxy_sort_column == column && d->sort_order == order)
2595 return;
2596 d->sort_order = order;
2597 d->proxy_sort_column = column;
2598 d->update_source_sort_column();
2599 d->sort();
2600}
2601
2602/*!
2603 \since 4.5
2604 \brief the column currently used for sorting
2605
2606 This returns the most recently used sort column.
2607*/
2608int QSortFilterProxyModel::sortColumn() const
2609{
2610 Q_D(const QSortFilterProxyModel);
2611 return d->proxy_sort_column;
2612}
2613
2614/*!
2615 \since 4.5
2616 \brief the order currently used for sorting
2617
2618 This returns the most recently used sort order.
2619*/
2620Qt::SortOrder QSortFilterProxyModel::sortOrder() const
2621{
2622 Q_D(const QSortFilterProxyModel);
2623 return d->sort_order;
2624}
2625
2626/*!
2627 \property QSortFilterProxyModel::filterRegExp
2628 \brief the QRegExp used to filter the contents of the source model
2629
2630 Setting this property overwrites the current
2631 \l{QSortFilterProxyModel::filterCaseSensitivity}{filterCaseSensitivity}.
2632 By default, the QRegExp is an empty string matching all contents.
2633
2634 If no QRegExp or an empty string is set, everything in the source model
2635 will be accepted.
2636
2637 \sa filterCaseSensitivity, setFilterWildcard(), setFilterFixedString()
2638*/
2639QRegExp QSortFilterProxyModel::filterRegExp() const
2640{
2641 Q_D(const QSortFilterProxyModel);
2642 return d->filter_data.regExp();
2643}
2644
2645void QSortFilterProxyModel::setFilterRegExp(const QRegExp &regExp)
2646{
2647 Q_D(QSortFilterProxyModel);
2648 d->filter_about_to_be_changed();
2649 d->filter_data.setRegExp(regExp);
2650 d->filter_changed();
2651}
2652
2653#if QT_CONFIG(regularexpression)
2654/*!
2655 \since 5.12
2656 \property QSortFilterProxyModel::filterRegularExpression
2657 \brief the QRegularExpression used to filter the contents of the source model
2658
2659 Setting this property overwrites the current
2660 \l{QSortFilterProxyModel::filterCaseSensitivity}{filterCaseSensitivity}.
2661 By default, the QRegularExpression is an empty string matching all contents.
2662
2663 If no QRegularExpression or an empty string is set, everything in the source
2664 model will be accepted.
2665
2666 \sa filterCaseSensitivity, setFilterWildcard(), setFilterFixedString()
2667*/
2668QRegularExpression QSortFilterProxyModel::filterRegularExpression() const
2669{
2670 Q_D(const QSortFilterProxyModel);
2671 return d->filter_data.regularExpression();
2672}
2673
2674void QSortFilterProxyModel::setFilterRegularExpression(const QRegularExpression &regularExpression)
2675{
2676 Q_D(QSortFilterProxyModel);
2677 d->filter_about_to_be_changed();
2678 d->filter_data.setRegularExpression(regularExpression);
2679 d->filter_changed();
2680}
2681#endif
2682
2683/*!
2684 \property QSortFilterProxyModel::filterKeyColumn
2685 \brief the column where the key used to filter the contents of the
2686 source model is read from.
2687
2688 The default value is 0. If the value is -1, the keys will be read
2689 from all columns.
2690*/
2691int QSortFilterProxyModel::filterKeyColumn() const
2692{
2693 Q_D(const QSortFilterProxyModel);
2694 return d->filter_column;
2695}
2696
2697void QSortFilterProxyModel::setFilterKeyColumn(int column)
2698{
2699 Q_D(QSortFilterProxyModel);
2700 d->filter_about_to_be_changed();
2701 d->filter_column = column;
2702 d->filter_changed();
2703}
2704
2705/*!
2706 \property QSortFilterProxyModel::filterCaseSensitivity
2707
2708 \brief the case sensitivity of the QRegExp pattern used to filter the
2709 contents of the source model.
2710
2711 By default, the filter is case sensitive.
2712
2713 \sa filterRegExp, sortCaseSensitivity
2714*/
2715
2716/*!
2717 \since 5.15
2718 \fn void QSortFilterProxyModel::filterCaseSensitivityChanged(Qt::CaseSensitivity filterCaseSensitivity)
2719 \brief This signal is emitted when the case sensitivity of the filter
2720 changes to \a filterCaseSensitivity.
2721 */
2722Qt::CaseSensitivity QSortFilterProxyModel::filterCaseSensitivity() const
2723{
2724 Q_D(const QSortFilterProxyModel);
2725 return d->filter_data.caseSensitivity();
2726}
2727
2728void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs)
2729{
2730 Q_D(QSortFilterProxyModel);
2731 if (cs == d->filter_data.caseSensitivity())
2732 return;
2733 d->filter_about_to_be_changed();
2734 d->filter_data.setCaseSensitivity(cs);
2735 d->filter_changed();
2736 emit filterCaseSensitivityChanged(filterCaseSensitivity: cs);
2737}
2738
2739/*!
2740 \since 4.2
2741 \property QSortFilterProxyModel::sortCaseSensitivity
2742 \brief the case sensitivity setting used for comparing strings when sorting
2743
2744 By default, sorting is case sensitive.
2745
2746 \sa filterCaseSensitivity, lessThan()
2747*/
2748
2749/*!
2750 \since 5.15
2751 \fn void QSortFilterProxyModel::sortCaseSensitivityChanged(Qt::CaseSensitivity sortCaseSensitivity)
2752 \brief This signal is emitted when the case sensitivity for sorting
2753 changes to \a sortCaseSensitivity.
2754*/
2755Qt::CaseSensitivity QSortFilterProxyModel::sortCaseSensitivity() const
2756{
2757 Q_D(const QSortFilterProxyModel);
2758 return d->sort_casesensitivity;
2759}
2760
2761void QSortFilterProxyModel::setSortCaseSensitivity(Qt::CaseSensitivity cs)
2762{
2763 Q_D(QSortFilterProxyModel);
2764 if (d->sort_casesensitivity == cs)
2765 return;
2766
2767 d->sort_casesensitivity = cs;
2768 d->sort();
2769 emit sortCaseSensitivityChanged(sortCaseSensitivity: cs);
2770}
2771
2772/*!
2773 \since 4.3
2774 \property QSortFilterProxyModel::isSortLocaleAware
2775 \brief the local aware setting used for comparing strings when sorting
2776
2777 By default, sorting is not local aware.
2778
2779 \sa sortCaseSensitivity, lessThan()
2780*/
2781
2782/*!
2783 \since 5.15
2784 \fn void QSortFilterProxyModel::sortLocaleAwareChanged(bool sortLocaleAware)
2785 \brief This signal is emitted when the locale aware setting
2786 changes to \a sortLocaleAware.
2787*/
2788bool QSortFilterProxyModel::isSortLocaleAware() const
2789{
2790 Q_D(const QSortFilterProxyModel);
2791 return d->sort_localeaware;
2792}
2793
2794void QSortFilterProxyModel::setSortLocaleAware(bool on)
2795{
2796 Q_D(QSortFilterProxyModel);
2797 if (d->sort_localeaware == on)
2798 return;
2799
2800 d->sort_localeaware = on;
2801 d->sort();
2802 emit sortLocaleAwareChanged(sortLocaleAware: on);
2803}
2804
2805/*!
2806 \overload
2807
2808 Sets the regular expression used to filter the contents
2809 of the source model to \a pattern.
2810
2811 \sa setFilterCaseSensitivity(), setFilterWildcard(), setFilterFixedString(), filterRegExp()
2812*/
2813void QSortFilterProxyModel::setFilterRegExp(const QString &pattern)
2814{
2815 Q_D(QSortFilterProxyModel);
2816 d->filter_about_to_be_changed();
2817 QRegExp rx(pattern);
2818 rx.setCaseSensitivity(d->filter_data.caseSensitivity());
2819 d->filter_data.setRegExp(rx);
2820 d->filter_changed();
2821}
2822
2823#if QT_CONFIG(regularexpression)
2824/*!
2825 \since 5.12
2826
2827 Sets the regular expression used to filter the contents
2828 of the source model to \a pattern.
2829
2830 This method should be preferred for new code as it will use
2831 QRegularExpression internally.
2832
2833 \sa setFilterCaseSensitivity(), setFilterWildcard(), setFilterFixedString(), filterRegularExpression()
2834*/
2835void QSortFilterProxyModel::setFilterRegularExpression(const QString &pattern)
2836{
2837 Q_D(QSortFilterProxyModel);
2838 d->filter_about_to_be_changed();
2839 QRegularExpression rx(pattern,
2840 d->filter_data.caseSensitivity()
2841 ? QRegularExpression::NoPatternOption
2842 : QRegularExpression::CaseInsensitiveOption);
2843 d->filter_data.setRegularExpression(rx);
2844 d->filter_changed();
2845}
2846#endif
2847
2848/*!
2849 Sets the wildcard expression used to filter the contents
2850 of the source model to the given \a pattern.
2851
2852 \sa setFilterCaseSensitivity(), setFilterRegExp(), setFilterFixedString(), filterRegExp()
2853*/
2854void QSortFilterProxyModel::setFilterWildcard(const QString &pattern)
2855{
2856 Q_D(QSortFilterProxyModel);
2857 d->filter_about_to_be_changed();
2858 QRegExp rx(pattern, d->filter_data.caseSensitivity(), QRegExp::Wildcard);
2859 d->filter_data.setRegExp(rx);
2860 d->filter_changed();
2861}
2862
2863/*!
2864 Sets the fixed string used to filter the contents
2865 of the source model to the given \a pattern.
2866
2867 \sa setFilterCaseSensitivity(), setFilterRegExp(), setFilterWildcard(), filterRegExp()
2868*/
2869void QSortFilterProxyModel::setFilterFixedString(const QString &pattern)
2870{
2871 Q_D(QSortFilterProxyModel);
2872 d->filter_about_to_be_changed();
2873 QRegExp rx(pattern, d->filter_data.caseSensitivity(), QRegExp::FixedString);
2874 d->filter_data.setRegExp(rx);
2875 d->filter_changed();
2876}
2877
2878/*!
2879 \since 4.2
2880 \property QSortFilterProxyModel::dynamicSortFilter
2881 \brief whether the proxy model is dynamically sorted and filtered
2882 whenever the contents of the source model change
2883
2884 Note that you should not update the source model through the proxy
2885 model when dynamicSortFilter is true. For instance, if you set the
2886 proxy model on a QComboBox, then using functions that update the
2887 model, e.g., \l{QComboBox::}{addItem()}, will not work as
2888 expected. An alternative is to set dynamicSortFilter to false and
2889 call \l{QSortFilterProxyModel::}{sort()} after adding items to the
2890 QComboBox.
2891
2892 The default value is true.
2893*/
2894bool QSortFilterProxyModel::dynamicSortFilter() const
2895{
2896 Q_D(const QSortFilterProxyModel);
2897 return d->dynamic_sortfilter;
2898}
2899
2900void QSortFilterProxyModel::setDynamicSortFilter(bool enable)
2901{
2902 Q_D(QSortFilterProxyModel);
2903 d->dynamic_sortfilter = enable;
2904 if (enable)
2905 d->sort();
2906}
2907
2908/*!
2909 \since 4.2
2910 \property QSortFilterProxyModel::sortRole
2911 \brief the item role that is used to query the source model's data when
2912 sorting items.
2913
2914 The default value is Qt::DisplayRole.
2915
2916 \sa lessThan()
2917*/
2918
2919/*!
2920 \since 5.15
2921 \fn void QSortFilterProxyModel::sortRoleChanged(int sortRole)
2922 \brief This signal is emitted when the sort role changes to \a sortRole.
2923*/
2924int QSortFilterProxyModel::sortRole() const
2925{
2926 Q_D(const QSortFilterProxyModel);
2927 return d->sort_role;
2928}
2929
2930void QSortFilterProxyModel::setSortRole(int role)
2931{
2932 Q_D(QSortFilterProxyModel);
2933 if (d->sort_role == role)
2934 return;
2935 d->sort_role = role;
2936 d->sort();
2937 emit sortRoleChanged(sortRole: role);
2938}
2939
2940/*!
2941 \since 4.2
2942 \property QSortFilterProxyModel::filterRole
2943 \brief the item role that is used to query the source model's data when
2944 filtering items.
2945
2946 The default value is Qt::DisplayRole.
2947
2948 \sa filterAcceptsRow()
2949*/
2950
2951/*!
2952 \since 5.15
2953 \fn void QSortFilterProxyModel::filterRoleChanged(int filterRole)
2954 \brief This signal is emitted when the filter role changes to \a filterRole.
2955*/
2956int QSortFilterProxyModel::filterRole() const
2957{
2958 Q_D(const QSortFilterProxyModel);
2959 return d->filter_role;
2960}
2961
2962void QSortFilterProxyModel::setFilterRole(int role)
2963{
2964 Q_D(QSortFilterProxyModel);
2965 if (d->filter_role == role)
2966 return;
2967 d->filter_about_to_be_changed();
2968 d->filter_role = role;
2969 d->filter_changed();
2970 emit filterRoleChanged(filterRole: role);
2971}
2972
2973/*!
2974 \since 5.10
2975 \property QSortFilterProxyModel::recursiveFilteringEnabled
2976 \brief whether the filter to be applied recursively on children, and for
2977 any matching child, its parents will be visible as well.
2978
2979 The default value is false.
2980
2981 \sa filterAcceptsRow()
2982*/
2983
2984/*!
2985 \since 5.15
2986 \fn void QSortFilterProxyModel::recursiveFilteringEnabledChanged(bool recursiveFilteringEnabled)
2987 \brief This signal is emitted when the recursive filter setting is changed
2988 to \a recursiveFilteringEnabled.
2989*/
2990bool QSortFilterProxyModel::isRecursiveFilteringEnabled() const
2991{
2992 Q_D(const QSortFilterProxyModel);
2993 return d->filter_recursive;
2994}
2995
2996void QSortFilterProxyModel::setRecursiveFilteringEnabled(bool recursive)
2997{
2998 Q_D(QSortFilterProxyModel);
2999 if (d->filter_recursive == recursive)
3000 return;
3001 d->filter_about_to_be_changed();
3002 d->filter_recursive = recursive;
3003 d->filter_changed();
3004 emit recursiveFilteringEnabledChanged(recursiveFilteringEnabled: recursive);
3005}
3006
3007#if QT_DEPRECATED_SINCE(5, 11)
3008/*!
3009 \obsolete
3010
3011 This function is obsolete. Use invalidate() instead.
3012*/
3013void QSortFilterProxyModel::clear()
3014{
3015 invalidate();
3016}
3017#endif
3018/*!
3019 \since 4.3
3020
3021 Invalidates the current sorting and filtering.
3022
3023 \sa invalidateFilter()
3024*/
3025void QSortFilterProxyModel::invalidate()
3026{
3027 Q_D(QSortFilterProxyModel);
3028 emit layoutAboutToBeChanged();
3029 d->_q_clearMapping();
3030 emit layoutChanged();
3031}
3032
3033#if QT_DEPRECATED_SINCE(5, 11)
3034/*!
3035 \obsolete
3036
3037 This function is obsolete. Use invalidateFilter() instead.
3038*/
3039void QSortFilterProxyModel::filterChanged()
3040{
3041 invalidateFilter();
3042}
3043#endif
3044
3045/*!
3046 \since 4.3
3047
3048 Invalidates the current filtering.
3049
3050 This function should be called if you are implementing custom filtering
3051 (e.g. filterAcceptsRow()), and your filter parameters have changed.
3052
3053 \sa invalidate()
3054*/
3055void QSortFilterProxyModel::invalidateFilter()
3056{
3057 Q_D(QSortFilterProxyModel);
3058 d->filter_changed();
3059}
3060
3061/*!
3062 Returns \c true if the value of the item referred to by the given
3063 index \a source_left is less than the value of the item referred to by
3064 the given index \a source_right, otherwise returns \c false.
3065
3066 This function is used as the < operator when sorting, and handles
3067 the following QVariant types:
3068
3069 \list
3070 \li QMetaType::Int
3071 \li QMetaType::UInt
3072 \li QMetaType::LongLong
3073 \li QMetaType::ULongLong
3074 \li QMetaType::Float
3075 \li QMetaType::Double
3076 \li QMetaType::QChar
3077 \li QMetaType::QDate
3078 \li QMetaType::QTime
3079 \li QMetaType::QDateTime
3080 \li QMetaType::QString
3081 \endlist
3082
3083 Any other type will be converted to a QString using
3084 QVariant::toString().
3085
3086 Comparison of \l{QString}s is case sensitive by default; this can
3087 be changed using the \l {QSortFilterProxyModel::sortCaseSensitivity}
3088 {sortCaseSensitivity} property.
3089
3090 By default, the Qt::DisplayRole associated with the
3091 \l{QModelIndex}es is used for comparisons. This can be changed by
3092 setting the \l {QSortFilterProxyModel::sortRole} {sortRole} property.
3093
3094 \note The indices passed in correspond to the source model.
3095
3096 \sa sortRole, sortCaseSensitivity, dynamicSortFilter
3097*/
3098bool QSortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
3099{
3100 Q_D(const QSortFilterProxyModel);
3101 QVariant l = (source_left.model() ? source_left.model()->data(index: source_left, role: d->sort_role) : QVariant());
3102 QVariant r = (source_right.model() ? source_right.model()->data(index: source_right, role: d->sort_role) : QVariant());
3103 return QAbstractItemModelPrivate::isVariantLessThan(left: l, right: r, cs: d->sort_casesensitivity, isLocaleAware: d->sort_localeaware);
3104}
3105
3106/*!
3107 Returns \c true if the item in the row indicated by the given \a source_row
3108 and \a source_parent should be included in the model; otherwise returns
3109 false.
3110
3111 The default implementation returns \c true if the value held by the relevant item
3112 matches the filter string, wildcard string or regular expression.
3113
3114 \note By default, the Qt::DisplayRole is used to determine if the row
3115 should be accepted or not. This can be changed by setting the
3116 \l{QSortFilterProxyModel::filterRole}{filterRole} property.
3117
3118 \sa filterAcceptsColumn(), setFilterFixedString(), setFilterRegExp(), setFilterWildcard()
3119*/
3120bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
3121{
3122 Q_D(const QSortFilterProxyModel);
3123
3124 if (d->filter_data.isEmpty())
3125 return true;
3126 if (d->filter_column == -1) {
3127 int column_count = d->model->columnCount(parent: source_parent);
3128 for (int column = 0; column < column_count; ++column) {
3129 QModelIndex source_index = d->model->index(row: source_row, column, parent: source_parent);
3130 QString key = d->model->data(index: source_index, role: d->filter_role).toString();
3131 if (d->filter_data.hasMatch(str: key))
3132 return true;
3133 }
3134 return false;
3135 }
3136 QModelIndex source_index = d->model->index(row: source_row, column: d->filter_column, parent: source_parent);
3137 if (!source_index.isValid()) // the column may not exist
3138 return true;
3139 QString key = d->model->data(index: source_index, role: d->filter_role).toString();
3140 return d->filter_data.hasMatch(str: key);
3141}
3142
3143/*!
3144 Returns \c true if the item in the column indicated by the given \a source_column
3145 and \a source_parent should be included in the model; otherwise returns \c false.
3146
3147 \note The default implementation always returns \c true. You must reimplement this
3148 method to get the described behavior.
3149
3150 \sa filterAcceptsRow(), setFilterFixedString(), setFilterRegExp(), setFilterWildcard()
3151*/
3152bool QSortFilterProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
3153{
3154 Q_UNUSED(source_column);
3155 Q_UNUSED(source_parent);
3156 return true;
3157}
3158
3159/*!
3160 Returns the source model index corresponding to the given \a
3161 proxyIndex from the sorting filter model.
3162
3163 \sa mapFromSource()
3164*/
3165QModelIndex QSortFilterProxyModel::mapToSource(const QModelIndex &proxyIndex) const
3166{
3167 Q_D(const QSortFilterProxyModel);
3168 return d->proxy_to_source(proxy_index: proxyIndex);
3169}
3170
3171/*!
3172 Returns the model index in the QSortFilterProxyModel given the \a
3173 sourceIndex from the source model.
3174
3175 \sa mapToSource()
3176*/
3177QModelIndex QSortFilterProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
3178{
3179 Q_D(const QSortFilterProxyModel);
3180 return d->source_to_proxy(source_index: sourceIndex);
3181}
3182
3183/*!
3184 \reimp
3185*/
3186QItemSelection QSortFilterProxyModel::mapSelectionToSource(const QItemSelection &proxySelection) const
3187{
3188 return QAbstractProxyModel::mapSelectionToSource(selection: proxySelection);
3189}
3190
3191/*!
3192 \reimp
3193*/
3194QItemSelection QSortFilterProxyModel::mapSelectionFromSource(const QItemSelection &sourceSelection) const
3195{
3196 return QAbstractProxyModel::mapSelectionFromSource(selection: sourceSelection);
3197}
3198
3199QT_END_NAMESPACE
3200
3201#include "moc_qsortfilterproxymodel.cpp"
3202

source code of qtbase/src/corelib/itemmodels/qsortfilterproxymodel.cpp