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(t: 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{
108 Q_Q(KDescendantsProxyModel);
109 const QList<QPersistentModelIndex>::iterator begin = m_pendingParents.begin();
110 QList<QPersistentModelIndex>::iterator it = 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(pos: it);
121 continue;
122 }
123 if (!q->isSourceIndexVisible(sourceIndex: sourceParent)) {
124 // It's a collapsed node, or its parents are collapsed, ignore.
125 it = m_pendingParents.erase(pos: it);
126 continue;
127 }
128
129 const int rowCount = q->sourceModel()->rowCount(parent: 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(pos: it);
134 continue;
135 }
136 const QPersistentModelIndex sourceIndex = q->sourceModel()->index(row: rowCount - 1, column: 0, parent: sourceParent);
137
138 Q_ASSERT(sourceIndex.isValid());
139
140 const QModelIndex proxyParent = q->mapFromSource(sourceIndex: 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(parent: QModelIndex(), first: proxyStartRow, last: proxyEndRow);
148 }
149
150 updateInternalIndexes(start: proxyStartRow, offset: rowCount);
151 m_mapping.insert(t: sourceIndex, u: proxyEndRow);
152 it = m_pendingParents.erase(pos: 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(row: sourceRow, column, parent: sourceParent);
162 Q_ASSERT(child.isValid());
163
164 if (q->sourceModel()->hasChildren(parent: child) && q->isSourceIndexExpanded(sourceIndex: child) && q->sourceModel()->rowCount(parent: child) > 0) {
165 newPendingParents.append(t: 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.
179 QHash<int, QPersistentModelIndex> updates;
180 {
181 Mapping::right_iterator it = m_mapping.rightLowerBound(key: start);
182 const Mapping::right_iterator end = m_mapping.rightEnd();
183
184 while (it != end) {
185 updates.insert(key: it.key() + offset, value: *it);
186 ++it;
187 }
188 }
189
190 {
191 QHash<int, QPersistentModelIndex>::const_iterator it = updates.constBegin();
192 const QHash<int, QPersistentModelIndex>::const_iterator end = updates.constEnd();
193
194 for (; it != end; ++it) {
195 m_mapping.insert(t: it.value(), u: it.key());
196 }
197 }
198}
199
200KDescendantsProxyModel::KDescendantsProxyModel(QObject *parent)
201 : QAbstractProxyModel(parent)
202 , d_ptr(new KDescendantsProxyModelPrivate(this))
203{
204}
205
206KDescendantsProxyModel::~KDescendantsProxyModel() = default;
207
208QHash<int, QByteArray> KDescendantsProxyModel::roleNames() const
209{
210 QHash<int, QByteArray> roleNames = QAbstractProxyModel::roleNames();
211
212 roleNames[LevelRole] = "kDescendantLevel";
213 roleNames[ExpandableRole] = "kDescendantExpandable";
214 roleNames[ExpandedRole] = "kDescendantExpanded";
215 roleNames[HasSiblingsRole] = "kDescendantHasSiblings";
216 return roleNames;
217}
218
219void KDescendantsProxyModel::setExpandsByDefault(bool expand)
220{
221 if (d_ptr->m_expandsByDefault == expand) {
222 return;
223 }
224
225 beginResetModel();
226 d_ptr->m_expandsByDefault = expand;
227 d_ptr->m_expandedSourceIndexes.clear();
228 d_ptr->m_collapsedSourceIndexes.clear();
229 endResetModel();
230}
231
232bool KDescendantsProxyModel::expandsByDefault() const
233{
234 return d_ptr->m_expandsByDefault;
235}
236
237bool KDescendantsProxyModel::isSourceIndexExpanded(const QModelIndex &sourceIndex) const
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(value: QPersistentModelIndex(sourceIndex));
244 } else {
245 return d_ptr->m_expandedSourceIndexes.contains(value: QPersistentModelIndex(sourceIndex));
246 }
247}
248
249bool KDescendantsProxyModel::isSourceIndexVisible(const QModelIndex &sourceIndex) const
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(sourceIndex: index));
263
264 return false;
265}
266
267void KDescendantsProxyModel::expandSourceIndex(const QModelIndex &sourceIndex)
268{
269 if (!sourceIndex.isValid() || isSourceIndexExpanded(sourceIndex)) {
270 return;
271 }
272
273 if (d_ptr->m_expandsByDefault) {
274 d_ptr->m_collapsedSourceIndexes.remove(value: 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(topLeft: index, bottomRight: index, roles: {ExpandedRole});
285}
286
287void KDescendantsProxyModel::collapseSourceIndex(const QModelIndex &sourceIndex)
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(value: index)) {
302 visited << index;
303 const int nRows = sourceModel()->rowCount(parent: index);
304 rowEnd += nRows;
305 for (int i = 0; i < nRows; ++i) {
306 QModelIndex child = sourceModel()->index(row: i, column: 0, parent: index);
307 if (isSourceIndexExpanded(sourceIndex: child)) {
308 toVisit << child;
309 }
310 }
311 }
312 }
313
314 beginRemoveRows(parent: QModelIndex(), first: rowStart, last: rowEnd);
315
316 if (d_ptr->m_expandsByDefault) {
317 d_ptr->m_collapsedSourceIndexes << QPersistentModelIndex(sourceIndex);
318 } else {
319 d_ptr->m_expandedSourceIndexes.remove(value: QPersistentModelIndex(sourceIndex));
320 }
321
322 {
323 Mapping::right_iterator it = d_ptr->m_mapping.rightLowerBound(key: rowStart);
324 const Mapping::right_iterator endIt = d_ptr->m_mapping.rightUpperBound(key: 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(key: rowEnd)) {
332 it = d_ptr->m_mapping.eraseRight(it);
333 }
334 }
335 }
336
337 d_ptr->m_removePair = qMakePair(value1: rowStart, value2&: rowEnd);
338
339 d_ptr->synchronousMappingRefresh();
340 endRemoveRows();
341 Q_EMIT sourceIndexCollapsed(sourceIndex);
342
343 const QModelIndex ownIndex = mapFromSource(sourceIndex);
344 Q_EMIT dataChanged(topLeft: ownIndex, bottomRight: ownIndex, roles: {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
358void KDescendantsProxyModel::setDisplayAncestorData(bool display)
359{
360 Q_D(KDescendantsProxyModel);
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(topLeft: index(0, 0), bottomRight: index(rowCount() - 1, columnCount() - 1), roles: changedRoles);
367 }
368}
369
370bool KDescendantsProxyModel::displayAncestorData() const
371{
372 Q_D(const KDescendantsProxyModel);
373 return d->m_displayAncestorData;
374}
375
376void KDescendantsProxyModel::setAncestorSeparator(const QString &separator)
377{
378 Q_D(KDescendantsProxyModel);
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(topLeft: index(0, 0), bottomRight: index(rowCount() - 1, columnCount() - 1), roles: changedRoles);
386 }
387 }
388}
389
390QString KDescendantsProxyModel::ancestorSeparator() const
391{
392 Q_D(const KDescendantsProxyModel);
393 return d->m_ancestorSeparator;
394}
395
396void KDescendantsProxyModel::setSourceModel(QAbstractItemModel *_sourceModel)
397{
398 Q_D(KDescendantsProxyModel);
399
400 beginResetModel();
401
402 if (sourceModel()) {
403 disconnect(sender: sourceModel(), signal: nullptr, receiver: this, member: nullptr);
404 }
405
406 QAbstractProxyModel::setSourceModel(_sourceModel);
407 d_ptr->m_expandedSourceIndexes.clear();
408
409 if (_sourceModel) {
410 connect(sender: _sourceModel, signal: &QAbstractItemModel::rowsAboutToBeInserted, context: this, slot: [d](const QModelIndex &parent, int start, int end) {
411 d->sourceRowsAboutToBeInserted(parent, start, end);
412 });
413
414 connect(sender: _sourceModel, signal: &QAbstractItemModel::rowsInserted, context: this, slot: [d](const QModelIndex &parent, int start, int end) {
415 d->sourceRowsInserted(parent, start, end);
416 });
417
418 connect(sender: _sourceModel, signal: &QAbstractItemModel::rowsAboutToBeRemoved, context: this, slot: [d](const QModelIndex &parent, int start, int end) {
419 d->sourceRowsAboutToBeRemoved(parent, start, end);
420 });
421
422 connect(sender: _sourceModel, signal: &QAbstractItemModel::rowsRemoved, context: this, slot: [d](const QModelIndex &parent, int start, int end) {
423 d->sourceRowsRemoved(parent, start, end);
424 });
425
426 connect(sender: _sourceModel,
427 signal: &QAbstractItemModel::rowsAboutToBeMoved,
428 context: this,
429 slot: [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(sender: _sourceModel,
434 signal: &QAbstractItemModel::rowsMoved,
435 context: this,
436 slot: [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(sender: _sourceModel, signal: &QAbstractItemModel::modelAboutToBeReset, context: this, slot: [d]() {
441 d->sourceModelAboutToBeReset();
442 });
443
444 connect(sender: _sourceModel, signal: &QAbstractItemModel::modelReset, context: this, slot: [d]() {
445 d->sourceModelReset();
446 });
447
448 connect(sender: _sourceModel, signal: &QAbstractItemModel::dataChanged, context: this, slot: [d](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
449 d->sourceDataChanged(topLeft, bottomRight);
450 });
451
452 connect(sender: _sourceModel, signal: &QAbstractItemModel::layoutAboutToBeChanged, context: this, slot: [d]() {
453 d->sourceLayoutAboutToBeChanged();
454 });
455
456 connect(sender: _sourceModel, signal: &QAbstractItemModel::layoutChanged, context: this, slot: [d]() {
457 d->sourceLayoutChanged();
458 });
459
460 connect(sender: _sourceModel, signal: &QObject::destroyed, context: this, slot: [d]() {
461 d->sourceModelDestroyed();
462 });
463 }
464
465 resetInternalData();
466 if (_sourceModel && _sourceModel->hasChildren()) {
467 d->synchronousMappingRefresh();
468 }
469
470 endResetModel();
471 Q_EMIT sourceModelChanged();
472}
473
474QModelIndex KDescendantsProxyModel::parent(const QModelIndex &index) const
475{
476 Q_UNUSED(index)
477 return QModelIndex();
478}
479
480bool KDescendantsProxyModel::hasChildren(const QModelIndex &parent) const
481{
482 Q_D(const KDescendantsProxyModel);
483 return !(d->m_mapping.isEmpty() || parent.isValid());
484}
485
486int KDescendantsProxyModel::rowCount(const QModelIndex &parent) const
487{
488 Q_D(const KDescendantsProxyModel);
489 if (d->m_pendingParents.contains(t: 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(arow: row, acolumn: column);
510}
511
512QModelIndex KDescendantsProxyModel::mapToSource(const QModelIndex &proxyIndex) const
513{
514 Q_D(const KDescendantsProxyModel);
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(key: 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(arow: ancestorRow - verticalDistance, acolumn: 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{
574 Q_D(const KDescendantsProxyModel);
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(arow: proxyRow - (index.row() - sourceIndex.row()), acolumn: 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{
645 Q_D(const KDescendantsProxyModel);
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(proxyIndex: 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(s: d->m_ancestorSeparator);
665 displayData.prepend(s: sourceIndex.data().toString());
666 sourceIndex = sourceIndex.parent();
667 }
668 return displayData;
669 } else if (role == LevelRole) {
670 QModelIndex sourceIndex = mapToSource(proxyIndex: 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(proxyIndex: index);
679 return sourceModel()->hasChildren(parent: sourceIndex);
680 } else if (role == ExpandedRole) {
681 return isSourceIndexExpanded(sourceIndex: mapToSource(proxyIndex: index));
682 } else if (role == HasSiblingsRole) {
683 QModelIndex sourceIndex = mapToSource(proxyIndex: index);
684 QList<bool> hasSibling;
685 while (sourceIndex.isValid()) {
686 hasSibling.prepend(t: sourceModel()->rowCount(parent: sourceIndex.parent()) > sourceIndex.row() + 1);
687 sourceIndex = sourceIndex.parent();
688 }
689 return QVariant::fromValue(value: hasSibling);
690 } else {
691 return sourceIndex.data(arole: 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(proxyIndex: index);
712 Q_ASSERT(srcIndex.isValid());
713 return sourceModel()->flags(index: srcIndex);
714}
715
716void KDescendantsProxyModelPrivate::notifyhasSiblings(const QModelIndex &parent)
717{
718 Q_Q(KDescendantsProxyModel);
719
720 if (!parent.isValid()) {
721 return;
722 }
723
724 QModelIndex localParent = q->mapFromSource(sourceIndex: parent);
725 Q_EMIT q->dataChanged(topLeft: localParent, bottomRight: localParent, roles: {KDescendantsProxyModel::HasSiblingsRole});
726 for (int i = 0; i < q->sourceModel()->rowCount(parent); ++i) {
727 notifyhasSiblings(parent: q->sourceModel()->index(row: i, column: 0, parent));
728 }
729}
730
731void KDescendantsProxyModelPrivate::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
732{
733 Q_Q(KDescendantsProxyModel);
734
735 if (parent.isValid() && (!q->isSourceIndexExpanded(sourceIndex: parent) || !q->isSourceIndexVisible(sourceIndex: 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(row: start, column: 0, parent);
751 proxyStart = q->mapFromSource(sourceIndex: belowStart).row();
752 } else if (rowCount == 0) {
753 proxyStart = q->mapFromSource(sourceIndex: parent).row() + 1;
754 } else {
755 Q_ASSERT(rowCount == start);
756 static const int column = 0;
757 QModelIndex idx = q->sourceModel()->index(row: rowCount - 1, column, parent);
758 while (q->isSourceIndexExpanded(sourceIndex: idx) && q->sourceModel()->hasChildren(parent: idx) && q->sourceModel()->rowCount(parent: idx) > 0) {
759 idx = q->sourceModel()->index(row: q->sourceModel()->rowCount(parent: idx) - 1, column, parent: idx);
760 }
761 // The last item in the list is getting a sibling below it.
762 proxyStart = q->mapFromSource(sourceIndex: idx).row() + 1;
763 }
764 const int proxyEnd = proxyStart + (end - start);
765
766 m_insertPair = qMakePair(value1&: proxyStart, value2: proxyEnd);
767 q->beginInsertRows(parent: QModelIndex(), first: proxyStart, last: proxyEnd);
768}
769
770void KDescendantsProxyModelPrivate::sourceRowsInserted(const QModelIndex &parent, int start, int end)
771{
772 Q_Q(KDescendantsProxyModel);
773 if (parent.isValid() && (!q->isSourceIndexExpanded(sourceIndex: parent) || !q->isSourceIndexVisible(sourceIndex: parent))) {
774 const QModelIndex index = q->mapFromSource(sourceIndex: parent);
775 Q_EMIT q->dataChanged(topLeft: index,
776 bottomRight: index,
777 roles: {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole, KDescendantsProxyModel::HasSiblingsRole});
778 if (start > 0) {
779 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 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(sourceIndex: parent);
792 if (parent.isValid()) {
793 Q_EMIT q->dataChanged(topLeft: index,
794 bottomRight: index,
795 roles: {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole, KDescendantsProxyModel::HasSiblingsRole});
796 }
797 // @p parent was not a parent before.
798 m_pendingParents.append(t: parent);
799 scheduleProcessPendingParents();
800 if (start > 0) {
801 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
802 }
803 return;
804 }
805
806 const int proxyStart = m_insertPair.first;
807
808 Q_ASSERT(proxyStart >= 0);
809
810 updateInternalIndexes(start: proxyStart, offset: 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(row: rowCount - 1 - difference, column, parent);
847 Q_ASSERT(m_mapping.leftContains(oldIndex));
848
849 const QModelIndex newIndex = q->sourceModel()->index(row: 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(sourceIndex: indexAbove) && q->sourceModel()->hasChildren(parent: indexAbove)) {
864 Q_ASSERT(q->sourceModel()->rowCount(indexAbove) > 0);
865 indexAbove = q->sourceModel()->index(row: q->sourceModel()->rowCount(parent: indexAbove) - 1, column, parent: 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(t: indexAbove) + difference;
873
874 // oldIndex is E in the source. proxyRow is 4.
875 m_mapping.removeLeft(t: oldIndex);
876
877 // newIndex is J. (proxyRow + difference) is 5.
878 m_mapping.insert(t: newIndex, u: 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(sourceIndex: idx) && q->sourceModel()->hasChildren(parent: idx) && q->sourceModel()->rowCount(parent: idx) > 0) {
887 m_pendingParents.append(t: idx);
888 }
889 }
890
891 m_rowCount += difference;
892
893 q->endInsertRows();
894 scheduleProcessPendingParents();
895 if (parent.isValid()) {
896 const QModelIndex index = q->mapFromSource(sourceIndex: parent);
897 Q_EMIT q->dataChanged(topLeft: index,
898 bottomRight: index,
899 roles: {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole, KDescendantsProxyModel::HasSiblingsRole});
900 }
901
902 if (start > 0) {
903 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
904 }
905}
906
907void KDescendantsProxyModelPrivate::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
908{
909 Q_Q(KDescendantsProxyModel);
910
911 if (!q->isSourceIndexExpanded(sourceIndex: parent) || !q->isSourceIndexVisible(sourceIndex: parent)) {
912 return;
913 }
914
915 const int proxyStart = q->mapFromSource(sourceIndex: q->sourceModel()->index(row: start, column: 0, parent)).row();
916
917 static const int column = 0;
918 QModelIndex idx = q->sourceModel()->index(row: end, column, parent);
919 while (q->sourceModel()->hasChildren(parent: idx) && q->sourceModel()->rowCount(parent: idx) > 0) {
920 idx = q->sourceModel()->index(row: q->sourceModel()->rowCount(parent: idx) - 1, column, parent: idx);
921 }
922 const int proxyEnd = q->mapFromSource(sourceIndex: idx).row();
923
924 for (int i = start; i <= end; ++i) {
925 QModelIndex idx = q->sourceModel()->index(row: i, column, parent);
926 m_expandedSourceIndexes.remove(value: QPersistentModelIndex(idx));
927 }
928
929 m_removePair = qMakePair(value1: proxyStart, value2: proxyEnd);
930
931 q->beginRemoveRows(parent: QModelIndex(), first: proxyStart, last: 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(parent: child)) {
944 return getFirstDeepest(model, parent: child, count);
945 }
946 }
947 return model->index(row: model->rowCount(parent) - 1, column, parent);
948}
949
950void KDescendantsProxyModelPrivate::sourceRowsRemoved(const QModelIndex &parent, int start, int end)
951{
952 Q_Q(KDescendantsProxyModel);
953 Q_UNUSED(end)
954
955 if (!q->isSourceIndexExpanded(sourceIndex: parent) || !q->isSourceIndexVisible(sourceIndex: parent)) {
956 if (parent.isValid()) {
957 const QModelIndex index = q->mapFromSource(sourceIndex: parent);
958 Q_EMIT q->dataChanged(topLeft: index, bottomRight: index, roles: {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(key: proxyStart);
971 const Mapping::right_iterator endIt = m_mapping.rightUpperBound(key: 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(key: proxyEnd)) {
979 it = m_mapping.eraseRight(it);
980 }
981 }
982 }
983
984 m_removePair = qMakePair(value1: -1, value2: -1);
985 m_rowCount -= difference;
986 Q_ASSERT(m_rowCount >= 0);
987
988 updateInternalIndexes(start: proxyStart, offset: -1 * difference);
989
990 if (rowCount != start || rowCount == 0) {
991 q->endRemoveRows();
992 if (parent.isValid()) {
993 const QModelIndex index = q->mapFromSource(sourceIndex: parent);
994 Q_EMIT q->dataChanged(topLeft: index, bottomRight: index, roles: {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole});
995 }
996 if (start > 0) {
997 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
998 }
999 return;
1000 }
1001
1002 static const int column = 0;
1003 const QModelIndex newEnd = q->sourceModel()->index(row: rowCount - 1, column, parent);
1004 Q_ASSERT(newEnd.isValid());
1005
1006 if (m_mapping.isEmpty()) {
1007 m_mapping.insert(t: newEnd, u: newEnd.row());
1008 q->endRemoveRows();
1009 if (start > 0) {
1010 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
1011 }
1012 return;
1013 }
1014 if (q->sourceModel()->hasChildren(parent: newEnd)) {
1015 int count = 0;
1016 const QModelIndex firstDeepest = getFirstDeepest(model: q->sourceModel(), parent: newEnd, count: &count);
1017 Q_ASSERT(firstDeepest.isValid());
1018 const int firstDeepestProxy = m_mapping.leftToRight(t: firstDeepest);
1019
1020 m_mapping.insert(t: newEnd, u: firstDeepestProxy - count);
1021 q->endRemoveRows();
1022 if (start > 0) {
1023 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
1024 }
1025 return;
1026 }
1027 Mapping::right_iterator lowerBound = m_mapping.rightLowerBound(key: proxyStart);
1028 if (lowerBound == m_mapping.rightEnd()) {
1029 int proxyRow = std::prev(x: 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(parent: newEndSibling)) {
1034 ++proxyRow;
1035 } else {
1036 break;
1037 }
1038 }
1039 m_mapping.insert(t: newEnd, u: proxyRow);
1040 q->endRemoveRows();
1041 if (start > 0) {
1042 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 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(t: newEnd, u: proxyRow);
1053 q->endRemoveRows();
1054 if (start > 0) {
1055 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
1056 }
1057 return;
1058 }
1059 const Mapping::right_iterator boundAbove = std::prev(x: lowerBound);
1060
1061 QList<QModelIndex> targetParents;
1062 targetParents.push_back(t: parent);
1063 {
1064 QModelIndex target = parent;
1065 int count = 0;
1066 while (target.isValid()) {
1067 if (target == boundAbove.value()) {
1068 m_mapping.insert(t: newEnd, u: count + boundAbove.key() + newEnd.row() + 1);
1069 q->endRemoveRows();
1070 if (start > 0) {
1071 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
1072 }
1073 return;
1074 }
1075 count += (target.row() + 1);
1076 target = target.parent();
1077 if (target.isValid()) {
1078 targetParents.push_back(t: 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(t: prevParent)) {
1091 break;
1092 }
1093
1094 if (!m_mapping.leftContains(t: prevParent)) {
1095 break;
1096 }
1097
1098 if (m_mapping.leftToRight(t: 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(t: newEnd, u: proxyRow + newEnd.row());
1114 q->endRemoveRows();
1115
1116 if (parent.isValid()) {
1117 const QModelIndex oindex = q->mapFromSource(sourceIndex: 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(sourceIndex: q->sourceModel()->index(row: q->sourceModel()->rowCount(parent) - 1, column: 0, parent));
1124 Q_EMIT q->dataChanged(topLeft: index, bottomRight: index, roles: {KDescendantsProxyModel::ExpandedRole});
1125 }
1126
1127 Q_EMIT q->dataChanged(topLeft: oindex, bottomRight: oindex, roles: rolesChanged);
1128 }
1129
1130 if (start > 0) {
1131 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 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{
1141 Q_Q(KDescendantsProxyModel);
1142
1143 Q_UNUSED(destStart)
1144
1145 if (q->isSourceIndexExpanded(sourceIndex: srcParent) && q->isSourceIndexVisible(sourceIndex: srcParent)
1146 && (!q->isSourceIndexExpanded(sourceIndex: destParent) || !q->isSourceIndexVisible(sourceIndex: destParent))) {
1147 const QModelIndex proxySrcParent = q->mapFromSource(sourceIndex: srcParent);
1148 const int proxyParentRow = proxySrcParent.isValid() ? proxySrcParent.row() : 0;
1149 q->beginRemoveRows(parent: QModelIndex(), first: proxyParentRow + srcStart, last: proxyParentRow + srcEnd);
1150
1151 } else if ((!q->isSourceIndexExpanded(sourceIndex: srcParent) || !q->isSourceIndexVisible(sourceIndex: srcParent)) && q->isSourceIndexExpanded(sourceIndex: destParent)
1152 && q->isSourceIndexVisible(sourceIndex: destParent)) {
1153 const QModelIndex proxyDestParent = q->mapFromSource(sourceIndex: srcParent);
1154 const int proxyParentRow = proxyDestParent.isValid() ? proxyDestParent.row() : 0;
1155
1156 q->beginInsertRows(parent: QModelIndex(), first: proxyParentRow + destStart, last: 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{
1164 Q_Q(KDescendantsProxyModel);
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(sourceIndex: srcParent) && q->isSourceIndexVisible(sourceIndex: srcParent)
1173 && (!q->isSourceIndexExpanded(sourceIndex: destParent) || !q->isSourceIndexVisible(sourceIndex: destParent))) {
1174 q->endRemoveRows();
1175 } else if (!q->isSourceIndexExpanded(sourceIndex: srcParent) && q->isSourceIndexExpanded(sourceIndex: destParent)) {
1176 q->endInsertRows();
1177 }
1178
1179 sourceLayoutChanged();
1180
1181 const QModelIndex index1 = q->mapFromSource(sourceIndex: srcParent);
1182 const QModelIndex index2 = q->mapFromSource(sourceIndex: destParent);
1183 Q_EMIT q->dataChanged(topLeft: index1, bottomRight: index1, roles: {KDescendantsProxyModel::ExpandableRole});
1184 if (index1 != index2) {
1185 Q_EMIT q->dataChanged(topLeft: index2, bottomRight: index2, roles: {KDescendantsProxyModel::ExpandableRole});
1186 if (!q->sourceModel()->hasChildren(parent: destParent)) {
1187 Q_EMIT q->dataChanged(topLeft: index2, bottomRight: index2, roles: {KDescendantsProxyModel::ExpandableRole});
1188 }
1189 }
1190 const QModelIndex lastIndex = q->mapFromSource(sourceIndex: q->sourceModel()->index(row: q->sourceModel()->rowCount(parent: srcParent) - 1, column: 0, parent: srcParent));
1191 Q_EMIT q->dataChanged(topLeft: lastIndex, bottomRight: lastIndex, roles: {KDescendantsProxyModel::ExpandableRole});
1192
1193 if (srcStart > 0) {
1194 notifyhasSiblings(parent: q->sourceModel()->index(row: srcStart - 1, column: 0, parent: srcParent));
1195 }
1196 if (destStart > 0) {
1197 notifyhasSiblings(parent: q->sourceModel()->index(row: destStart - 1, column: 0, parent: destParent));
1198 }
1199}
1200
1201void KDescendantsProxyModelPrivate::sourceModelAboutToBeReset()
1202{
1203 Q_Q(KDescendantsProxyModel);
1204 q->beginResetModel();
1205}
1206
1207void KDescendantsProxyModelPrivate::sourceModelReset()
1208{
1209 Q_Q(KDescendantsProxyModel);
1210 resetInternalData();
1211 if (q->sourceModel()->hasChildren() && q->sourceModel()->rowCount() > 0) {
1212 m_pendingParents.append(t: QModelIndex());
1213 scheduleProcessPendingParents();
1214 }
1215 q->endResetModel();
1216}
1217
1218void KDescendantsProxyModelPrivate::sourceLayoutAboutToBeChanged()
1219{
1220 Q_Q(KDescendantsProxyModel);
1221
1222 if (m_ignoreNextLayoutChanged) {
1223 m_ignoreNextLayoutChanged = false;
1224 return;
1225 }
1226
1227 if (m_mapping.isEmpty()) {
1228 return;
1229 }
1230
1231 Q_EMIT q->layoutAboutToBeChanged();
1232
1233 QPersistentModelIndex srcPersistentIndex;
1234 const auto lst = q->persistentIndexList();
1235 for (const QModelIndex &proxyPersistentIndex : lst) {
1236 m_proxyIndexes << proxyPersistentIndex;
1237 Q_ASSERT(proxyPersistentIndex.isValid());
1238 srcPersistentIndex = q->mapToSource(proxyIndex: proxyPersistentIndex);
1239 Q_ASSERT(srcPersistentIndex.isValid());
1240 m_layoutChangePersistentIndexes << srcPersistentIndex;
1241 }
1242}
1243
1244void KDescendantsProxyModelPrivate::sourceLayoutChanged()
1245{
1246 Q_Q(KDescendantsProxyModel);
1247
1248 if (m_ignoreNextLayoutAboutToBeChanged) {
1249 m_ignoreNextLayoutAboutToBeChanged = false;
1250 return;
1251 }
1252
1253 if (m_mapping.isEmpty()) {
1254 return;
1255 }
1256
1257 m_rowCount = 0;
1258
1259 synchronousMappingRefresh();
1260
1261 for (int i = 0; i < m_proxyIndexes.size(); ++i) {
1262 q->changePersistentIndex(from: m_proxyIndexes.at(i), to: q->mapFromSource(sourceIndex: m_layoutChangePersistentIndexes.at(i)));
1263 }
1264
1265 m_layoutChangePersistentIndexes.clear();
1266 m_proxyIndexes.clear();
1267
1268 Q_EMIT q->layoutChanged();
1269}
1270
1271void KDescendantsProxyModelPrivate::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
1272{
1273 Q_Q(KDescendantsProxyModel);
1274 // It is actually possible in a real world scenario that the source model emits dataChanged
1275 // with invalid indexes when the source model is a QSortFilterProxyModel
1276 // because QSortFilterProxyModel doesn't check for mapped index validity when its
1277 // source model emitted dataChanged on a column QSortFilterProxyModel doesn't accept.
1278 // See https://bugreports.qt.io/browse/QTBUG-86850
1279 if (!topLeft.isValid() || !bottomRight.isValid()) {
1280 return;
1281 }
1282 Q_ASSERT(topLeft.model() == q->sourceModel());
1283 Q_ASSERT(bottomRight.model() == q->sourceModel());
1284
1285 if (!q->isSourceIndexExpanded(sourceIndex: topLeft.parent()) || !q->isSourceIndexVisible(sourceIndex: topLeft.parent())) {
1286 return;
1287 }
1288
1289 const int topRow = topLeft.row();
1290 const int bottomRow = bottomRight.row();
1291
1292 for (int i = topRow; i <= bottomRow; ++i) {
1293 const QModelIndex sourceTopLeft = q->sourceModel()->index(row: i, column: topLeft.column(), parent: topLeft.parent());
1294
1295 Q_ASSERT(sourceTopLeft.isValid());
1296 const QModelIndex proxyTopLeft = q->mapFromSource(sourceIndex: sourceTopLeft);
1297 // TODO. If an index does not have any descendants, then we can emit in blocks of rows.
1298 // As it is we emit once for each row.
1299 const QModelIndex sourceBottomRight = q->sourceModel()->index(row: i, column: bottomRight.column(), parent: bottomRight.parent());
1300 const QModelIndex proxyBottomRight = q->mapFromSource(sourceIndex: sourceBottomRight);
1301 Q_ASSERT(proxyTopLeft.isValid());
1302 Q_ASSERT(proxyBottomRight.isValid());
1303 Q_EMIT q->dataChanged(topLeft: proxyTopLeft, bottomRight: proxyBottomRight);
1304 }
1305}
1306
1307void KDescendantsProxyModelPrivate::sourceModelDestroyed()
1308{
1309 resetInternalData();
1310}
1311
1312QMimeData *KDescendantsProxyModel::mimeData(const QModelIndexList &indexes) const
1313{
1314 if (!sourceModel()) {
1315 return QAbstractProxyModel::mimeData(indexes);
1316 }
1317 Q_ASSERT(sourceModel());
1318 QModelIndexList sourceIndexes;
1319 for (const QModelIndex &index : indexes) {
1320 sourceIndexes << mapToSource(proxyIndex: index);
1321 }
1322 return sourceModel()->mimeData(indexes: sourceIndexes);
1323}
1324
1325QStringList KDescendantsProxyModel::mimeTypes() const
1326{
1327 if (!sourceModel()) {
1328 return QAbstractProxyModel::mimeTypes();
1329 }
1330 Q_ASSERT(sourceModel());
1331 return sourceModel()->mimeTypes();
1332}
1333
1334Qt::DropActions KDescendantsProxyModel::supportedDropActions() const
1335{
1336 if (!sourceModel()) {
1337 return QAbstractProxyModel::supportedDropActions();
1338 }
1339 return sourceModel()->supportedDropActions();
1340}
1341
1342#include "moc_kdescendantsproxymodel.cpp"
1343

source code of kitemmodels/src/core/kdescendantsproxymodel.cpp