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

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