Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qsortfilterproxymodel.cpp
Go to the documentation of this file.
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
6#include <qsize.h>
7#include <qdebug.h>
8#include <qdatetime.h>
9#include <qstringlist.h>
10#include <private/qabstractitemmodel_p.h>
11#include <private/qabstractproxymodel_p.h>
12#include <private/qproperty_p.h>
13
14#include <algorithm>
15
17
18using QModelIndexPairList = QList<std::pair<QModelIndex, QPersistentModelIndex>>;
19
28
29static inline QSet<int> qListToSet(const QList<int> &vector)
30{
31 return {vector.begin(), vector.end()};
32}
33
35{
36public:
40 : sort_column(column), source_parent(parent), source_model(source), proxy_model(proxy) {}
41
42 inline bool operator()(int r1, int r2) const
43 {
44 QModelIndex i1 = source_model->index(r1, sort_column, source_parent);
45 QModelIndex i2 = source_model->index(r2, sort_column, source_parent);
46 return proxy_model->lessThan(i1, i2);
47 }
48
49private:
50 int sort_column;
51 QModelIndex source_parent;
52 const QAbstractItemModel *source_model;
53 const QSortFilterProxyModel *proxy_model;
54};
55
57{
58public:
62 : sort_column(column), source_parent(parent),
63 source_model(source), proxy_model(proxy) {}
64
65 inline bool operator()(int r1, int r2) const
66 {
67 QModelIndex i1 = source_model->index(r1, sort_column, source_parent);
68 QModelIndex i2 = source_model->index(r2, sort_column, source_parent);
69 return proxy_model->lessThan(i2, i1);
70 }
71
72private:
73 int sort_column;
74 QModelIndex source_parent;
75 const QAbstractItemModel *source_model;
76 const QSortFilterProxyModel *proxy_model;
77};
78
79
80//this struct is used to store what are the rows that are removed
81//between a call to rowsAboutToBeRemoved and rowsRemoved
82//it avoids readding rows to the mapping that are currently being removed
84{
85 QRowsRemoval(const QModelIndex &parent_source, int start, int end) : parent_source(parent_source), start(start), end(end)
86 {
87 }
88
89 QRowsRemoval() : start(-1), end(-1)
90 {
91 }
92
93 bool contains(QModelIndex parent, int row) const
94 {
95 do {
96 if (parent == parent_source)
97 return row >= start && row <= end;
98 row = parent.row();
99 parent = parent.parent();
100 } while (row >= 0);
101 return false;
102 }
103private:
104 QModelIndex parent_source;
105 int start;
106 int end;
107};
108
110{
111public:
112 Q_DECLARE_PUBLIC(QSortFilterProxyModel)
113
114 enum class Direction {
115 Rows = 1,
116 Columns = 2,
117 All = Rows | Columns
118 };
119
120 struct Mapping {
121 QList<int> source_rows;
122 QList<int> source_columns;
123 QList<int> proxy_rows;
124 QList<int> proxy_columns;
125 QList<QModelIndex> mapped_children;
127 };
128
129 mutable QHash<QModelIndex, Mapping*> source_index_mapping;
130
132 {
133 q_func()->setSortCaseSensitivity(cs);
134 }
136 {
137 emit q_func()->sortCaseSensitivityChanged(cs);
138 }
139
140 void setSortRoleForwarder(int role) { q_func()->setSortRole(role); }
141 void sortRoleChangedForwarder(int role) { emit q_func()->sortRoleChanged(role); }
142
143 void setSortLocaleAwareForwarder(bool on) { q_func()->setSortLocaleAware(on); }
144 void sortLocaleAwareChangedForwarder(bool on) { emit q_func()->sortLocaleAwareChanged(on); }
145
146 void setFilterKeyColumnForwarder(int column) { q_func()->setFilterKeyColumn(column); }
147
148 void setFilterRoleForwarder(int role) { q_func()->setFilterRole(role); }
149 void filterRoleChangedForwarder(int role) { emit q_func()->filterRoleChanged(role); }
150
152 {
153 q_func()->setRecursiveFilteringEnabled(recursive);
154 }
156 {
157 emit q_func()->recursiveFilteringEnabledChanged(recursive);
158 }
159
160 void setAutoAcceptChildRowsForwarder(bool accept) { q_func()->setAutoAcceptChildRows(accept); }
162 {
163 emit q_func()->autoAcceptChildRowsChanged(accept);
164 }
165
166 void setDynamicSortFilterForwarder(bool enable) { q_func()->setDynamicSortFilter(enable); }
167
169 {
170 q_func()->setFilterCaseSensitivity(cs);
171 }
173 {
174 emit q_func()->filterCaseSensitivityChanged(cs);
175 }
176
178 {
179 q_func()->setFilterRegularExpression(re);
180 }
181
185 bool complete_insert = false;
186
191
195 Qt::DisplayRole)
196
199 0)
200
204 Qt::DisplayRole)
205
207 QSortFilterProxyModelPrivate, bool, sort_localeaware,
210
215
217 QSortFilterProxyModelPrivate, bool, accept_children,
220
223 true)
224
226 QSortFilterProxyModelPrivate, Qt::CaseSensitivity, filter_casesensitive,
229
233
236
239
241
242 QHash<QModelIndex, Mapping *>::const_iterator create_mapping(
243 const QModelIndex &source_parent) const;
245 const QModelIndex &source_parent) const;
246 QModelIndex proxy_to_source(const QModelIndex &proxyIndex) const;
247 QModelIndex source_to_proxy(const QModelIndex &sourceIndex) const;
248 bool can_create_mapping(const QModelIndex &source_parent) const;
249
250 void remove_from_mapping(const QModelIndex &source_parent);
251
252 /*
253 * Legacy: changing the pattern through a string does not change the
254 * case sensitivity.
255 */
257 {
258 QRegularExpression re = filter_regularexpression.valueBypassingBindings();
259 const auto cs = re.patternOptions() & QRegularExpression::CaseInsensitiveOption;
260 re.setPattern(pattern);
261 re.setPatternOptions(cs);
262 // This is a helper function, which is supposed to be called from a
263 // more complicated context. Because of that, the caller is responsible
264 // for calling notify() and removeBindingUnlessInWrapper(), if needed.
265 filter_regularexpression.setValueBypassingBindings(re);
266 }
267
269 const QModelIndex &proxy_index) const
270 {
271 Q_ASSERT(proxy_index.isValid());
272 Q_ASSERT(proxy_index.model() == q_func());
273 const void *p = proxy_index.internalPointer();
274 Q_ASSERT(p);
276 source_index_mapping.constFind(static_cast<const Mapping*>(p)->source_parent);
277 Q_ASSERT(it != source_index_mapping.constEnd());
278 Q_ASSERT(it.value());
279 return it;
280 }
281
284 {
285 return q_func()->createIndex(row, column, *it);
286 }
287
288 void _q_sourceDataChanged(const QModelIndex &source_top_left,
289 const QModelIndex &source_bottom_right,
290 const QList<int> &roles);
291 void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int start, int end);
292
294 void _q_sourceReset();
295
296 void _q_sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint);
297 void _q_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint);
298
299 void _q_sourceRowsAboutToBeInserted(const QModelIndex &source_parent,
300 int start, int end);
301 void _q_sourceRowsInserted(const QModelIndex &source_parent,
302 int start, int end);
303 void _q_sourceRowsAboutToBeRemoved(const QModelIndex &source_parent,
304 int start, int end);
305 void _q_sourceRowsRemoved(const QModelIndex &source_parent,
306 int start, int end);
307 void _q_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent,
308 int sourceStart, int sourceEnd,
309 const QModelIndex &destParent, int dest);
310 void _q_sourceRowsMoved(const QModelIndex &sourceParent,
311 int sourceStart, int sourceEnd,
312 const QModelIndex &destParent, int dest);
313 void _q_sourceColumnsAboutToBeInserted(const QModelIndex &source_parent,
314 int start, int end);
315 void _q_sourceColumnsInserted(const QModelIndex &source_parent,
316 int start, int end);
317 void _q_sourceColumnsAboutToBeRemoved(const QModelIndex &source_parent,
318 int start, int end);
319 void _q_sourceColumnsRemoved(const QModelIndex &source_parent,
320 int start, int end);
321 void _q_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent,
322 int sourceStart, int sourceEnd,
323 const QModelIndex &destParent, int dest);
324 void _q_sourceColumnsMoved(const QModelIndex &sourceParent,
325 int sourceStart, int sourceEnd,
326 const QModelIndex &destParent, int dest);
327
328 void _q_clearMapping();
329
330 void sort();
332 int find_source_sort_column() const;
333 void sort_source_rows(QList<int> &source_rows,
334 const QModelIndex &source_parent) const;
335 QList<std::pair<int, QList<int>>> proxy_intervals_for_source_items_to_add(
336 const QList<int> &proxy_to_source, const QList<int> &source_items,
337 const QModelIndex &source_parent, Qt::Orientation orient) const;
338 QList<std::pair<int, int>> proxy_intervals_for_source_items(
339 const QList<int> &source_to_proxy, const QList<int> &source_items) const;
341 QList<int> &source_to_proxy, QList<int> &proxy_to_source,
342 const QList<int> &source_items, const QModelIndex &source_parent,
343 Qt::Orientation orient, bool emit_signal = true);
345 QList<int> &source_to_proxy, QList<int> &proxy_to_source,
346 const QList<int> &source_items, const QModelIndex &source_parent,
347 Qt::Orientation orient, bool emit_signal = true);
349 QList<int> &source_to_proxy, QList<int> &proxy_to_source,
350 int proxy_start, int proxy_end, const QModelIndex &proxy_parent,
351 Qt::Orientation orient, bool emit_signal = true);
352 static inline void build_source_to_proxy_mapping(
353 const QList<int> &proxy_to_source, QList<int> &source_to_proxy, int start = 0);
354 void source_items_inserted(const QModelIndex &source_parent,
355 int start, int end, Qt::Orientation orient);
356 void source_items_about_to_be_removed(const QModelIndex &source_parent,
357 int start, int end, Qt::Orientation orient);
358 void source_items_removed(const QModelIndex &source_parent,
359 int start, int end, Qt::Orientation orient);
360 void proxy_item_range(
361 const QList<int> &source_to_proxy, const QList<int> &source_items,
362 int &proxy_low, int &proxy_high) const;
363
365 void update_persistent_indexes(const QModelIndexPairList &source_indexes);
366
367 void filter_about_to_be_changed(const QModelIndex &source_parent = QModelIndex());
368 void filter_changed(Direction dir, const QModelIndex &source_parent = QModelIndex());
369 QSet<int> handle_filter_changed(
370 QList<int> &source_to_proxy, QList<int> &proxy_to_source,
371 const QModelIndex &source_parent, Qt::Orientation orient);
372
373 void updateChildrenMapping(const QModelIndex &source_parent, Mapping *parent_mapping,
374 Qt::Orientation orient, int start, int end, int delta_item_count, bool remove);
375
376 void _q_sourceModelDestroyed() override;
377
378 bool needsReorder(const QList<int> &source_rows, const QModelIndex &source_parent) const;
379
380 bool filterAcceptsRowInternal(int source_row, const QModelIndex &source_parent) const;
381 bool recursiveChildAcceptsRow(int source_row, const QModelIndex &source_parent) const;
382 bool recursiveParentAcceptsRow(const QModelIndex &source_parent) const;
383};
384
385typedef QHash<QModelIndex, QSortFilterProxyModelPrivate::Mapping *> IndexMap;
386
391
393{
394 QAbstractProxyModelPrivate::_q_sourceModelDestroyed();
396 source_index_mapping.clear();
397}
398
399bool QSortFilterProxyModelPrivate::filterAcceptsRowInternal(int source_row, const QModelIndex &source_parent) const
400{
401 Q_Q(const QSortFilterProxyModel);
402
403 if (q->filterAcceptsRow(source_row, source_parent))
404 return true;
405
406 // Go up the tree and accept this row if a parent is accepted
407 if (accept_children && recursiveParentAcceptsRow(source_parent))
408 return true;
409
410 // Go down the tree and accept this row if a child is accepted
411 if (filter_recursive && recursiveChildAcceptsRow(source_row, source_parent))
412 return true;
413
414 return false;
415}
416
418{
419 Q_Q(const QSortFilterProxyModel);
420
421 if (source_parent.isValid()) {
422 const QModelIndex index = source_parent.parent();
423
424 if (q->filterAcceptsRow(source_parent.row(), index))
425 return true;
426
428 }
429
430 return false;
431}
432
433bool QSortFilterProxyModelPrivate::recursiveChildAcceptsRow(int source_row, const QModelIndex &source_parent) const
434{
435 Q_Q(const QSortFilterProxyModel);
436
437 const QModelIndex index = model->index(source_row, 0, source_parent);
438 const int count = model->rowCount(index);
439
440 for (int i = 0; i < count; ++i) {
441 if (q->filterAcceptsRow(i, index))
442 return true;
443
445 return true;
446 }
447
448 return false;
449}
450
452{
453 if (Mapping *m = source_index_mapping.take(source_parent)) {
454 for (const QModelIndex &mappedIdx : std::as_const(m->mapped_children))
455 remove_from_mapping(mappedIdx);
456 delete m;
457 }
458}
459
461{
462 // store the persistent indexes
464
466 source_index_mapping.clear();
469
470 // update the persistent indexes
471 update_persistent_indexes(source_indexes);
472}
473
475 const QModelIndex &source_parent) const
476{
477 Q_Q(const QSortFilterProxyModel);
478
479 IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
480 if (it != source_index_mapping.constEnd()) // was mapped already
481 return it;
482
483 Mapping *m = new Mapping;
484
485 int source_rows = model->rowCount(source_parent);
486 m->source_rows.reserve(source_rows);
487 for (int i = 0; i < source_rows; ++i) {
488 if (filterAcceptsRowInternal(i, source_parent))
489 m->source_rows.append(i);
490 }
491 int source_cols = model->columnCount(source_parent);
492 m->source_columns.reserve(source_cols);
493 for (int i = 0; i < source_cols; ++i) {
494 if (q->filterAcceptsColumn(i, source_parent))
495 m->source_columns.append(i);
496 }
497
498 sort_source_rows(m->source_rows, source_parent);
499 m->proxy_rows.resize(source_rows);
500 build_source_to_proxy_mapping(m->source_rows, m->proxy_rows);
501 m->proxy_columns.resize(source_cols);
502 build_source_to_proxy_mapping(m->source_columns, m->proxy_columns);
503
504 m->source_parent = source_parent;
505
506 if (source_parent.isValid()) {
507 QModelIndex source_grand_parent = source_parent.parent();
508 IndexMap::const_iterator it2 = create_mapping(source_grand_parent);
509 Q_ASSERT(it2 != source_index_mapping.constEnd());
510 it2.value()->mapped_children.append(source_parent);
511 }
512
513 it = IndexMap::const_iterator(source_index_mapping.insert(source_parent, m));
514 Q_ASSERT(it != source_index_mapping.constEnd());
515 Q_ASSERT(it.value());
516
517 return it;
518}
519
520// Go up the tree, creating mappings, unless of course the parent is filtered out
522{
523 if (source_parent.isValid()) {
524 const QModelIndex source_grand_parent = source_parent.parent();
525 IndexMap::const_iterator it = source_index_mapping.constFind(source_grand_parent);
527 if (it == end) {
528 it = create_mapping_recursive(source_grand_parent);
529 end = source_index_mapping.constEnd();
530 if (it == end)
531 return end;
532 }
533 Mapping *gm = it.value();
534 if (gm->proxy_rows.at(source_parent.row()) == -1 ||
535 gm->proxy_columns.at(source_parent.column()) == -1) {
536 // Can't do, parent is filtered
537 return end;
538 }
539 }
540 return create_mapping(source_parent);
541}
542
544{
545 if (!proxy_index.isValid())
546 return QModelIndex(); // for now; we may want to be able to set a root index later
547 if (proxy_index.model() != q_func()) {
548 qWarning("QSortFilterProxyModel: index from wrong model passed to mapToSource");
549 Q_ASSERT(!"QSortFilterProxyModel: index from wrong model passed to mapToSource");
550 return QModelIndex();
551 }
553 Mapping *m = it.value();
554 if ((proxy_index.row() >= m->source_rows.size()) || (proxy_index.column() >= m->source_columns.size()))
555 return QModelIndex();
556 int source_row = m->source_rows.at(proxy_index.row());
557 int source_col = m->source_columns.at(proxy_index.column());
558 return model->index(source_row, source_col, it.key());
559}
560
562{
563 if (!source_index.isValid())
564 return QModelIndex(); // for now; we may want to be able to set a root index later
565 if (source_index.model() != model) {
566 qWarning("QSortFilterProxyModel: index from wrong model passed to mapFromSource");
567 Q_ASSERT(!"QSortFilterProxyModel: index from wrong model passed to mapFromSource");
568 return QModelIndex();
569 }
570 QModelIndex source_parent = source_index.parent();
572 Mapping *m = it.value();
573 if ((source_index.row() >= m->proxy_rows.size()) || (source_index.column() >= m->proxy_columns.size()))
574 return QModelIndex();
575 int proxy_row = m->proxy_rows.at(source_index.row());
576 int proxy_column = m->proxy_columns.at(source_index.column());
577 if (proxy_row == -1 || proxy_column == -1)
578 return QModelIndex();
579 return create_index(proxy_row, proxy_column, it);
580}
581
583{
584 if (source_parent.isValid()) {
585 QModelIndex source_grand_parent = source_parent.parent();
586 IndexMap::const_iterator it = source_index_mapping.constFind(source_grand_parent);
587 if (it == source_index_mapping.constEnd()) {
588 // Don't care, since we don't have mapping for the grand parent
589 return false;
590 }
591 Mapping *gm = it.value();
592 if (gm->proxy_rows.at(source_parent.row()) == -1 ||
593 gm->proxy_columns.at(source_parent.column()) == -1) {
594 // Don't care, since parent is filtered
595 return false;
596 }
597 }
598 return true;
599}
600
607{
609 emit q->layoutAboutToBeChanged(QList<QPersistentModelIndex>(), QAbstractItemModel::VerticalSortHint);
611 const auto end = source_index_mapping.constEnd();
612 for (auto it = source_index_mapping.constBegin(); it != end; ++it) {
613 const QModelIndex &source_parent = it.key();
614 Mapping *m = it.value();
615 sort_source_rows(m->source_rows, source_parent);
616 build_source_to_proxy_mapping(m->source_rows, m->proxy_rows);
617 }
618 update_persistent_indexes(source_indexes);
619 emit q->layoutChanged(QList<QPersistentModelIndex>(), QAbstractItemModel::VerticalSortHint);
620}
621
629{
630 int old_source_sort_column = source_sort_column;
631
632 if (proxy_sort_column == -1) {
634 } else {
635 // We cannot use index mapping here because in case of a still-empty
636 // proxy model there's no valid proxy index we could map to source.
637 // So always use the root mapping directly instead.
638 Mapping *m = create_mapping(QModelIndex()).value();
639 if (proxy_sort_column < m->source_columns.size())
640 source_sort_column = m->source_columns.at(proxy_sort_column);
641 else
643 }
644
645 return old_source_sort_column != source_sort_column;
646}
647
655{
656 if (proxy_sort_column == -1)
657 return -1;
658
659 const QModelIndex rootIndex;
660 const int source_cols = model->columnCount();
661 int accepted_columns = -1;
662
663 Q_Q(const QSortFilterProxyModel);
664 for (int i = 0; i < source_cols; ++i) {
665 if (q->filterAcceptsColumn(i, rootIndex)) {
666 if (++accepted_columns == proxy_sort_column)
667 return i;
668 }
669 }
670
671 return -1;
672}
673
680 QList<int> &source_rows, const QModelIndex &source_parent) const
681{
682 Q_Q(const QSortFilterProxyModel);
683 if (source_sort_column >= 0) {
686 std::stable_sort(source_rows.begin(), source_rows.end(), lt);
687 } else {
689 std::stable_sort(source_rows.begin(), source_rows.end(), gt);
690 }
691 } else if (sort_order == Qt::AscendingOrder) {
692 std::stable_sort(source_rows.begin(), source_rows.end(), std::less{});
693 } else {
694 std::stable_sort(source_rows.begin(), source_rows.end(), std::greater{});
695 }
696}
697
710 const QList<int> &source_to_proxy, const QList<int> &source_items) const
711{
712 QList<std::pair<int, int>> proxy_intervals;
713 if (source_items.isEmpty())
714 return proxy_intervals;
715
716 int source_items_index = 0;
717 while (source_items_index < source_items.size()) {
718 int first_proxy_item = source_to_proxy.at(source_items.at(source_items_index));
719 Q_ASSERT(first_proxy_item != -1);
720 int last_proxy_item = first_proxy_item;
721 ++source_items_index;
722 // Find end of interval
723 while ((source_items_index < source_items.size())
724 && (source_to_proxy.at(source_items.at(source_items_index)) == last_proxy_item + 1)) {
725 ++last_proxy_item;
726 ++source_items_index;
727 }
728 // Add interval to result
729 proxy_intervals.emplace_back(first_proxy_item, last_proxy_item);
730 }
731 std::stable_sort(proxy_intervals.begin(), proxy_intervals.end());
732 // Consolidate adjacent intervals
733 for (int i = proxy_intervals.size()-1; i > 0; --i) {
734 std::pair<int, int> &interval = proxy_intervals[i];
735 std::pair<int, int> &preceeding_interval = proxy_intervals[i - 1];
736 if (interval.first == preceeding_interval.second + 1) {
737 preceeding_interval.second = interval.second;
738 interval.first = interval.second = -1;
739 }
740 }
741 proxy_intervals.removeIf([](std::pair<int, int> interval) { return interval.first < 0; });
742 return proxy_intervals;
743}
744
754 QList<int> &source_to_proxy, QList<int> &proxy_to_source,
755 const QList<int> &source_items, const QModelIndex &source_parent,
756 Qt::Orientation orient, bool emit_signal)
757{
759 QModelIndex proxy_parent = q->mapFromSource(source_parent);
760 if (!proxy_parent.isValid() && source_parent.isValid()) {
761 proxy_to_source.clear();
762 return; // nothing to do (already removed)
763 }
764
765 const auto proxy_intervals = proxy_intervals_for_source_items(
766 source_to_proxy, source_items);
767
768 const auto end = proxy_intervals.rend();
769 for (auto it = proxy_intervals.rbegin(); it != end; ++it) {
770 const std::pair<int, int> &interval = *it;
771 const int proxy_start = interval.first;
772 const int proxy_end = interval.second;
774 proxy_parent, orient, emit_signal);
775 }
776}
777
786 QList<int> &source_to_proxy, QList<int> &proxy_to_source, int proxy_start, int proxy_end,
787 const QModelIndex &proxy_parent, Qt::Orientation orient, bool emit_signal)
788{
790 if (emit_signal) {
791 if (orient == Qt::Vertical)
792 q->beginRemoveRows(proxy_parent, proxy_start, proxy_end);
793 else
794 q->beginRemoveColumns(proxy_parent, proxy_start, proxy_end);
795 }
796
797 // Remove items from proxy-to-source mapping
798 for (int i = proxy_start; i <= proxy_end; ++i)
800 proxy_to_source.remove(proxy_start, proxy_end - proxy_start + 1);
801
803
804 if (emit_signal) {
805 if (orient == Qt::Vertical)
806 q->endRemoveRows();
807 else
808 q->endRemoveColumns();
809 }
810}
811
825 const QList<int> &proxy_to_source, const QList<int> &source_items,
826 const QModelIndex &source_parent, Qt::Orientation orient) const
827{
828 Q_Q(const QSortFilterProxyModel);
829 QList<std::pair<int, QList<int>>> proxy_intervals;
830 if (source_items.isEmpty())
831 return proxy_intervals;
832
833 int proxy_low = 0;
834 int proxy_item = 0;
835 int source_items_index = 0;
836 bool compare = (orient == Qt::Vertical && source_sort_column >= 0 && dynamic_sortfilter);
837 while (source_items_index < source_items.size()) {
838 QList<int> source_items_in_interval;
839 int first_new_source_item = source_items.at(source_items_index);
840 source_items_in_interval.append(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(first_new_source_item, source_sort_column, 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(proxy_to_source.at(proxy_item), source_sort_column, source_parent);
850 if ((sort_order == Qt::AscendingOrder) ? q->lessThan(i1, i2) : q->lessThan(i2, 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(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(source_items.at(source_items_index));
867 } else {
868 i1 = compare ? model->index(proxy_to_source.at(proxy_item), source_sort_column, source_parent) : QModelIndex();
869 for ( ; source_items_index < source_items.size(); ++source_items_index) {
870 int new_source_item = source_items.at(source_items_index);
871 if (compare) {
872 QModelIndex i2 = model->index(new_source_item, source_sort_column, source_parent);
873 if ((sort_order == Qt::AscendingOrder) ? q->lessThan(i1, i2) : q->lessThan(i2, i1))
874 break;
875 } else {
876 if (proxy_to_source.at(proxy_item) < new_source_item)
877 break;
878 }
879 source_items_in_interval.append(new_source_item);
880 }
881 }
882
883 // Add interval to result
884 proxy_intervals.emplace_back(proxy_item, std::move(source_items_in_interval));
885 }
886 return proxy_intervals;
887}
888
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{
903 QModelIndex proxy_parent = q->mapFromSource(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 std::pair<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(proxy_parent, proxy_start, proxy_end);
920 else
921 q->beginInsertColumns(proxy_parent, proxy_start, proxy_end);
922 }
923
924 // TODO: use the range QList::insert() overload once it is implemented (QTBUG-58633).
925 proxy_to_source.insert(proxy_start, source_items.size(), 0);
926 std::copy(source_items.cbegin(), source_items.cend(), proxy_to_source.begin() + proxy_start);
927
929
930 if (emit_signal) {
931 if (orient == Qt::Vertical)
932 q->endInsertRows();
933 else
934 q->endInsertColumns();
935 }
936 }
937}
938
952 const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
953{
955 if ((start < 0) || (end < 0))
956 return;
957 IndexMap::const_iterator it = source_index_mapping.constFind(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(source_parent);
964 if (m->source_rows.size() > 0) {
965 q->beginInsertRows(proxy_parent, 0, m->source_rows.size() - 1);
966 q->endInsertRows();
967 }
968 if (m->source_columns.size() > 0) {
969 q->beginInsertColumns(proxy_parent, 0, 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, m, orient, start, end, delta_item_count, false);
983
984 // Expand source-to-proxy mapping to account for new items
985 if (start < 0 || start > source_to_proxy.size()) {
986 qWarning("QSortFilterProxyModel: invalid inserted rows reported by source model");
987 remove_from_mapping(source_parent);
988 return;
989 }
990 source_to_proxy.insert(start, delta_item_count, -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(proxy_item);
997 if (source_item >= start)
998 proxy_to_source.replace(proxy_item, source_item + delta_item_count);
999 }
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(i, source_parent)
1008 : q->filterAcceptsColumn(i, source_parent)) {
1009 source_items.append(i);
1010 }
1011 }
1012
1013 if (model->rowCount(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(source_parent) : model->columnCount(source_parent);
1023
1024 orthogonal_source_to_proxy.resize(ortho_end);
1025
1026 for (int ortho_item = 0; ortho_item < ortho_end; ++ortho_item) {
1027 if ((orient == Qt::Horizontal) ? filterAcceptsRowInternal(ortho_item, source_parent)
1028 : q->filterAcceptsColumn(ortho_item, source_parent)) {
1029 orthogonal_proxy_to_source.append(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(orthogonal_proxy_to_source, source_parent);
1035 }
1036 build_source_to_proxy_mapping(orthogonal_proxy_to_source, 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_items, source_parent);
1043 insert_source_items(source_to_proxy, proxy_to_source, source_items, source_parent, orient);
1044}
1045
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(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(proxy_item);
1072 if ((source_item >= start) && (source_item <= end))
1073 source_items_to_remove.append(source_item);
1074 }
1075
1076 remove_source_items(source_to_proxy, proxy_to_source, source_items_to_remove,
1077 source_parent, orient);
1078}
1079
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(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(start, 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("QSortFilterProxyModel: inconsistent changes reported by source model");
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(proxy_item);
1121 if (source_item >= start) {
1122 Q_ASSERT(source_item - delta_item_count >= 0);
1123 proxy_to_source.replace(proxy_item, source_item - delta_item_count);
1124 }
1125 }
1127
1128 updateChildrenMapping(source_parent, m, orient, start, end, delta_item_count, true);
1129
1130}
1131
1132
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<std::pair<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(it2);
1154 remove_from_mapping(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(newpos,
1161 source_child_index.column(),
1162 source_parent);
1163 } else {
1164 new_index = model->index(source_child_index.row(),
1165 newpos,
1166 source_parent);
1167 }
1168 *it2 = new_index;
1169 ++it2;
1170
1171 // update mapping
1172 Mapping *cm = source_index_mapping.take(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.emplace_back(new_index, cm);
1176 }
1177 }
1178
1179 // reinsert moved, mapped indexes
1180 for (auto &pair : std::as_const(moved_source_index_mappings)) {
1181 pair.second->source_parent = pair.first;
1182 source_index_mapping.insert(pair.first, pair.second);
1183 }
1184}
1185
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(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
1209 const QList<int> &proxy_to_source, QList<int> &source_to_proxy, int start)
1210{
1211 if (start == 0)
1212 source_to_proxy.fill(-1);
1213 const int proxy_count = proxy_to_source.size();
1214 for (int i = start; i < proxy_count; ++i)
1216}
1217
1225{
1226 Q_Q(const QSortFilterProxyModel);
1227 QModelIndexPairList source_indexes;
1228 source_indexes.reserve(persistent.indexes.size());
1229 for (const QPersistentModelIndexData *data : std::as_const(persistent.indexes)) {
1230 const QModelIndex &proxy_index = data->index;
1231 QModelIndex source_index = q->mapToSource(proxy_index);
1232 source_indexes.emplace_back(proxy_index, source_index);
1233 }
1234 return source_indexes;
1235}
1236
1244 const QModelIndexPairList &source_indexes)
1245{
1247 QModelIndexList from, to;
1248 const int numSourceIndexes = source_indexes.size();
1249 from.reserve(numSourceIndexes);
1250 to.reserve(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_index.parent());
1255 QModelIndex proxy_index = q->mapFromSource(source_index);
1256 from << old_proxy_index;
1257 to << proxy_index;
1258 }
1259 q->changePersistentIndexList(from, to);
1260}
1261
1269{
1270 if (!filter_regularexpression.valueBypassingBindings().pattern().isEmpty()
1271 && source_index_mapping.constFind(source_parent) == source_index_mapping.constEnd()) {
1272 create_mapping(source_parent);
1273 }
1274}
1275
1276
1284{
1285 IndexMap::const_iterator it = source_index_mapping.constFind(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(m->proxy_rows, m->source_rows, source_parent, Qt::Vertical) : QSet<int>();
1290 const QSet<int> columns_removed = (dir & Direction::Columns) ? handle_filter_changed(m->proxy_columns, m->source_columns, source_parent, 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(source_child_index.row()) || columns_removed.contains(source_child_index.column())) {
1301 indexesToRemove.push_back(i);
1302 remove_from_mapping(source_child_index);
1303 } else {
1304 filter_changed(dir, 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(*removeIt);
1318 }
1319}
1320
1326 QList<int> &source_to_proxy, QList<int> &proxy_to_source,
1327 const QModelIndex &source_parent, Qt::Orientation orient)
1328{
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_item, source_parent)
1336 : !q->filterAcceptsColumn(source_item, source_parent)) {
1337 // This source item does not satisfy the filter, so it must be removed
1338 source_items_remove.append(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(source_item) == -1) {
1346 if ((orient == Qt::Vertical)
1347 ? filterAcceptsRowInternal(source_item, source_parent)
1348 : q->filterAcceptsColumn(source_item, source_parent)) {
1349 // This source item satisfies the filter, so it must be added
1350 source_items_insert.append(source_item);
1351 }
1352 }
1353 }
1354 if (!source_items_remove.isEmpty() || !source_items_insert.isEmpty()) {
1355 // Do item removal and insertion
1357 source_items_remove, source_parent, orient);
1358 if (orient == Qt::Vertical)
1359 sort_source_rows(source_items_insert, source_parent);
1361 source_items_insert, source_parent, orient);
1362 }
1363 return qListToSet(source_items_remove);
1364}
1365
1366bool QSortFilterProxyModelPrivate::needsReorder(const QList<int> &source_rows, const QModelIndex &source_parent) const
1367{
1368 Q_Q(const QSortFilterProxyModel);
1370 const int proxyRowCount = q->rowCount(source_to_proxy(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(source_rows.begin(), source_rows.end(),
1373 [this, q, proxyRowCount, source_parent](int sourceRow) -> bool {
1374 const QModelIndex sourceIndex = model->index(sourceRow, source_sort_column, source_parent);
1375 const QModelIndex proxyIndex = source_to_proxy(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(proxyIndex.row() - 1, proxy_sort_column, proxyIndex);
1379 const QModelIndex prevSourceIndex = proxy_to_source(prevProxyIndex);
1380 if (sort_order == Qt::AscendingOrder ? q->lessThan(sourceIndex, prevSourceIndex) : q->lessThan(prevSourceIndex, sourceIndex))
1381 return true;
1382 }
1383 if (proxyIndex.row() < proxyRowCount - 1) {
1384 const QModelIndex nextProxyIndex = q->sibling(proxyIndex.row() + 1, proxy_sort_column, proxyIndex);
1385 const QModelIndex nextSourceIndex = proxy_to_source(nextProxyIndex);
1386 if (sort_order == Qt::AscendingOrder ? q->lessThan(nextSourceIndex, sourceIndex) : q->lessThan(sourceIndex, nextSourceIndex))
1387 return true;
1388 }
1389 return false;
1390 });
1391}
1392
1394 const QModelIndex &source_bottom_right,
1395 const QList<int> &roles)
1396{
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(source_top_left, 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(filter_role))) {
1406 QModelIndex source_parent = source_top_left.parent();
1407
1408 while (source_parent.isValid()) {
1409 data_changed_list.emplace_back(source_parent, 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(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(source_bottom_right.row(), 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(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(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(source_row);
1450 } else {
1451 // This row has simply changed, without affecting filtering nor sorting
1452 source_rows_change.append(source_row);
1453 }
1454 } else {
1455 if (!itemsBeingRemoved.contains(source_parent, 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(source_row);
1458 }
1459 }
1460 } else {
1461 if (m->proxy_rows.at(source_row) != -1)
1462 source_rows_change.append(source_row);
1463 }
1464 }
1465
1466 if (!source_rows_remove.isEmpty()) {
1467 remove_source_items(m->proxy_rows, m->source_rows,
1468 source_rows_remove, source_parent, Qt::Vertical);
1469 QSet<int> source_rows_remove_set = qListToSet(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(source_child_index.row())) {
1475 childIt = m->mapped_children.erase(childIt);
1476 remove_from_mapping(source_child_index);
1477 }
1478 }
1479 }
1480
1481 if (!source_rows_resort.isEmpty()) {
1482 if (needsReorder(source_rows_resort, source_parent)) {
1483 // Re-sort the rows of this level
1484 QList<QPersistentModelIndex> parents;
1485 parents << q->mapFromSource(source_parent);
1486 emit q->layoutAboutToBeChanged(parents, QAbstractItemModel::VerticalSortHint);
1488 remove_source_items(m->proxy_rows, m->source_rows, source_rows_resort,
1489 source_parent, Qt::Vertical, false);
1490 sort_source_rows(source_rows_resort, source_parent);
1491 insert_source_items(m->proxy_rows, m->source_rows, source_rows_resort,
1492 source_parent, Qt::Vertical, false);
1493 update_persistent_indexes(source_indexes);
1494 emit q->layoutChanged(parents, 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(m->proxy_rows, source_rows_change,
1505 proxy_start_row, 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(source_left_column) == -1)
1512 ++source_left_column;
1513 if (m->proxy_columns.at(source_left_column) != -1) {
1514 const QModelIndex proxy_top_left = create_index(
1515 proxy_start_row, m->proxy_columns.at(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(source_right_column) == -1)
1519 --source_right_column;
1520 if (m->proxy_columns.at(source_right_column) != -1) {
1521 const QModelIndex proxy_bottom_right = create_index(
1522 proxy_end_row, m->proxy_columns.at(source_right_column), it);
1523 emit q->dataChanged(proxy_top_left, proxy_bottom_right, roles);
1524 }
1525 }
1526 }
1527 }
1528
1529 if (!source_rows_insert.isEmpty()) {
1530 sort_source_rows(source_rows_insert, source_parent);
1531 insert_source_items(m->proxy_rows, m->source_rows,
1532 source_rows_insert, source_parent, Qt::Vertical);
1533 }
1534 }
1535}
1536
1538 int start, int end)
1539{
1540 Q_ASSERT(start <= end);
1541
1543 Mapping *m = create_mapping(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(end - start + 1);
1549 {
1550 Q_ASSERT(source_to_proxy.size() > end);
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(*it);
1556 }
1557 }
1558
1559 std::sort(proxy_positions.begin(), 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(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, proxyStart, proxyEnd);
1576 }
1577}
1578
1580{
1582 q->beginResetModel();
1583}
1584
1586{
1590 // All internal structures are deleted in clear()
1591 q->endResetModel();
1593 sort();
1594}
1595
1597{
1599 Q_UNUSED(hint); // We can't forward Hint because we might filter additional rows or columns
1601
1603 for (const QPersistentModelIndex &parent : sourceParents) {
1604 if (!parent.isValid()) {
1606 continue;
1607 }
1608 const QModelIndex mappedParent = q->mapFromSource(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(saved_layoutChange_parents);
1619 if (persistent.indexes.isEmpty())
1620 return;
1621
1623}
1624
1626{
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.
1636 source_index_mapping.clear();
1637
1640
1643
1644 emit q->layoutChanged(saved_layoutChange_parents);
1646}
1647
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_parent.row(), 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(parent.row(), 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
1680 const QModelIndex &source_parent, int start, int end)
1681{
1683 if (filter_recursive)
1684 complete_insert = false;
1685 source_items_inserted(source_parent, start, end, 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(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
1706 }
1707}
1708
1710 const QModelIndex &source_parent, int start, int end)
1711{
1712 itemsBeingRemoved = QRowsRemoval(source_parent, start, end);
1714 Qt::Vertical);
1715}
1716
1718 const QModelIndex &source_parent, int start, int end)
1719{
1721 source_items_removed(source_parent, start, end, 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_ascendant.row(), 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(to_hide, to_hide, QList<int>());
1742 }
1743}
1744
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;
1759}
1760
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;
1769}
1770
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
1783 const QModelIndex &source_parent, int start, int end)
1784{
1785 Q_Q(const QSortFilterProxyModel);
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
1793 sort();
1794 } else {
1796 source_sort_column += end - start + 1;
1797
1798 proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column();
1799 }
1800}
1801
1808
1810 const QModelIndex &source_parent, int start, int end)
1811{
1812 Q_Q(const QSortFilterProxyModel);
1813 source_items_removed(source_parent, start, end, 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(model->index(0,source_sort_column, source_parent)).column();
1826 else
1827 proxy_sort_column = -1;
1828}
1829
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;
1838}
1839
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;
1848}
1849
2015
2020{
2022 qDeleteAll(d->source_index_mapping);
2023 d->source_index_mapping.clear();
2024}
2025
2030{
2032
2033 if (sourceModel == d->model)
2034 return;
2035
2037
2038 if (d->model) {
2039 for (const QMetaObject::Connection &connection : std::as_const(d->sourceConnections))
2041 }
2042
2043 // same as in _q_sourceReset()
2044 d->invalidatePersistentIndexes();
2045 d->_q_clearMapping();
2046
2048
2049 d->sourceConnections = std::array<QMetaObject::Connection, 18>{
2052
2055
2058
2061
2064
2067
2070
2073
2076
2079
2082
2085
2088
2091
2094
2097
2100
2103 };
2104 endResetModel();
2105 if (d->update_source_sort_column() && d->dynamic_sortfilter)
2106 d->sort();
2107}
2108
2113{
2114 Q_D(const QSortFilterProxyModel);
2115 if (row < 0 || column < 0)
2116 return QModelIndex();
2117
2118 QModelIndex source_parent = mapToSource(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
2130{
2131 Q_D(const QSortFilterProxyModel);
2132 if (!d->indexValid(child))
2133 return QModelIndex();
2134 IndexMap::const_iterator it = d->index_to_iterator(child);
2135 Q_ASSERT(it != d->source_index_mapping.constEnd());
2136 QModelIndex source_parent = it.key();
2137 QModelIndex proxy_parent = mapFromSource(source_parent);
2138 return proxy_parent;
2139}
2140
2145{
2146 Q_D(const QSortFilterProxyModel);
2147 if (!d->indexValid(idx))
2148 return QModelIndex();
2149
2150 const IndexMap::const_iterator it = d->index_to_iterator(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
2161{
2162 Q_D(const QSortFilterProxyModel);
2163 QModelIndex source_parent = mapToSource(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
2174{
2175 Q_D(const QSortFilterProxyModel);
2176 QModelIndex source_parent = mapToSource(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
2187{
2188 Q_D(const QSortFilterProxyModel);
2189 QModelIndex source_parent = mapToSource(parent);
2190 if (parent.isValid() && !source_parent.isValid())
2191 return false;
2192 if (!d->model->hasChildren(source_parent))
2193 return false;
2194
2195 if (d->model->canFetchMore(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
2206{
2207 Q_D(const QSortFilterProxyModel);
2208 QModelIndex source_index = mapToSource(index);
2209 if (index.isValid() && !source_index.isValid())
2210 return QVariant();
2211 return d->model->data(source_index, role);
2212}
2213
2218{
2220 QModelIndex source_index = mapToSource(index);
2221 if (index.isValid() && !source_index.isValid())
2222 return false;
2223 return d->model->setData(source_index, value, role);
2224}
2225
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(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(section);
2240 } else {
2241 if (section < 0 || section >= it.value()->source_columns.size())
2242 return QVariant();
2243 source_section = it.value()->source_columns.at(section);
2244 }
2245 return d->model->headerData(source_section, orientation, role);
2246}
2247
2252 const QVariant &value, int role)
2253{
2255 IndexMap::const_iterator it = d->create_mapping(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(section);
2263 } else {
2264 if (section < 0 || section >= it.value()->source_columns.size())
2265 return false;
2266 source_section = it.value()->source_columns.at(section);
2267 }
2268 return d->model->setHeaderData(source_section, orientation, value, role);
2269}
2270
2275{
2276 Q_D(const QSortFilterProxyModel);
2277 QModelIndexList source_indexes;
2278 source_indexes.reserve(indexes.size());
2279 for (const QModelIndex &idx : indexes)
2280 source_indexes << mapToSource(idx);
2281 return d->model->mimeData(source_indexes);
2282}
2283
2288{
2289 Q_D(const QSortFilterProxyModel);
2290 return d->model->mimeTypes();
2291}
2292
2297{
2298 Q_D(const QSortFilterProxyModel);
2299 return d->model->supportedDropActions();
2300}
2301
2302// Qt6: remove unnecessary reimplementation
2307 int row, int column, const QModelIndex &parent)
2308{
2310}
2311
2316{
2318 if (row < 0 || count <= 0)
2319 return false;
2320 QModelIndex source_parent = mapToSource(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(row));
2329 return d->model->insertRows(source_row, count, source_parent);
2330}
2331
2336{
2338 if (column < 0|| count <= 0)
2339 return false;
2340 QModelIndex source_parent = mapToSource(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(column));
2349 return d->model->insertColumns(source_column, count, source_parent);
2350}
2351
2356{
2358 if (row < 0 || count <= 0)
2359 return false;
2360 QModelIndex source_parent = mapToSource(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(row);
2369 return d->model->removeRows(source_row, count, 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(count);
2375 for (int i = row; i < row + count; ++i)
2376 rows.append(m->source_rows.at(i));
2377 std::sort(rows.begin(), rows.end());
2378
2379 int pos = rows.size() - 1;
2380 bool ok = true;
2381 while (pos >= 0) {
2382 const int source_end = rows.at(pos--);
2383 int source_start = source_end;
2384 while ((pos >= 0) && (rows.at(pos) == (source_start - 1))) {
2385 --source_start;
2386 --pos;
2387 }
2388 ok = ok && d->model->removeRows(source_start, source_end - source_start + 1,
2389 source_parent);
2390 }
2391 return ok;
2392}
2393
2398{
2400 if (column < 0 || count <= 0)
2401 return false;
2402 QModelIndex source_parent = mapToSource(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(column);
2410 return d->model->removeColumns(source_column, count, source_parent);
2411 }
2412 // remove corresponding source intervals
2413 QList<int> columns;
2414 columns.reserve(count);
2415 for (int i = column; i < column + count; ++i)
2416 columns.append(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(pos--);
2422 int source_start = source_end;
2423 while ((pos >= 0) && (columns.at(pos) == (source_start - 1))) {
2424 --source_start;
2425 --pos;
2426 }
2427 ok = ok && d->model->removeColumns(source_start, source_end - source_start + 1,
2428 source_parent);
2429 }
2430 return ok;
2431}
2432
2440
2448
2453{
2455}
2456
2461{
2462 Q_D(const QSortFilterProxyModel);
2463 if (!d->indexValid(index))
2464 return QModelIndex();
2465 QModelIndex source_index = mapToSource(index);
2466 QModelIndex source_buddy = d->model->buddy(source_index);
2467 if (source_index == source_buddy)
2468 return index;
2469 return mapFromSource(source_buddy);
2470}
2471
2476 const QVariant &value, int hits,
2477 Qt::MatchFlags flags) const
2478{
2479 return QAbstractProxyModel::match(start, role, value, hits, flags);
2480}
2481
2486{
2487 Q_D(const QSortFilterProxyModel);
2488 QModelIndex source_index = mapToSource(index);
2489 if (index.isValid() && !source_index.isValid())
2490 return QSize();
2491 return d->model->span(source_index);
2492}
2493
2501{
2503 if (d->dynamic_sortfilter && d->proxy_sort_column == column && d->sort_order == order)
2504 return;
2505 d->sort_order = order;
2506 d->proxy_sort_column = column;
2507 d->update_source_sort_column();
2508 d->sort();
2509}
2510
2521{
2522 Q_D(const QSortFilterProxyModel);
2523 return d->proxy_sort_column;
2524}
2525
2536{
2537 Q_D(const QSortFilterProxyModel);
2538 return d->sort_order;
2539}
2540
2562{
2563 Q_D(const QSortFilterProxyModel);
2564 return d->filter_regularexpression;
2565}
2566
2568{
2570 return QBindable<QRegularExpression>(&d->filter_regularexpression);
2571}
2572
2574{
2576 const QScopedPropertyUpdateGroup guard;
2577 const bool regExpChanged =
2578 regularExpression != d->filter_regularexpression.valueBypassingBindings();
2579 d->filter_regularexpression.removeBindingUnlessInWrapper();
2580 d->filter_casesensitive.removeBindingUnlessInWrapper();
2581 const Qt::CaseSensitivity cs = d->filter_casesensitive.valueBypassingBindings();
2582 d->filter_about_to_be_changed();
2583 const Qt::CaseSensitivity updatedCs =
2586 d->filter_regularexpression.setValueBypassingBindings(regularExpression);
2587 if (cs != updatedCs)
2588 d->filter_casesensitive.setValueBypassingBindings(updatedCs);
2590 // Do not change the evaluation logic, but notify only if the regular
2591 // expression has actually changed.
2592 if (regExpChanged)
2593 d->filter_regularexpression.notify();
2594 if (cs != updatedCs)
2595 d->filter_casesensitive.notify();
2596}
2597
2607{
2608 Q_D(const QSortFilterProxyModel);
2609 return d->filter_column;
2610}
2611
2613{
2614 // While introducing new bindable properties, we still update the value
2615 // unconditionally (even if it didn't really change), and call the
2616 // filter_about_to_be_changed()/filter_changed() methods, so that we do
2617 // not break any code. However we do notify the observing bindings only
2618 // if the column has actually changed
2620 d->filter_column.removeBindingUnlessInWrapper();
2621 d->filter_about_to_be_changed();
2622 const auto oldColumn = d->filter_column.valueBypassingBindings();
2623 d->filter_column.setValueBypassingBindings(column);
2625 if (oldColumn != column)
2626 d->filter_column.notify();
2627}
2628
2630{
2632 return QBindable<int>(&d->filter_column);
2633}
2634
2658{
2659 Q_D(const QSortFilterProxyModel);
2660 return d->filter_casesensitive;
2661}
2662
2664{
2666 d->filter_casesensitive.removeBindingUnlessInWrapper();
2667 d->filter_regularexpression.removeBindingUnlessInWrapper();
2668 if (cs == d->filter_casesensitive)
2669 return;
2670
2671 const QScopedPropertyUpdateGroup guard;
2672 QRegularExpression::PatternOptions options =
2673 d->filter_regularexpression.value().patternOptions();
2675 d->filter_casesensitive.setValueBypassingBindings(cs);
2676
2677 d->filter_about_to_be_changed();
2678 QRegularExpression re = d->filter_regularexpression;
2679 re.setPatternOptions(options);
2680 d->filter_regularexpression.setValueBypassingBindings(re);
2682 d->filter_regularexpression.notify();
2683 d->filter_casesensitive.notify();
2684}
2685
2687{
2689 return QBindable<Qt::CaseSensitivity>(&d->filter_casesensitive);
2690}
2691
2709{
2710 Q_D(const QSortFilterProxyModel);
2711 return d->sort_casesensitivity;
2712}
2713
2715{
2717 d->sort_casesensitivity.removeBindingUnlessInWrapper();
2718 if (d->sort_casesensitivity == cs)
2719 return;
2720
2721 d->sort_casesensitivity.setValueBypassingBindings(cs);
2722 d->sort();
2723 d->sort_casesensitivity.notify(); // also emits a signal
2724}
2725
2727{
2729 return QBindable<Qt::CaseSensitivity>(&d->sort_casesensitivity);
2730}
2731
2749{
2750 Q_D(const QSortFilterProxyModel);
2751 return d->sort_localeaware;
2752}
2753
2755{
2757 d->sort_localeaware.removeBindingUnlessInWrapper();
2758 if (d->sort_localeaware == on)
2759 return;
2760
2761 d->sort_localeaware.setValueBypassingBindings(on);
2762 d->sort();
2763 d->sort_localeaware.notify(); // also emits a signal
2764}
2765
2767{
2769 return QBindable<bool>(&d->sort_localeaware);
2770}
2771
2791{
2793 d->filter_regularexpression.removeBindingUnlessInWrapper();
2794 d->filter_about_to_be_changed();
2795 d->set_filter_pattern(pattern);
2797 d->filter_regularexpression.notify();
2798}
2799
2814{
2816 d->filter_regularexpression.removeBindingUnlessInWrapper();
2817 d->filter_about_to_be_changed();
2821 d->filter_regularexpression.notify();
2822}
2823
2838{
2840 d->filter_regularexpression.removeBindingUnlessInWrapper();
2841 d->filter_about_to_be_changed();
2842 d->set_filter_pattern(QRegularExpression::escape(pattern));
2844 d->filter_regularexpression.notify();
2845}
2846
2866{
2867 Q_D(const QSortFilterProxyModel);
2868 return d->dynamic_sortfilter;
2869}
2870
2872{
2873 // While introducing new bindable properties, we still update the value
2874 // unconditionally (even if it didn't really change), and call the
2875 // sort() method, so that we do not break any code.
2876 // However we do notify the observing bindings only if the value has
2877 // actually changed.
2879 d->dynamic_sortfilter.removeBindingUnlessInWrapper();
2880 const bool valueChanged = d->dynamic_sortfilter.value() != enable;
2881 d->dynamic_sortfilter.setValueBypassingBindings(enable);
2882 if (enable)
2883 d->sort();
2884 if (valueChanged)
2885 d->dynamic_sortfilter.notify();
2886}
2887
2889{
2891 return QBindable<bool>(&d->dynamic_sortfilter);
2892}
2893
2911{
2912 Q_D(const QSortFilterProxyModel);
2913 return d->sort_role;
2914}
2915
2917{
2919 d->sort_role.removeBindingUnlessInWrapper();
2920 if (d->sort_role.valueBypassingBindings() == role)
2921 return;
2922 d->sort_role.setValueBypassingBindings(role);
2923 d->sort();
2924 d->sort_role.notify(); // also emits a signal
2925}
2926
2928{
2930 return QBindable<int>(&d->sort_role);
2931}
2932
2950{
2951 Q_D(const QSortFilterProxyModel);
2952 return d->filter_role;
2953}
2954
2956{
2958 d->filter_role.removeBindingUnlessInWrapper();
2959 if (d->filter_role.valueBypassingBindings() == role)
2960 return;
2961 d->filter_about_to_be_changed();
2962 d->filter_role.setValueBypassingBindings(role);
2964 d->filter_role.notify(); // also emits a signal
2965}
2966
2968{
2970 return QBindable<int>(&d->filter_role);
2971}
2972
2992{
2993 Q_D(const QSortFilterProxyModel);
2994 return d->filter_recursive;
2995}
2996
2998{
3000 d->filter_recursive.removeBindingUnlessInWrapper();
3001 if (d->filter_recursive == recursive)
3002 return;
3003 d->filter_about_to_be_changed();
3004 d->filter_recursive.setValueBypassingBindings(recursive);
3006 d->filter_recursive.notify(); // also emits a signal
3007}
3008
3010{
3012 return QBindable<bool>(&d->filter_recursive);
3013}
3014
3036{
3037 Q_D(const QSortFilterProxyModel);
3038 return d->accept_children;
3039}
3040
3042{
3044 d->accept_children.removeBindingUnlessInWrapper();
3045 if (d->accept_children == accept)
3046 return;
3047
3048 d->filter_about_to_be_changed();
3049 d->accept_children.setValueBypassingBindings(accept);
3051 d->accept_children.notify(); // also emits a signal
3052}
3053
3055{
3057 return QBindable<bool>(&d->accept_children);
3058}
3059
3068{
3071 d->_q_clearMapping();
3073}
3074
3092
3114
3136
3174bool QSortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
3175{
3176 Q_D(const QSortFilterProxyModel);
3177 QVariant l = (source_left.model() ? source_left.model()->data(source_left, d->sort_role) : QVariant());
3178 QVariant r = (source_right.model() ? source_right.model()->data(source_right, d->sort_role) : QVariant());
3179 return QAbstractItemModelPrivate::isVariantLessThan(l, r, d->sort_casesensitivity, d->sort_localeaware);
3180}
3181
3196bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
3197{
3198 Q_D(const QSortFilterProxyModel);
3199
3200 if (d->filter_regularexpression.value().pattern().isEmpty())
3201 return true;
3202
3203 int column_count = d->model->columnCount(source_parent);
3204 if (d->filter_column == -1) {
3205 for (int column = 0; column < column_count; ++column) {
3206 QModelIndex source_index = d->model->index(source_row, column, source_parent);
3207 QString key = d->model->data(source_index, d->filter_role).toString();
3208 if (key.contains(d->filter_regularexpression.value()))
3209 return true;
3210 }
3211 return false;
3212 }
3213
3214 if (d->filter_column >= column_count) // the column may not exist
3215 return true;
3216 QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent);
3217 QString key = d->model->data(source_index, d->filter_role).toString();
3218 return key.contains(d->filter_regularexpression.value());
3219}
3220
3230bool QSortFilterProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
3231{
3232 Q_UNUSED(source_column);
3233 Q_UNUSED(source_parent);
3234 return true;
3235}
3236
3244{
3245 Q_D(const QSortFilterProxyModel);
3246 return d->proxy_to_source(proxyIndex);
3247}
3248
3256{
3257 Q_D(const QSortFilterProxyModel);
3258 return d->source_to_proxy(sourceIndex);
3259}
3260
3268
3276
3278
3279#include "moc_qsortfilterproxymodel.cpp"
struct QAbstractItemModelPrivate::Persistent persistent
static bool isVariantLessThan(const QVariant &left, const QVariant &right, Qt::CaseSensitivity cs=Qt::CaseSensitive, bool isLocaleAware=false)
void rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow, QPrivateSignal)
void endResetModel()
Completes a model reset operation.
void modelAboutToBeReset(QPrivateSignal)
virtual Q_INVOKABLE QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits=1, Qt::MatchFlags flags=Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const
Returns a list of indexes for the items in the column of the start index where data stored under the ...
void columnsRemoved(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted after columns have been removed from the model.
LayoutChangeHint
This enum describes the way the model changes layout.
void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted just before rows are inserted into the model.
void columnsAboutToBeInserted(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted just before columns are inserted into the model.
void modelReset(QPrivateSignal)
Q_INVOKABLE Qt::SortOrder order
void layoutAboutToBeChanged(const QList< QPersistentModelIndex > &parents=QList< QPersistentModelIndex >(), QAbstractItemModel::LayoutChangeHint hint=QAbstractItemModel::NoLayoutChangeHint)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles=QList< int >())
This signal is emitted whenever the data in an existing item changes.
void columnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn, QPrivateSignal)
void columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted just before columns are removed from the model.
void layoutChanged(const QList< QPersistentModelIndex > &parents=QList< QPersistentModelIndex >(), QAbstractItemModel::LayoutChangeHint hint=QAbstractItemModel::NoLayoutChangeHint)
void headerDataChanged(Qt::Orientation orientation, int first, int last)
This signal is emitted whenever a header is changed.
void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted just before rows are removed from the model.
void beginResetModel()
Begins a model reset operation.
void rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow, QPrivateSignal)
void rowsInserted(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted after rows have been inserted into the model.
void columnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn, QPrivateSignal)
void columnsInserted(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted after columns have been inserted into the model.
virtual Q_INVOKABLE QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const =0
Returns the index of the item in the model specified by the given row, column and parent index.
void rowsRemoved(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted after rows have been removed from the model.
The QAbstractProxyModel class provides a base class for proxy item models that can do sorting,...
void fetchMore(const QModelIndex &parent) override
\reimp
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
\reimp
virtual Q_INVOKABLE QItemSelection mapSelectionFromSource(const QItemSelection &selection) const
Returns a proxy selection mapped from the specified sourceSelection.
Qt::ItemFlags flags(const QModelIndex &index) const override
\reimp
bool canFetchMore(const QModelIndex &parent) const override
\reimp
QAbstractItemModel * sourceModel
the source model of this proxy model.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
\reimp
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role=Qt::EditRole) override
\reimp
virtual Q_INVOKABLE QItemSelection mapSelectionToSource(const QItemSelection &selection) const
Returns a source selection mapped from the specified proxySelection.
virtual void setSourceModel(QAbstractItemModel *sourceModel)
Sets the given sourceModel to be processed by the proxy model.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
Returns the index of the data in row and column with parent.
\inmodule QtCore
Definition qhash.h:1145
\inmodule QtCore
Definition qhash.h:820
\inmodule QtCore
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
iterator end()
Definition qlist.h:626
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
iterator begin()
Definition qlist.h:625
void reserve(qsizetype size)
Definition qlist.h:753
void clear()
Definition qlist.h:434
\inmodule QtCore Represents a handle to a signal-slot (or signal-functor) connection.
\inmodule QtCore
Definition qmimedata.h:16
\inmodule QtCore
constexpr int row() const noexcept
Returns the row this model index refers to.
QModelIndex parent() const
Returns the parent of the model index, or QModelIndex() if it has no parent.
constexpr const QAbstractItemModel * model() const noexcept
Returns a pointer to the model containing the item that this index refers to.
constexpr int column() const noexcept
Returns the column this model index refers to.
constexpr bool isValid() const noexcept
Returns {true} if this model index is valid; otherwise returns {false}.
QObject * parent
Definition qobject.h:73
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type=Qt::AutoConnection)
Definition qobject_p.h:299
\inmodule QtCore
Definition qobject.h:103
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
\inmodule QtCore \reentrant
PatternOptions patternOptions() const
Returns the pattern options for the regular expression.
void setPatternOptions(PatternOptions options)
Sets the given options as the pattern options of the regular expression.
static QString escape(const QString &str)
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QString wildcardToRegularExpression(const QString &str, WildcardConversionOptions options=DefaultWildcardConversion)
RAII class around Qt::beginPropertyUpdateGroup()/Qt::endPropertyUpdateGroup().
Definition qproperty.h:57
qsizetype size() const
Definition qset.h:50
const_iterator constEnd() const noexcept
Definition qset.h:143
\inmodule QtCore
Definition qsize.h:25
QSortFilterProxyModelGreaterThan(int column, const QModelIndex &parent, const QAbstractItemModel *source, const QSortFilterProxyModel *proxy)
bool operator()(int r1, int r2) const
QSortFilterProxyModelLessThan(int column, const QModelIndex &parent, const QAbstractItemModel *source, const QSortFilterProxyModel *proxy)
bool operator()(int r1, int r2) const
void _q_sourceColumnsInserted(const QModelIndex &source_parent, int start, int end)
QModelIndex source_to_proxy(const QModelIndex &sourceIndex) const
void _q_sourceRowsAboutToBeInserted(const QModelIndex &source_parent, int start, int end)
void _q_sourceColumnsRemoved(const QModelIndex &source_parent, int start, int end)
bool filterAcceptsRowInternal(int source_row, const QModelIndex &source_parent) const
bool can_create_mapping(const QModelIndex &source_parent) const
QHash< QModelIndex, Mapping * >::const_iterator index_to_iterator(const QModelIndex &proxy_index) const
QHash< QModelIndex, Mapping * >::const_iterator create_mapping(const QModelIndex &source_parent) const
Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QSortFilterProxyModelPrivate, Qt::CaseSensitivity, sort_casesensitivity, &QSortFilterProxyModelPrivate::setSortCaseSensitivityForwarder, &QSortFilterProxyModelPrivate::sortCaseSensitivityChangedForwarder, Qt::CaseSensitive) Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QSortFilterProxyModelPrivate
void _q_sourceColumnsAboutToBeInserted(const QModelIndex &source_parent, int start, int end)
QList< std::pair< int, int > > proxy_intervals_for_source_items(const QList< int > &source_to_proxy, const QList< int > &source_items) const
void source_items_about_to_be_removed(const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
void remove_source_items(QList< int > &source_to_proxy, QList< int > &proxy_to_source, const QList< int > &source_items, const QModelIndex &source_parent, Qt::Orientation orient, bool emit_signal=true)
void _q_sourceRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest)
Qt::DisplayRole Qt::DisplayRole false dynamic_sortfilter
bool recursiveChildAcceptsRow(int source_row, const QModelIndex &source_parent) const
bool needsReorder(const QList< int > &source_rows, const QModelIndex &source_parent) const
void setAutoAcceptChildRowsForwarder(bool accept)
QHash< QModelIndex, Mapping * > source_index_mapping
void remove_from_mapping(const QModelIndex &source_parent)
void setRecursiveFilteringEnabledForwarder(bool recursive)
void _q_sourceColumnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest)
void _q_sourceRowsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end)
void sortCaseSensitivityChangedForwarder(Qt::CaseSensitivity cs)
void _q_sourceColumnsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end)
bool recursiveParentAcceptsRow(const QModelIndex &source_parent) const
void source_items_inserted(const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
QList< QPersistentModelIndex > saved_layoutChange_parents
Qt::DisplayRole Qt::DisplayRole filter_recursive
std::array< QMetaObject::Connection, 18 > sourceConnections
QSet< int > handle_filter_changed(QList< int > &source_to_proxy, QList< int > &proxy_to_source, const QModelIndex &source_parent, Qt::Orientation orient)
void autoAcceptChildRowsChangedForwarder(bool accept)
void filterCaseSensitivityChangedForwarder(Qt::CaseSensitivity cs)
QModelIndexPairList store_persistent_indexes() const
void _q_sourceLayoutAboutToBeChanged(const QList< QPersistentModelIndex > &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
void setFilterRegularExpressionForwarder(const QRegularExpression &re)
void _q_sourceRowsInserted(const QModelIndex &source_parent, int start, int end)
void filter_changed(Direction dir, const QModelIndex &source_parent=QModelIndex())
void update_persistent_indexes(const QModelIndexPairList &source_indexes)
void _q_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest)
QModelIndex create_index(int row, int column, QHash< QModelIndex, Mapping * >::const_iterator it) const
void source_items_removed(const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int start, int end)
void setFilterCaseSensitivityForwarder(Qt::CaseSensitivity cs)
QHash< QModelIndex, Mapping * >::const_iterator create_mapping_recursive(const QModelIndex &source_parent) const
void _q_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest)
Qt::DisplayRole Qt::DisplayRole false true filter_regularexpression
static void build_source_to_proxy_mapping(const QList< int > &proxy_to_source, QList< int > &source_to_proxy, int start=0)
void setSortCaseSensitivityForwarder(Qt::CaseSensitivity cs)
void filter_about_to_be_changed(const QModelIndex &source_parent=QModelIndex())
Qt::DisplayRole Qt::DisplayRole false true &QSortFilterProxyModelPrivate::setFilterRegularExpressionForwarder QModelIndex last_top_source
void updateChildrenMapping(const QModelIndex &source_parent, Mapping *parent_mapping, Qt::Orientation orient, int start, int end, int delta_item_count, bool remove)
void sort_source_rows(QList< int > &source_rows, const QModelIndex &source_parent) const
void set_filter_pattern(const QString &pattern)
QModelIndex proxy_to_source(const QModelIndex &proxyIndex) const
void insert_source_items(QList< int > &source_to_proxy, QList< int > &proxy_to_source, const QList< int > &source_items, const QModelIndex &source_parent, Qt::Orientation orient, bool emit_signal=true)
void _q_sourceRowsRemoved(const QModelIndex &source_parent, int start, int end)
void remove_proxy_interval(QList< int > &source_to_proxy, QList< int > &proxy_to_source, int proxy_start, int proxy_end, const QModelIndex &proxy_parent, Qt::Orientation orient, bool emit_signal=true)
void recursiveFilteringEnabledChangedForwarder(bool recursive)
void _q_sourceLayoutChanged(const QList< QPersistentModelIndex > &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
void _q_sourceDataChanged(const QModelIndex &source_top_left, const QModelIndex &source_bottom_right, const QList< int > &roles)
QList< std::pair< int, QList< int > > > proxy_intervals_for_source_items_to_add(const QList< int > &proxy_to_source, const QList< int > &source_items, const QModelIndex &source_parent, Qt::Orientation orient) const
void proxy_item_range(const QList< int > &source_to_proxy, const QList< int > &source_items, int &proxy_low, int &proxy_high) const
The QSortFilterProxyModel class provides support for sorting and filtering data passed between anothe...
void setFilterFixedString(const QString &pattern)
Sets the fixed string used to filter the contents of the source model to the given pattern.
virtual bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
Returns true if the item in the column indicated by the given source_column and source_parent should ...
QBindable< int > bindableFilterKeyColumn()
int sortRole
the item role that is used to query the source model's data when sorting items.
void sort(int column, Qt::SortOrder order=Qt::AscendingOrder) override
\reimp Sorts the model by column in the given order.
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
\reimp
QBindable< bool > bindableRecursiveFilteringEnabled()
Qt::DropActions supportedDropActions() const override
\reimp
QStringList mimeTypes() const override
\reimp
void setSortCaseSensitivity(Qt::CaseSensitivity cs)
QItemSelection mapSelectionToSource(const QItemSelection &proxySelection) const override
\reimp
~QSortFilterProxyModel()
Destroys this sorting filter model.
bool autoAcceptChildRows
if true the proxy model will not filter out children of accepted rows, even if they themselves would ...
QSortFilterProxyModel(QObject *parent=nullptr)
Constructs a sorting filter model with the given parent.
void setSourceModel(QAbstractItemModel *sourceModel) override
\reimp
Qt::CaseSensitivity sortCaseSensitivity
the case sensitivity setting used for comparing strings when sorting
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
Returns the model index in the QSortFilterProxyModel given the sourceIndex from the source model.
QBindable< bool > bindableAutoAcceptChildRows()
QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits=1, Qt::MatchFlags flags=Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const override
\reimp
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
int rowCount(const QModelIndex &parent=QModelIndex()) const override
\reimp
void setFilterRegularExpression(const QString &pattern)
QItemSelection mapSelectionFromSource(const QItemSelection &sourceSelection) const override
\reimp
bool dynamicSortFilter
whether the proxy model is dynamically sorted and filtered whenever the contents of the source model ...
QBindable< bool > bindableDynamicSortFilter()
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
Returns the source model index corresponding to the given proxyIndex from the sorting filter model.
void setFilterCaseSensitivity(Qt::CaseSensitivity cs)
QMimeData * mimeData(const QModelIndexList &indexes) const override
\reimp
QBindable< QRegularExpression > bindableFilterRegularExpression()
bool canFetchMore(const QModelIndex &parent) const override
\reimp
QBindable< Qt::CaseSensitivity > bindableFilterCaseSensitivity()
int filterKeyColumn
the column where the key used to filter the contents of the source model is read from.
bool hasChildren(const QModelIndex &parent=QModelIndex()) const override
\reimp
QModelIndex sibling(int row, int column, const QModelIndex &idx) const override
\reimp
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role=Qt::EditRole) override
\reimp
QBindable< int > bindableFilterRole()
Qt::ItemFlags flags(const QModelIndex &index) const override
\reimp
Qt::CaseSensitivity filterCaseSensitivity
the case sensitivity of the QRegularExpression pattern used to filter the contents of the source mode...
void fetchMore(const QModelIndex &parent) override
\reimp
QSize span(const QModelIndex &index) const override
\reimp
bool removeColumns(int column, int count, const QModelIndex &parent=QModelIndex()) override
\reimp
int columnCount(const QModelIndex &parent=QModelIndex()) const override
\reimp
QModelIndex buddy(const QModelIndex &index) const override
\reimp
int filterRole
the item role that is used to query the source model's data when filtering items.
void setRecursiveFilteringEnabled(bool recursive)
virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
Returns true if the value of the item referred to by the given index source_left is less than the val...
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
\reimp
QBindable< Qt::CaseSensitivity > bindableSortCaseSensitivity()
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
\reimp
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
Returns true if the item in the row indicated by the given source_row and source_parent should be inc...
bool isSortLocaleAware
the local aware setting used for comparing strings when sorting
Qt::SortOrder sortOrder() const
QBindable< int > bindableSortRole()
QRegularExpression filterRegularExpression
the QRegularExpression used to filter the contents of the source model
QBindable< bool > bindableIsSortLocaleAware()
void setDynamicSortFilter(bool enable)
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
\reimp
void setFilterWildcard(const QString &pattern)
Sets the wildcard expression used to filter the contents of the source model to the given pattern.
bool insertColumns(int column, int count, const QModelIndex &parent=QModelIndex()) override
\reimp
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
\reimp
void setAutoAcceptChildRows(bool accept)
bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
\reimp
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
\reimp
int rowCount(const QModelIndex &parent=QModelIndex()) const override
If the database supports returning the size of a query (see QSqlDriver::hasFeature()),...
int columnCount(const QModelIndex &parent=QModelIndex()) const override
\reimp
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QChar * data()
Returns a pointer to the data stored in the QString.
Definition qstring.h:1240
\inmodule QtCore
Definition qvariant.h:65
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
Combined button and popup list for selecting options.
Definition qcompare.h:63
Orientation
Definition qnamespace.h:98
@ Horizontal
Definition qnamespace.h:99
@ Vertical
Definition qnamespace.h:100
SortOrder
Definition qnamespace.h:121
@ AscendingOrder
Definition qnamespace.h:122
CaseSensitivity
@ CaseInsensitive
@ CaseSensitive
DropAction
DBusConnection * connection
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:166
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLboolean GLboolean GLboolean b
const GLfloat * m
GLuint64 key
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLbitfield flags
GLboolean enable
GLuint start
GLsizei GLsizei GLchar * source
GLenum GLenum GLsizei void GLsizei void * column
GLenum array
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLenum GLenum GLsizei void * row
GLfloat GLfloat p
[1]
GLfixed GLfixed GLint GLint order
GLubyte * pattern
#define Q_OBJECT_COMPAT_PROPERTY(...)
#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(...)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QHash< QModelIndex, QSortFilterProxyModelPrivate::Mapping * > IndexMap
static bool operator&(QSortFilterProxyModelPrivate::Direction a, QSortFilterProxyModelPrivate::Direction b)
static QSet< int > qListToSet(const QList< int > &vector)
static QT_BEGIN_NAMESPACE QVariant hint(QPlatformIntegration::StyleHint h)
#define emit
#define Q_UNUSED(x)
static int compare(quint64 a, quint64 b)
QSqlQueryModel * model
[16]
QList< int > vector
[14]
if(qFloatDistance(a, b)<(1<< 7))
[0]
settings remove("monkey")
myObject disconnect()
[26]
QRect r1(100, 200, 11, 16)
[0]
QRect r2(QPoint(100, 200), QSize(11, 16))
QString dir
[11]
QLayoutItem * child
[0]
QNetworkProxy proxy
[0]
\inmodule QtCore
bool contains(QModelIndex parent, int row) const
QRowsRemoval(const QModelIndex &parent_source, int start, int end)
QSortFilterProxyModelDataChanged(const QModelIndex &tl, const QModelIndex &br)