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

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