KItemModels

kdescendantsproxymodel.cpp
1/*
2 SPDX-FileCopyrightText: 2009 Stephen Kelly <steveire@gmail.com>
3 SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
4 SPDX-FileContributor: Stephen Kelly <stephen@kdab.com>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "kdescendantsproxymodel.h"
10
11#include <QStringList>
12
13#include "kbihash_p.h"
14
15typedef KHash2Map<QPersistentModelIndex, int> Mapping;
16
17class KDescendantsProxyModelPrivate
18{
19 KDescendantsProxyModelPrivate(KDescendantsProxyModel *qq)
20 : q_ptr(qq)
21 , m_rowCount(0)
22 , m_ignoreNextLayoutAboutToBeChanged(false)
23 , m_ignoreNextLayoutChanged(false)
24 , m_relayouting(false)
25 , m_displayAncestorData(false)
26 , m_ancestorSeparator(QStringLiteral(" / "))
27 {
28 }
29
30 Q_DECLARE_PUBLIC(KDescendantsProxyModel)
31 KDescendantsProxyModel *const q_ptr;
32
33 mutable QList<QPersistentModelIndex> m_pendingParents;
34
35 void scheduleProcessPendingParents() const;
36 void processPendingParents();
37
38 void synchronousMappingRefresh();
39
40 void updateInternalIndexes(int start, int offset);
41
42 void resetInternalData();
43
44 void notifyhasSiblings(const QModelIndex &parent);
45 void sourceRowsAboutToBeInserted(const QModelIndex &, int, int);
46 void sourceRowsInserted(const QModelIndex &, int, int);
47 void sourceRowsAboutToBeRemoved(const QModelIndex &, int, int);
48 void sourceRowsRemoved(const QModelIndex &, int, int);
49 void sourceRowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int);
50 void sourceRowsMoved(const QModelIndex &, int, int, const QModelIndex &, int);
51 void sourceModelAboutToBeReset();
52 void sourceModelReset();
53 void sourceLayoutAboutToBeChanged();
54 void sourceLayoutChanged();
55 void sourceDataChanged(const QModelIndex &, const QModelIndex &);
56 void sourceModelDestroyed();
57
58 Mapping m_mapping;
59 int m_rowCount;
60 QPair<int, int> m_removePair;
61 QPair<int, int> m_insertPair;
62
63 bool m_expandsByDefault = true;
64 bool m_ignoreNextLayoutAboutToBeChanged;
65 bool m_ignoreNextLayoutChanged;
66 bool m_relayouting;
67
68 bool m_displayAncestorData;
69 QString m_ancestorSeparator;
70
71 QSet<QPersistentModelIndex> m_expandedSourceIndexes;
72 QSet<QPersistentModelIndex> m_collapsedSourceIndexes;
73
74 QList<QPersistentModelIndex> m_layoutChangePersistentIndexes;
75 QModelIndexList m_proxyIndexes;
76};
77
78void KDescendantsProxyModelPrivate::resetInternalData()
79{
80 m_rowCount = 0;
81 m_mapping.clear();
82 m_layoutChangePersistentIndexes.clear();
83 m_proxyIndexes.clear();
84}
85
86void KDescendantsProxyModelPrivate::synchronousMappingRefresh()
87{
88 m_rowCount = 0;
89 m_mapping.clear();
90 m_pendingParents.clear();
91
92 m_pendingParents.append(QModelIndex());
93
94 m_relayouting = true;
95 while (!m_pendingParents.isEmpty()) {
96 processPendingParents();
97 }
98 m_relayouting = false;
99}
100
101void KDescendantsProxyModelPrivate::scheduleProcessPendingParents() const
102{
103 const_cast<KDescendantsProxyModelPrivate *>(this)->processPendingParents();
104}
105
106void KDescendantsProxyModelPrivate::processPendingParents()
107{
109 const QList<QPersistentModelIndex>::iterator begin = m_pendingParents.begin();
111
112 const QList<QPersistentModelIndex>::iterator end = m_pendingParents.end();
113
114 QList<QPersistentModelIndex> newPendingParents;
115
116 while (it != end && it != m_pendingParents.end()) {
117 const QModelIndex sourceParent = *it;
118 if (!sourceParent.isValid() && m_rowCount > 0) {
119 // It was removed from the source model before it was inserted.
120 it = m_pendingParents.erase(it);
121 continue;
122 }
123 if (!q->isSourceIndexVisible(sourceParent)) {
124 // It's a collapsed node, or its parents are collapsed, ignore.
125 it = m_pendingParents.erase(it);
126 continue;
127 }
128
129 const int rowCount = q->sourceModel()->rowCount(sourceParent);
130
131 // A node can be marked as collapsed or expanded even if it doesn't have children
132 if (rowCount == 0) {
133 it = m_pendingParents.erase(it);
134 continue;
135 }
136 const QPersistentModelIndex sourceIndex = q->sourceModel()->index(rowCount - 1, 0, sourceParent);
137
138 Q_ASSERT(sourceIndex.isValid());
139
140 const QModelIndex proxyParent = q->mapFromSource(sourceParent);
141
142 Q_ASSERT(sourceParent.isValid() == proxyParent.isValid());
143 const int proxyEndRow = proxyParent.row() + rowCount;
144 const int proxyStartRow = proxyEndRow - rowCount + 1;
145
146 if (!m_relayouting) {
147 q->beginInsertRows(QModelIndex(), proxyStartRow, proxyEndRow);
148 }
149
150 updateInternalIndexes(proxyStartRow, rowCount);
151 m_mapping.insert(sourceIndex, proxyEndRow);
152 it = m_pendingParents.erase(it);
153 m_rowCount += rowCount;
154
155 if (!m_relayouting) {
156 q->endInsertRows();
157 }
158
159 for (int sourceRow = 0; sourceRow < rowCount; ++sourceRow) {
160 static const int column = 0;
161 const QModelIndex child = q->sourceModel()->index(sourceRow, column, sourceParent);
162 Q_ASSERT(child.isValid());
163
164 if (q->sourceModel()->hasChildren(child) && q->isSourceIndexExpanded(child) && q->sourceModel()->rowCount(child) > 0) {
165 newPendingParents.append(child);
166 }
167 }
168 }
169 m_pendingParents += newPendingParents;
170 if (!m_pendingParents.isEmpty()) {
171 processPendingParents();
172 }
173 // scheduleProcessPendingParents();
174}
175
176void KDescendantsProxyModelPrivate::updateInternalIndexes(int start, int offset)
177{
178 // TODO: Make KHash2Map support key updates and do this backwards.
180 {
181 Mapping::right_iterator it = m_mapping.rightLowerBound(start);
182 const Mapping::right_iterator end = m_mapping.rightEnd();
183
184 while (it != end) {
185 updates.insert(it.key() + offset, *it);
186 ++it;
187 }
188 }
189
190 {
193
194 for (; it != end; ++it) {
195 m_mapping.insert(it.value(), it.key());
196 }
197 }
198}
199
201 : QAbstractProxyModel(parent)
202 , d_ptr(new KDescendantsProxyModelPrivate(this))
203{
204}
205
207
208QHash<int, QByteArray> KDescendantsProxyModel::roleNames() const
209{
211
212 roleNames[LevelRole] = "kDescendantLevel";
213 roleNames[ExpandableRole] = "kDescendantExpandable";
214 roleNames[ExpandedRole] = "kDescendantExpanded";
215 roleNames[HasSiblingsRole] = "kDescendantHasSiblings";
216 return roleNames;
217}
218
220{
221 if (d_ptr->m_expandsByDefault == expand) {
222 return;
223 }
224
226 d_ptr->m_expandsByDefault = expand;
227 d_ptr->m_expandedSourceIndexes.clear();
228 d_ptr->m_collapsedSourceIndexes.clear();
230}
231
233{
234 return d_ptr->m_expandsByDefault;
235}
236
238{
239 // Root is always expanded
240 if (!sourceIndex.isValid()) {
241 return true;
242 } else if (d_ptr->m_expandsByDefault) {
243 return !d_ptr->m_collapsedSourceIndexes.contains(QPersistentModelIndex(sourceIndex));
244 } else {
245 return d_ptr->m_expandedSourceIndexes.contains(QPersistentModelIndex(sourceIndex));
246 }
247}
248
250{
251 // Root is always visible
252 if (!sourceIndex.isValid()) {
253 return true;
254 }
255
256 QModelIndex index(sourceIndex);
257 do {
258 index = index.parent();
259 if (!index.isValid()) {
260 return true;
261 }
262 } while (isSourceIndexExpanded(index));
263
264 return false;
265}
266
268{
269 if (!sourceIndex.isValid() || isSourceIndexExpanded(sourceIndex)) {
270 return;
271 }
272
273 if (d_ptr->m_expandsByDefault) {
274 d_ptr->m_collapsedSourceIndexes.remove(QPersistentModelIndex(sourceIndex));
275 } else {
276 d_ptr->m_expandedSourceIndexes << QPersistentModelIndex(sourceIndex);
277 }
278
279 d_ptr->m_pendingParents << sourceIndex;
280 d_ptr->scheduleProcessPendingParents();
281 Q_EMIT sourceIndexExpanded(sourceIndex);
282
283 const QModelIndex index = mapFromSource(sourceIndex);
284 Q_EMIT dataChanged(index, index, {ExpandedRole});
285}
286
288{
289 if (!sourceIndex.isValid() || !isSourceIndexExpanded(sourceIndex)) {
290 return;
291 }
292
293 const int row = mapFromSource(sourceIndex).row();
294 const int rowStart = row + 1;
295 int rowEnd = row;
296
297 QList<QModelIndex> toVisit = {sourceIndex};
298 QSet<QModelIndex> visited;
299 while (!toVisit.isEmpty()) {
300 QModelIndex index = toVisit.takeLast();
301 if (!visited.contains(index)) {
302 visited << index;
303 const int nRows = sourceModel()->rowCount(index);
304 rowEnd += nRows;
305 for (int i = 0; i < nRows; ++i) {
306 QModelIndex child = sourceModel()->index(i, 0, index);
307 if (isSourceIndexExpanded(child)) {
308 toVisit << child;
309 }
310 }
311 }
312 }
313
314 beginRemoveRows(QModelIndex(), rowStart, rowEnd);
315
316 if (d_ptr->m_expandsByDefault) {
317 d_ptr->m_collapsedSourceIndexes << QPersistentModelIndex(sourceIndex);
318 } else {
319 d_ptr->m_expandedSourceIndexes.remove(QPersistentModelIndex(sourceIndex));
320 }
321
322 {
323 Mapping::right_iterator it = d_ptr->m_mapping.rightLowerBound(rowStart);
324 const Mapping::right_iterator endIt = d_ptr->m_mapping.rightUpperBound(rowEnd);
325
326 if (endIt != d_ptr->m_mapping.rightEnd()) {
327 while (it != endIt) {
328 it = d_ptr->m_mapping.eraseRight(it);
329 }
330 } else {
331 while (it != d_ptr->m_mapping.rightUpperBound(rowEnd)) {
332 it = d_ptr->m_mapping.eraseRight(it);
333 }
334 }
335 }
336
337 d_ptr->m_removePair = qMakePair(rowStart, rowEnd);
338
339 d_ptr->synchronousMappingRefresh();
341 Q_EMIT sourceIndexCollapsed(sourceIndex);
342
343 const QModelIndex ownIndex = mapFromSource(sourceIndex);
344 Q_EMIT dataChanged(ownIndex, ownIndex, {ExpandedRole});
345}
346
347QModelIndexList KDescendantsProxyModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
348{
349 return QAbstractProxyModel::match(start, role, value, hits, flags);
350}
351
352namespace
353{
354// we only work on DisplayRole for now
355static const QList<int> changedRoles = {Qt::DisplayRole};
356}
357
359{
361 bool displayChanged = (display != d->m_displayAncestorData);
362 d->m_displayAncestorData = display;
363 if (displayChanged) {
364 Q_EMIT displayAncestorDataChanged();
365 // send out big hammer. Everything needs to be updated.
366 Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1), changedRoles);
367 }
368}
369
371{
373 return d->m_displayAncestorData;
374}
375
377{
379 bool separatorChanged = (separator != d->m_ancestorSeparator);
380 d->m_ancestorSeparator = separator;
381 if (separatorChanged) {
382 Q_EMIT ancestorSeparatorChanged();
383 if (d->m_displayAncestorData) {
384 // send out big hammer. Everything needs to be updated.
385 Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1), changedRoles);
386 }
387 }
388}
389
391{
393 return d->m_ancestorSeparator;
394}
395
397{
399
401
402 if (sourceModel()) {
403 disconnect(sourceModel(), nullptr, this, nullptr);
404 }
405
407 d_ptr->m_expandedSourceIndexes.clear();
408
409 if (_sourceModel) {
410 connect(_sourceModel, &QAbstractItemModel::rowsAboutToBeInserted, this, [d](const QModelIndex &parent, int start, int end) {
411 d->sourceRowsAboutToBeInserted(parent, start, end);
412 });
413
414 connect(_sourceModel, &QAbstractItemModel::rowsInserted, this, [d](const QModelIndex &parent, int start, int end) {
415 d->sourceRowsInserted(parent, start, end);
416 });
417
418 connect(_sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, [d](const QModelIndex &parent, int start, int end) {
419 d->sourceRowsAboutToBeRemoved(parent, start, end);
420 });
421
422 connect(_sourceModel, &QAbstractItemModel::rowsRemoved, this, [d](const QModelIndex &parent, int start, int end) {
423 d->sourceRowsRemoved(parent, start, end);
424 });
425
426 connect(_sourceModel,
428 this,
429 [d](const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destStart) {
430 d->sourceRowsAboutToBeMoved(srcParent, srcStart, srcEnd, destParent, destStart);
431 });
432
433 connect(_sourceModel,
435 this,
436 [d](const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destStart) {
437 d->sourceRowsMoved(srcParent, srcStart, srcEnd, destParent, destStart);
438 });
439
440 connect(_sourceModel, &QAbstractItemModel::modelAboutToBeReset, this, [d]() {
441 d->sourceModelAboutToBeReset();
442 });
443
444 connect(_sourceModel, &QAbstractItemModel::modelReset, this, [d]() {
445 d->sourceModelReset();
446 });
447
448 connect(_sourceModel, &QAbstractItemModel::dataChanged, this, [d](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
449 d->sourceDataChanged(topLeft, bottomRight);
450 });
451
452 connect(_sourceModel, &QAbstractItemModel::layoutAboutToBeChanged, this, [d]() {
453 d->sourceLayoutAboutToBeChanged();
454 });
455
456 connect(_sourceModel, &QAbstractItemModel::layoutChanged, this, [d]() {
457 d->sourceLayoutChanged();
458 });
459
460 connect(_sourceModel, &QObject::destroyed, this, [d]() {
461 d->sourceModelDestroyed();
462 });
463 }
464
466 if (_sourceModel && _sourceModel->hasChildren()) {
467 d->synchronousMappingRefresh();
468 }
469
471 Q_EMIT sourceModelChanged();
472}
473
475{
476 Q_UNUSED(index)
477 return QModelIndex();
478}
479
480bool KDescendantsProxyModel::hasChildren(const QModelIndex &parent) const
481{
483 return !(d->m_mapping.isEmpty() || parent.isValid());
484}
485
486int KDescendantsProxyModel::rowCount(const QModelIndex &parent) const
487{
489 if (d->m_pendingParents.contains(parent) || parent.isValid() || !sourceModel()) {
490 return 0;
491 }
492
493 if (d->m_mapping.isEmpty() && sourceModel()->hasChildren()) {
494 const_cast<KDescendantsProxyModelPrivate *>(d)->synchronousMappingRefresh();
495 }
496 return d->m_rowCount;
497}
498
499QModelIndex KDescendantsProxyModel::index(int row, int column, const QModelIndex &parent) const
500{
501 if (parent.isValid()) {
502 return QModelIndex();
503 }
504
505 if (!hasIndex(row, column, parent)) {
506 return QModelIndex();
507 }
508
509 return createIndex(row, column);
510}
511
512QModelIndex KDescendantsProxyModel::mapToSource(const QModelIndex &proxyIndex) const
513{
515 if (d->m_mapping.isEmpty() || !proxyIndex.isValid() || !sourceModel()) {
516 return QModelIndex();
517 }
518
519 const Mapping::right_const_iterator result = d->m_mapping.rightLowerBound(proxyIndex.row());
520 Q_ASSERT(result != d->m_mapping.rightEnd());
521
522 const int proxyLastRow = result.key();
523 const QModelIndex sourceLastChild = result.value();
524 Q_ASSERT(sourceLastChild.isValid());
525
526 // proxyLastRow is greater than proxyIndex.row().
527 // sourceLastChild is vertically below the result we're looking for
528 // and not necessarily in the correct parent.
529 // We travel up through its parent hierarchy until we are in the
530 // right parent, then return the correct sibling.
531
532 // Source: Proxy: Row
533 // - A - A - 0
534 // - B - B - 1
535 // - C - C - 2
536 // - D - D - 3
537 // - - E - E - 4
538 // - - F - F - 5
539 // - - G - G - 6
540 // - - H - H - 7
541 // - - I - I - 8
542 // - - - J - J - 9
543 // - - - K - K - 10
544 // - - - L - L - 11
545 // - - M - M - 12
546 // - - N - N - 13
547 // - O - O - 14
548
549 // Note that L, N and O are lastChildIndexes, and therefore have a mapping. If we
550 // are trying to map G from the proxy to the source, We at this point have an iterator
551 // pointing to (L -> 11). The proxy row of G is 6. (proxyIndex.row() == 6). We seek the
552 // sourceIndex which is vertically above L by the distance proxyLastRow - proxyIndex.row().
553 // In this case the verticalDistance is 5.
554
555 int verticalDistance = proxyLastRow - proxyIndex.row();
556
557 // We traverse the ancestors of L, until we can index the desired row in the source.
558
559 QModelIndex ancestor = sourceLastChild;
560 while (ancestor.isValid()) {
561 const int ancestorRow = ancestor.row();
562 if (verticalDistance <= ancestorRow) {
563 return ancestor.sibling(ancestorRow - verticalDistance, proxyIndex.column());
564 }
565 verticalDistance -= (ancestorRow + 1);
566 ancestor = ancestor.parent();
567 }
568 Q_ASSERT(!"Didn't find target row.");
569 return QModelIndex();
570}
571
572QModelIndex KDescendantsProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
573{
575
576 if (!sourceModel()) {
577 return QModelIndex();
578 }
579
580 if (d->m_mapping.isEmpty()) {
581 return QModelIndex();
582 }
583
584 {
585 // TODO: Consider a parent Mapping to speed this up.
586
587 Mapping::right_const_iterator it = d->m_mapping.rightConstBegin();
588 const Mapping::right_const_iterator end = d->m_mapping.rightConstEnd();
589 const QModelIndex sourceParent = sourceIndex.parent();
590 Mapping::right_const_iterator result = end;
591
592 if (!isSourceIndexVisible(sourceIndex)) {
593 return QModelIndex();
594 }
595
596 for (; it != end; ++it) {
597 QModelIndex index = it.value();
598 bool found_block = false;
599 while (index.isValid()) {
600 const QModelIndex ancestor = index.parent();
601 if (ancestor == sourceParent && index.row() >= sourceIndex.row()) {
602 found_block = true;
603 if (result == end || it.key() < result.key()) {
604 result = it;
605 break; // Leave the while loop. index is still valid.
606 }
607 }
608 index = ancestor;
609 }
610 if (found_block && !index.isValid())
611 // Looked through the ascendants of it.key() without finding sourceParent.
612 // That means we've already got the result we need.
613 {
614 break;
615 }
616 }
617 Q_ASSERT(result != end);
618 const QModelIndex sourceLastChild = result.value();
619 int proxyRow = result.key();
620 QModelIndex index = sourceLastChild;
621 while (index.isValid()) {
622 const QModelIndex ancestor = index.parent();
623 if (ancestor == sourceParent) {
624 return createIndex(proxyRow - (index.row() - sourceIndex.row()), sourceIndex.column());
625 }
626 proxyRow -= (index.row() + 1);
627 index = ancestor;
628 }
629 Q_ASSERT(!"Didn't find valid proxy mapping.");
630 return QModelIndex();
631 }
632}
633
634int KDescendantsProxyModel::columnCount(const QModelIndex &parent) const
635{
636 if (parent.isValid() /* || rowCount(parent) == 0 */ || !sourceModel()) {
637 return 0;
638 }
639
640 return sourceModel()->columnCount();
641}
642
643QVariant KDescendantsProxyModel::data(const QModelIndex &index, int role) const
644{
646
647 if (!sourceModel()) {
648 return QVariant();
649 }
650
651 if (!index.isValid()) {
652 return sourceModel()->data(index, role);
653 }
654
655 QModelIndex sourceIndex = mapToSource(index);
656
657 if ((d->m_displayAncestorData) && (role == Qt::DisplayRole)) {
658 if (!sourceIndex.isValid()) {
659 return QVariant();
660 }
661 QString displayData = sourceIndex.data().toString();
662 sourceIndex = sourceIndex.parent();
663 while (sourceIndex.isValid()) {
664 displayData.prepend(d->m_ancestorSeparator);
665 displayData.prepend(sourceIndex.data().toString());
666 sourceIndex = sourceIndex.parent();
667 }
668 return displayData;
669 } else if (role == LevelRole) {
670 QModelIndex sourceIndex = mapToSource(index);
671 int level = 0;
672 while (sourceIndex.isValid()) {
673 sourceIndex = sourceIndex.parent();
674 ++level;
675 }
676 return level;
677 } else if (role == ExpandableRole) {
678 QModelIndex sourceIndex = mapToSource(index);
679 return sourceModel()->hasChildren(sourceIndex);
680 } else if (role == ExpandedRole) {
681 return isSourceIndexExpanded(mapToSource(index));
682 } else if (role == HasSiblingsRole) {
683 QModelIndex sourceIndex = mapToSource(index);
684 QList<bool> hasSibling;
685 while (sourceIndex.isValid()) {
686 hasSibling.prepend(sourceModel()->rowCount(sourceIndex.parent()) > sourceIndex.row() + 1);
687 sourceIndex = sourceIndex.parent();
688 }
689 return QVariant::fromValue(hasSibling);
690 } else {
691 return sourceIndex.data(role);
692 }
693}
694
695QVariant KDescendantsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
696{
697 if (!sourceModel() || columnCount() <= section) {
698 return QVariant();
699 }
700
701 // Here is safe to do sourceModel()->headerData, as in this proxy we neither filter out nor reorder columns
702 return sourceModel()->headerData(section, orientation, role);
703}
704
705Qt::ItemFlags KDescendantsProxyModel::flags(const QModelIndex &index) const
706{
707 if (!index.isValid() || !sourceModel()) {
708 return QAbstractProxyModel::flags(index);
709 }
710
711 const QModelIndex srcIndex = mapToSource(index);
712 Q_ASSERT(srcIndex.isValid());
713 return sourceModel()->flags(srcIndex);
714}
715
716void KDescendantsProxyModelPrivate::notifyhasSiblings(const QModelIndex &parent)
717{
719
720 if (!parent.isValid()) {
721 return;
722 }
723
724 QModelIndex localParent = q->mapFromSource(parent);
725 Q_EMIT q->dataChanged(localParent, localParent, {KDescendantsProxyModel::HasSiblingsRole});
726 for (int i = 0; i < q->sourceModel()->rowCount(parent); ++i) {
727 notifyhasSiblings(q->sourceModel()->index(i, 0, parent));
728 }
729}
730
731void KDescendantsProxyModelPrivate::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
732{
734
735 if (parent.isValid() && (!q->isSourceIndexExpanded(parent) || !q->isSourceIndexVisible(parent))) {
736 return;
737 }
738
739 if (!q->sourceModel()->hasChildren(parent)) {
740 Q_ASSERT(q->sourceModel()->rowCount(parent) == 0);
741 // parent was not a parent before.
742 return;
743 }
744
745 int proxyStart = -1;
746
747 const int rowCount = q->sourceModel()->rowCount(parent);
748
749 if (rowCount > start) {
750 const QModelIndex belowStart = q->sourceModel()->index(start, 0, parent);
751 proxyStart = q->mapFromSource(belowStart).row();
752 } else if (rowCount == 0) {
753 proxyStart = q->mapFromSource(parent).row() + 1;
754 } else {
755 Q_ASSERT(rowCount == start);
756 static const int column = 0;
757 QModelIndex idx = q->sourceModel()->index(rowCount - 1, column, parent);
758 while (q->isSourceIndexExpanded(idx) && q->sourceModel()->hasChildren(idx) && q->sourceModel()->rowCount(idx) > 0) {
759 idx = q->sourceModel()->index(q->sourceModel()->rowCount(idx) - 1, column, idx);
760 }
761 // The last item in the list is getting a sibling below it.
762 proxyStart = q->mapFromSource(idx).row() + 1;
763 }
764 const int proxyEnd = proxyStart + (end - start);
765
766 m_insertPair = qMakePair(proxyStart, proxyEnd);
767 q->beginInsertRows(QModelIndex(), proxyStart, proxyEnd);
768}
769
770void KDescendantsProxyModelPrivate::sourceRowsInserted(const QModelIndex &parent, int start, int end)
771{
773 if (parent.isValid() && (!q->isSourceIndexExpanded(parent) || !q->isSourceIndexVisible(parent))) {
774 const QModelIndex index = q->mapFromSource(parent);
775 Q_EMIT q->dataChanged(index,
776 index,
777 {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole, KDescendantsProxyModel::HasSiblingsRole});
778 if (start > 0) {
779 notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent));
780 }
781 return;
782 }
783 Q_ASSERT(q->sourceModel()->index(start, 0, parent).isValid());
784
785 const int rowCount = q->sourceModel()->rowCount(parent);
786 Q_ASSERT(rowCount > 0);
787
788 const int difference = end - start + 1;
789
790 if (rowCount == difference) {
791 const QModelIndex index = q->mapFromSource(parent);
792 if (parent.isValid()) {
793 Q_EMIT q->dataChanged(index,
794 index,
795 {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole, KDescendantsProxyModel::HasSiblingsRole});
796 }
797 // @p parent was not a parent before.
798 m_pendingParents.append(parent);
799 scheduleProcessPendingParents();
800 if (start > 0) {
801 notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent));
802 }
803 return;
804 }
805
806 const int proxyStart = m_insertPair.first;
807
808 Q_ASSERT(proxyStart >= 0);
809
810 updateInternalIndexes(proxyStart, difference);
811
812 if (rowCount - 1 == end) {
813 // The previously last row (the mapped one) is no longer the last.
814 // For example,
815
816 // - A - A 0
817 // - - B - B 1
818 // - - C - C 2
819 // - - - D - D 3
820 // - - - E -> - E 4
821 // - - F - F 5
822 // - - G -> - G 6
823 // - H - H 7
824 // - I -> - I 8
825
826 // As last children, E, F and G have mappings.
827 // Consider that 'J' is appended to the children of 'C', below 'E'.
828
829 // - A - A 0
830 // - - B - B 1
831 // - - C - C 2
832 // - - - D - D 3
833 // - - - E -> - E 4
834 // - - - J - ??? 5
835 // - - F - F 6
836 // - - G -> - G 7
837 // - H - H 8
838 // - I -> - I 9
839
840 // The updateInternalIndexes call above will have updated the F and G mappings correctly because proxyStart is 5.
841 // That means that E -> 4 was not affected by the updateInternalIndexes call.
842 // Now the mapping for E -> 4 needs to be updated so that it's a mapping for J -> 5.
843
844 Q_ASSERT(!m_mapping.isEmpty());
845 static const int column = 0;
846 const QModelIndex oldIndex = q->sourceModel()->index(rowCount - 1 - difference, column, parent);
847 Q_ASSERT(m_mapping.leftContains(oldIndex));
848
849 const QModelIndex newIndex = q->sourceModel()->index(rowCount - 1, column, parent);
850
851 QModelIndex indexAbove = oldIndex;
852
853 if (start > 0) {
854 // If we have something like this:
855 //
856 // - A
857 // - - B
858 // - - C
859 //
860 // and we then insert D as a sibling of A below it, we need to remove the mapping for A,
861 // and the row number used for D must take into account the descendants of A.
862
863 while (q->isSourceIndexExpanded(indexAbove) && q->sourceModel()->hasChildren(indexAbove)) {
864 Q_ASSERT(q->sourceModel()->rowCount(indexAbove) > 0);
865 indexAbove = q->sourceModel()->index(q->sourceModel()->rowCount(indexAbove) - 1, column, indexAbove);
866 }
867 Q_ASSERT(!q->isSourceIndexExpanded(indexAbove) || q->sourceModel()->rowCount(indexAbove) == 0);
868 }
869
870 Q_ASSERT(m_mapping.leftContains(indexAbove));
871
872 const int newProxyRow = m_mapping.leftToRight(indexAbove) + difference;
873
874 // oldIndex is E in the source. proxyRow is 4.
875 m_mapping.removeLeft(oldIndex);
876
877 // newIndex is J. (proxyRow + difference) is 5.
878 m_mapping.insert(newIndex, newProxyRow);
879 }
880
881 for (int row = start; row <= end; ++row) {
882 static const int column = 0;
883 const QModelIndex idx = q->sourceModel()->index(row, column, parent);
884 Q_ASSERT(idx.isValid());
885
886 if (q->isSourceIndexExpanded(idx) && q->sourceModel()->hasChildren(idx) && q->sourceModel()->rowCount(idx) > 0) {
887 m_pendingParents.append(idx);
888 }
889 }
890
891 m_rowCount += difference;
892
893 q->endInsertRows();
894 scheduleProcessPendingParents();
895 if (parent.isValid()) {
896 const QModelIndex index = q->mapFromSource(parent);
897 Q_EMIT q->dataChanged(index,
898 index,
899 {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole, KDescendantsProxyModel::HasSiblingsRole});
900 }
901
902 if (start > 0) {
903 notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent));
904 }
905}
906
907void KDescendantsProxyModelPrivate::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
908{
910
911 if (!q->isSourceIndexExpanded(parent) || !q->isSourceIndexVisible(parent)) {
912 return;
913 }
914
915 const int proxyStart = q->mapFromSource(q->sourceModel()->index(start, 0, parent)).row();
916
917 static const int column = 0;
918 QModelIndex idx = q->sourceModel()->index(end, column, parent);
919 while (q->sourceModel()->hasChildren(idx) && q->sourceModel()->rowCount(idx) > 0) {
920 idx = q->sourceModel()->index(q->sourceModel()->rowCount(idx) - 1, column, idx);
921 }
922 const int proxyEnd = q->mapFromSource(idx).row();
923
924 for (int i = start; i <= end; ++i) {
925 QModelIndex idx = q->sourceModel()->index(i, column, parent);
926 m_expandedSourceIndexes.remove(QPersistentModelIndex(idx));
927 }
928
929 m_removePair = qMakePair(proxyStart, proxyEnd);
930
931 q->beginRemoveRows(QModelIndex(), proxyStart, proxyEnd);
932}
933
934static QModelIndex getFirstDeepest(QAbstractItemModel *model, const QModelIndex &parent, int *count)
935{
936 static const int column = 0;
937 Q_ASSERT(model->hasChildren(parent));
938 Q_ASSERT(model->rowCount(parent) > 0);
939 for (int row = 0; row < model->rowCount(parent); ++row) {
940 (*count)++;
941 const QModelIndex child = model->index(row, column, parent);
942 Q_ASSERT(child.isValid());
943 if (model->hasChildren(child)) {
944 return getFirstDeepest(model, child, count);
945 }
946 }
947 return model->index(model->rowCount(parent) - 1, column, parent);
948}
949
950void KDescendantsProxyModelPrivate::sourceRowsRemoved(const QModelIndex &parent, int start, int end)
951{
953 Q_UNUSED(end)
954
955 if (!q->isSourceIndexExpanded(parent) || !q->isSourceIndexVisible(parent)) {
956 if (parent.isValid()) {
957 const QModelIndex index = q->mapFromSource(parent);
958 Q_EMIT q->dataChanged(index, index, {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole});
959 }
960 return;
961 }
962
963 const int rowCount = q->sourceModel()->rowCount(parent);
964
965 const int proxyStart = m_removePair.first;
966 const int proxyEnd = m_removePair.second;
967
968 const int difference = proxyEnd - proxyStart + 1;
969 {
970 Mapping::right_iterator it = m_mapping.rightLowerBound(proxyStart);
971 const Mapping::right_iterator endIt = m_mapping.rightUpperBound(proxyEnd);
972
973 if (endIt != m_mapping.rightEnd()) {
974 while (it != endIt) {
975 it = m_mapping.eraseRight(it);
976 }
977 } else {
978 while (it != m_mapping.rightUpperBound(proxyEnd)) {
979 it = m_mapping.eraseRight(it);
980 }
981 }
982 }
983
984 m_removePair = qMakePair(-1, -1);
985 m_rowCount -= difference;
986 Q_ASSERT(m_rowCount >= 0);
987
988 updateInternalIndexes(proxyStart, -1 * difference);
989
990 if (rowCount != start || rowCount == 0) {
991 q->endRemoveRows();
992 if (parent.isValid()) {
993 const QModelIndex index = q->mapFromSource(parent);
994 Q_EMIT q->dataChanged(index, index, {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole});
995 }
996 if (start > 0) {
997 notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent));
998 }
999 return;
1000 }
1001
1002 static const int column = 0;
1003 const QModelIndex newEnd = q->sourceModel()->index(rowCount - 1, column, parent);
1004 Q_ASSERT(newEnd.isValid());
1005
1006 if (m_mapping.isEmpty()) {
1007 m_mapping.insert(newEnd, newEnd.row());
1008 q->endRemoveRows();
1009 if (start > 0) {
1010 notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent));
1011 }
1012 return;
1013 }
1014 if (q->sourceModel()->hasChildren(newEnd)) {
1015 int count = 0;
1016 const QModelIndex firstDeepest = getFirstDeepest(q->sourceModel(), newEnd, &count);
1017 Q_ASSERT(firstDeepest.isValid());
1018 const int firstDeepestProxy = m_mapping.leftToRight(firstDeepest);
1019
1020 m_mapping.insert(newEnd, firstDeepestProxy - count);
1021 q->endRemoveRows();
1022 if (start > 0) {
1023 notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent));
1024 }
1025 return;
1026 }
1027 Mapping::right_iterator lowerBound = m_mapping.rightLowerBound(proxyStart);
1028 if (lowerBound == m_mapping.rightEnd()) {
1029 int proxyRow = std::prev(lowerBound).key();
1030
1031 for (int row = newEnd.row(); row >= 0; --row) {
1032 const QModelIndex newEndSibling = q->sourceModel()->index(row, column, parent);
1033 if (!q->sourceModel()->hasChildren(newEndSibling)) {
1034 ++proxyRow;
1035 } else {
1036 break;
1037 }
1038 }
1039 m_mapping.insert(newEnd, proxyRow);
1040 q->endRemoveRows();
1041 if (start > 0) {
1042 notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent));
1043 }
1044 return;
1045 } else if (lowerBound == m_mapping.rightBegin()) {
1046 int proxyRow = rowCount - 1;
1047 QModelIndex trackedParent = parent;
1048 while (trackedParent.isValid()) {
1049 proxyRow += (trackedParent.row() + 1);
1050 trackedParent = trackedParent.parent();
1051 }
1052 m_mapping.insert(newEnd, proxyRow);
1053 q->endRemoveRows();
1054 if (start > 0) {
1055 notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent));
1056 }
1057 return;
1058 }
1059 const Mapping::right_iterator boundAbove = std::prev(lowerBound);
1060
1061 QList<QModelIndex> targetParents;
1062 targetParents.push_back(parent);
1063 {
1064 QModelIndex target = parent;
1065 int count = 0;
1066 while (target.isValid()) {
1067 if (target == boundAbove.value()) {
1068 m_mapping.insert(newEnd, count + boundAbove.key() + newEnd.row() + 1);
1069 q->endRemoveRows();
1070 if (start > 0) {
1071 notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent));
1072 }
1073 return;
1074 }
1075 count += (target.row() + 1);
1076 target = target.parent();
1077 if (target.isValid()) {
1078 targetParents.push_back(target);
1079 }
1080 }
1081 }
1082
1083 QModelIndex boundParent = boundAbove.value().parent();
1084 QModelIndex prevParent = boundParent;
1085 Q_ASSERT(boundParent.isValid());
1086 while (boundParent.isValid()) {
1087 prevParent = boundParent;
1088 boundParent = boundParent.parent();
1089
1090 if (targetParents.contains(prevParent)) {
1091 break;
1092 }
1093
1094 if (!m_mapping.leftContains(prevParent)) {
1095 break;
1096 }
1097
1098 if (m_mapping.leftToRight(prevParent) > boundAbove.key()) {
1099 break;
1100 }
1101 }
1102
1103 QModelIndex trackedParent = parent;
1104
1105 int proxyRow = boundAbove.key();
1106
1107 Q_ASSERT(prevParent.isValid());
1108 proxyRow -= prevParent.row();
1109 while (trackedParent != boundParent) {
1110 proxyRow += (trackedParent.row() + 1);
1111 trackedParent = trackedParent.parent();
1112 }
1113 m_mapping.insert(newEnd, proxyRow + newEnd.row());
1114 q->endRemoveRows();
1115
1116 if (parent.isValid()) {
1117 const QModelIndex oindex = q->mapFromSource(parent);
1118 QList<int> rolesChanged({KDescendantsProxyModel::ExpandableRole});
1119
1120 if (!q->sourceModel()->hasChildren(parent)) {
1121 rolesChanged << KDescendantsProxyModel::ExpandedRole;
1122 } else if (q->sourceModel()->rowCount(parent) <= start) {
1123 const QModelIndex index = q->mapFromSource(q->sourceModel()->index(q->sourceModel()->rowCount(parent) - 1, 0, parent));
1124 Q_EMIT q->dataChanged(index, index, {KDescendantsProxyModel::ExpandedRole});
1125 }
1126
1127 Q_EMIT q->dataChanged(oindex, oindex, rolesChanged);
1128 }
1129
1130 if (start > 0) {
1131 notifyhasSiblings(q->sourceModel()->index(start - 1, 0, parent));
1132 }
1133}
1134
1135void KDescendantsProxyModelPrivate::sourceRowsAboutToBeMoved(const QModelIndex &srcParent,
1136 int srcStart,
1137 int srcEnd,
1138 const QModelIndex &destParent,
1139 int destStart)
1140{
1142
1143 Q_UNUSED(destStart)
1144
1145 if (q->isSourceIndexExpanded(srcParent) && q->isSourceIndexVisible(srcParent)
1146 && (!q->isSourceIndexExpanded(destParent) || !q->isSourceIndexVisible(destParent))) {
1147 const QModelIndex proxySrcParent = q->mapFromSource(srcParent);
1148 const int proxyParentRow = proxySrcParent.isValid() ? proxySrcParent.row() : 0;
1149 q->beginRemoveRows(QModelIndex(), proxyParentRow + srcStart, proxyParentRow + srcEnd);
1150
1151 } else if ((!q->isSourceIndexExpanded(srcParent) || !q->isSourceIndexVisible(srcParent)) && q->isSourceIndexExpanded(destParent)
1152 && q->isSourceIndexVisible(destParent)) {
1153 const QModelIndex proxyDestParent = q->mapFromSource(srcParent);
1154 const int proxyParentRow = proxyDestParent.isValid() ? proxyDestParent.row() : 0;
1155
1156 q->beginInsertRows(QModelIndex(), proxyParentRow + destStart, proxyParentRow + destStart + (srcEnd - srcStart));
1157 }
1158
1159 sourceLayoutAboutToBeChanged();
1160}
1161
1162void KDescendantsProxyModelPrivate::sourceRowsMoved(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destStart)
1163{
1165
1166 Q_UNUSED(srcParent)
1167 Q_UNUSED(srcStart)
1168 Q_UNUSED(srcEnd)
1169 Q_UNUSED(destParent)
1170 Q_UNUSED(destStart)
1171
1172 if (q->isSourceIndexExpanded(srcParent) && q->isSourceIndexVisible(srcParent)
1173 && (!q->isSourceIndexExpanded(destParent) || !q->isSourceIndexVisible(destParent))) {
1174 q->endRemoveRows();
1175 } else if (!q->isSourceIndexExpanded(srcParent) && q->isSourceIndexExpanded(destParent)) {
1176 q->endInsertRows();
1177 }
1178
1179 sourceLayoutChanged();
1180
1181 const QModelIndex index1 = q->mapFromSource(srcParent);
1182 const QModelIndex index2 = q->mapFromSource(destParent);
1183 Q_EMIT q->dataChanged(index1, index1, {KDescendantsProxyModel::ExpandableRole});
1184 if (index1 != index2) {
1185 Q_EMIT q->dataChanged(index2, index2, {KDescendantsProxyModel::ExpandableRole});
1186 if (!q->sourceModel()->hasChildren(destParent)) {
1187 Q_EMIT q->dataChanged(index2, index2, {KDescendantsProxyModel::ExpandableRole});
1188 }
1189 }
1190 const QModelIndex lastIndex = q->mapFromSource(q->sourceModel()->index(q->sourceModel()->rowCount(srcParent) - 1, 0, srcParent));
1191 Q_EMIT q->dataChanged(lastIndex, lastIndex, {KDescendantsProxyModel::ExpandableRole});
1192
1193 if (srcStart > 0) {
1194 notifyhasSiblings(q->sourceModel()->index(srcStart - 1, 0, srcParent));
1195 }
1196 if (destStart > 0) {
1197 notifyhasSiblings(q->sourceModel()->index(destStart - 1, 0, destParent));
1198 }
1199}
1200
1201void KDescendantsProxyModelPrivate::sourceModelAboutToBeReset()
1202{
1204 q->beginResetModel();
1205 m_relayouting = true;
1206}
1207
1208void KDescendantsProxyModelPrivate::sourceModelReset()
1209{
1211 resetInternalData();
1212 if (q->sourceModel()->hasChildren() && q->sourceModel()->rowCount() > 0) {
1213 m_pendingParents.append(QModelIndex());
1214 scheduleProcessPendingParents();
1215 }
1216 m_relayouting = false;
1217 q->endResetModel();
1218}
1219
1220void KDescendantsProxyModelPrivate::sourceLayoutAboutToBeChanged()
1221{
1223
1224 if (m_ignoreNextLayoutChanged) {
1225 m_ignoreNextLayoutChanged = false;
1226 return;
1227 }
1228
1229 if (m_mapping.isEmpty()) {
1230 return;
1231 }
1232
1233 Q_EMIT q->layoutAboutToBeChanged();
1234
1235 QPersistentModelIndex srcPersistentIndex;
1236 const auto lst = q->persistentIndexList();
1237 for (const QModelIndex &proxyPersistentIndex : lst) {
1238 m_proxyIndexes << proxyPersistentIndex;
1239 Q_ASSERT(proxyPersistentIndex.isValid());
1240 srcPersistentIndex = q->mapToSource(proxyPersistentIndex);
1241 Q_ASSERT(srcPersistentIndex.isValid());
1242 m_layoutChangePersistentIndexes << srcPersistentIndex;
1243 }
1244}
1245
1246void KDescendantsProxyModelPrivate::sourceLayoutChanged()
1247{
1249
1250 if (m_ignoreNextLayoutAboutToBeChanged) {
1251 m_ignoreNextLayoutAboutToBeChanged = false;
1252 return;
1253 }
1254
1255 if (m_mapping.isEmpty()) {
1256 return;
1257 }
1258
1259 m_rowCount = 0;
1260
1261 synchronousMappingRefresh();
1262
1263 for (int i = 0; i < m_proxyIndexes.size(); ++i) {
1264 q->changePersistentIndex(m_proxyIndexes.at(i), q->mapFromSource(m_layoutChangePersistentIndexes.at(i)));
1265 }
1266
1267 m_layoutChangePersistentIndexes.clear();
1268 m_proxyIndexes.clear();
1269
1270 Q_EMIT q->layoutChanged();
1271}
1272
1273void KDescendantsProxyModelPrivate::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
1274{
1276 // It is actually possible in a real world scenario that the source model emits dataChanged
1277 // with invalid indexes when the source model is a QSortFilterProxyModel
1278 // because QSortFilterProxyModel doesn't check for mapped index validity when its
1279 // source model emitted dataChanged on a column QSortFilterProxyModel doesn't accept.
1280 // See https://bugreports.qt.io/browse/QTBUG-86850
1281 if (!topLeft.isValid() || !bottomRight.isValid()) {
1282 return;
1283 }
1284 Q_ASSERT(topLeft.model() == q->sourceModel());
1285 Q_ASSERT(bottomRight.model() == q->sourceModel());
1286
1287 if (!q->isSourceIndexExpanded(topLeft.parent()) || !q->isSourceIndexVisible(topLeft.parent())) {
1288 return;
1289 }
1290
1291 const int topRow = topLeft.row();
1292 const int bottomRow = bottomRight.row();
1293
1294 for (int i = topRow; i <= bottomRow; ++i) {
1295 const QModelIndex sourceTopLeft = q->sourceModel()->index(i, topLeft.column(), topLeft.parent());
1296
1297 Q_ASSERT(sourceTopLeft.isValid());
1298 const QModelIndex proxyTopLeft = q->mapFromSource(sourceTopLeft);
1299 // TODO. If an index does not have any descendants, then we can emit in blocks of rows.
1300 // As it is we emit once for each row.
1301 const QModelIndex sourceBottomRight = q->sourceModel()->index(i, bottomRight.column(), bottomRight.parent());
1302 const QModelIndex proxyBottomRight = q->mapFromSource(sourceBottomRight);
1303 Q_ASSERT(proxyTopLeft.isValid());
1304 Q_ASSERT(proxyBottomRight.isValid());
1305 Q_EMIT q->dataChanged(proxyTopLeft, proxyBottomRight);
1306 }
1307}
1308
1309void KDescendantsProxyModelPrivate::sourceModelDestroyed()
1310{
1311 resetInternalData();
1312}
1313
1314QMimeData *KDescendantsProxyModel::mimeData(const QModelIndexList &indexes) const
1315{
1316 if (!sourceModel()) {
1317 return QAbstractProxyModel::mimeData(indexes);
1318 }
1319 Q_ASSERT(sourceModel());
1320 QModelIndexList sourceIndexes;
1321 for (const QModelIndex &index : indexes) {
1322 sourceIndexes << mapToSource(index);
1323 }
1324 return sourceModel()->mimeData(sourceIndexes);
1325}
1326
1327QStringList KDescendantsProxyModel::mimeTypes() const
1328{
1329 if (!sourceModel()) {
1331 }
1332 Q_ASSERT(sourceModel());
1333 return sourceModel()->mimeTypes();
1334}
1335
1336Qt::DropActions KDescendantsProxyModel::supportedDropActions() const
1337{
1338 if (!sourceModel()) {
1340 }
1341 return sourceModel()->supportedDropActions();
1342}
1343
1344#include "moc_kdescendantsproxymodel.cpp"
Proxy Model for restructuring a Tree into a list.
KDescendantsProxyModel(QObject *parent=nullptr)
Creates a new descendant entities proxy model.
~KDescendantsProxyModel() override
Destroys the descendant entities proxy model.
void collapseSourceIndex(const QModelIndex &sourceIndex)
Maps a source index as collapsed in the proxy, all its children will be hidden.
void setAncestorSeparator(const QString &separator)
Sets the ancestor separator used between data of ancestors.
bool isSourceIndexExpanded(const QModelIndex &sourceIndex) const
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits=1, Qt::MatchFlags flags=Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const override
Reimplemented to match all descendants.
void expandSourceIndex(const QModelIndex &sourceIndex)
Maps a source index as expanded in the proxy, all its children will become visible.
bool isSourceIndexVisible(const QModelIndex &sourceIndex) const
void setDisplayAncestorData(bool display)
Set whether to show ancestor data in the model.
bool expandsByDefault
If true, all the nodes in the whole tree will be expanded upon loading and all items of the source mo...
void setExpandsByDefault(bool expand)
If true, all the nodes in the whole tree will be expanded upon loading (default)
void setSourceModel(QAbstractItemModel *model) override
Sets the source model of the proxy.
Q_SCRIPTABLE Q_NOREPLY void start()
QStringView level(QStringView ifopt)
const QList< QKeySequence > & begin()
const QList< QKeySequence > & end()
void beginRemoveRows(const QModelIndex &parent, int first, int last)
QModelIndex createIndex(int row, int column, const void *ptr) const const
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
virtual bool hasChildren(const QModelIndex &parent) const const
bool hasIndex(int row, int column, const QModelIndex &parent) const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
void layoutAboutToBeChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
void layoutChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const const
void modelAboutToBeReset()
virtual void resetInternalData()
virtual int rowCount(const QModelIndex &parent) const const=0
void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
void rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow)
void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
void rowsInserted(const QModelIndex &parent, int first, int last)
void rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow)
void rowsRemoved(const QModelIndex &parent, int first, int last)
virtual Qt::ItemFlags flags(const QModelIndex &index) const const override
virtual QMimeData * mimeData(const QModelIndexList &indexes) const const override
virtual QStringList mimeTypes() const const override
virtual QHash< int, QByteArray > roleNames() const const override
virtual void setSourceModel(QAbstractItemModel *sourceModel)
virtual Qt::DropActions supportedDropActions() const const override
const_iterator constBegin() const const
const_iterator constEnd() const const
iterator insert(const Key &key, const T &value)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
iterator begin()
void clear()
bool contains(const AT &value) const const
iterator end()
iterator erase(const_iterator begin, const_iterator end)
bool isEmpty() const const
void prepend(parameter_type value)
void push_back(parameter_type value)
value_type takeLast()
int column() const const
QVariant data(int role) const const
bool isValid() const const
const QAbstractItemModel * model() const const
QModelIndex parent() const const
int row() const const
QModelIndex sibling(int row, int column) const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void destroyed(QObject *obj)
bool disconnect(const QMetaObject::Connection &connection)
QObject * parent() const const
bool isValid() const const
bool contains(const QSet< T > &other) const const
bool remove(const T &value)
QString & prepend(QChar ch)
typedef DropActions
DisplayRole
typedef ItemFlags
typedef MatchFlags
Orientation
QVariant fromValue(T &&value)
QString toString() const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:06:27 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.