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 const int rc = rowCount();
366 const int cc = columnCount();
367 if (rc > 0 && cc > 0) {
368 // send out big hammer. Everything needs to be updated.
369 Q_EMIT dataChanged(topLeft: index(0, 0), bottomRight: index(rc - 1, cc - 1), roles: changedRoles);
370 }
371 }
372}
373
374bool KDescendantsProxyModel::displayAncestorData() const
375{
376 Q_D(const KDescendantsProxyModel);
377 return d->m_displayAncestorData;
378}
379
380void KDescendantsProxyModel::setAncestorSeparator(const QString &separator)
381{
382 Q_D(KDescendantsProxyModel);
383 bool separatorChanged = (separator != d->m_ancestorSeparator);
384 d->m_ancestorSeparator = separator;
385 if (separatorChanged) {
386 Q_EMIT ancestorSeparatorChanged();
387 if (d->m_displayAncestorData) {
388 const int rc = rowCount();
389 const int cc = columnCount();
390 if (rc > 0 && cc > 0) {
391 // send out big hammer. Everything needs to be updated.
392 Q_EMIT dataChanged(topLeft: index(0, 0), bottomRight: index(rc - 1, cc - 1), roles: changedRoles);
393 }
394 }
395 }
396}
397
398QString KDescendantsProxyModel::ancestorSeparator() const
399{
400 Q_D(const KDescendantsProxyModel);
401 return d->m_ancestorSeparator;
402}
403
404void KDescendantsProxyModel::setSourceModel(QAbstractItemModel *_sourceModel)
405{
406 Q_D(KDescendantsProxyModel);
407
408 beginResetModel();
409
410 if (sourceModel()) {
411 disconnect(sender: sourceModel(), signal: nullptr, receiver: this, member: nullptr);
412 }
413
414 QAbstractProxyModel::setSourceModel(_sourceModel);
415 d_ptr->m_expandedSourceIndexes.clear();
416
417 if (_sourceModel) {
418 connect(sender: _sourceModel, signal: &QAbstractItemModel::rowsAboutToBeInserted, context: this, slot: [d](const QModelIndex &parent, int start, int end) {
419 d->sourceRowsAboutToBeInserted(parent, start, end);
420 });
421
422 connect(sender: _sourceModel, signal: &QAbstractItemModel::rowsInserted, context: this, slot: [d](const QModelIndex &parent, int start, int end) {
423 d->sourceRowsInserted(parent, start, end);
424 });
425
426 connect(sender: _sourceModel, signal: &QAbstractItemModel::rowsAboutToBeRemoved, context: this, slot: [d](const QModelIndex &parent, int start, int end) {
427 d->sourceRowsAboutToBeRemoved(parent, start, end);
428 });
429
430 connect(sender: _sourceModel, signal: &QAbstractItemModel::rowsRemoved, context: this, slot: [d](const QModelIndex &parent, int start, int end) {
431 d->sourceRowsRemoved(parent, start, end);
432 });
433
434 connect(sender: _sourceModel,
435 signal: &QAbstractItemModel::rowsAboutToBeMoved,
436 context: this,
437 slot: [d](const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destStart) {
438 d->sourceRowsAboutToBeMoved(srcParent, srcStart, srcEnd, destParent, destStart);
439 });
440
441 connect(sender: _sourceModel,
442 signal: &QAbstractItemModel::rowsMoved,
443 context: this,
444 slot: [d](const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destStart) {
445 d->sourceRowsMoved(srcParent, srcStart, srcEnd, destParent, destStart);
446 });
447
448 connect(sender: _sourceModel, signal: &QAbstractItemModel::modelAboutToBeReset, context: this, slot: [d]() {
449 d->sourceModelAboutToBeReset();
450 });
451
452 connect(sender: _sourceModel, signal: &QAbstractItemModel::modelReset, context: this, slot: [d]() {
453 d->sourceModelReset();
454 });
455
456 connect(sender: _sourceModel, signal: &QAbstractItemModel::dataChanged, context: this, slot: [d](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
457 d->sourceDataChanged(topLeft, bottomRight);
458 });
459
460 connect(sender: _sourceModel, signal: &QAbstractItemModel::layoutAboutToBeChanged, context: this, slot: [d]() {
461 d->sourceLayoutAboutToBeChanged();
462 });
463
464 connect(sender: _sourceModel, signal: &QAbstractItemModel::layoutChanged, context: this, slot: [d]() {
465 d->sourceLayoutChanged();
466 });
467
468 connect(sender: _sourceModel, signal: &QObject::destroyed, context: this, slot: [d]() {
469 d->sourceModelDestroyed();
470 });
471 }
472
473 resetInternalData();
474 if (_sourceModel && _sourceModel->hasChildren()) {
475 d->synchronousMappingRefresh();
476 }
477
478 endResetModel();
479 Q_EMIT sourceModelChanged();
480}
481
482QModelIndex KDescendantsProxyModel::parent(const QModelIndex &index) const
483{
484 Q_UNUSED(index)
485 return QModelIndex();
486}
487
488bool KDescendantsProxyModel::hasChildren(const QModelIndex &parent) const
489{
490 Q_D(const KDescendantsProxyModel);
491 return !(d->m_mapping.isEmpty() || parent.isValid());
492}
493
494int KDescendantsProxyModel::rowCount(const QModelIndex &parent) const
495{
496 Q_D(const KDescendantsProxyModel);
497 if (d->m_pendingParents.contains(t: parent) || parent.isValid() || !sourceModel()) {
498 return 0;
499 }
500
501 if (d->m_mapping.isEmpty() && sourceModel()->hasChildren()) {
502 const_cast<KDescendantsProxyModelPrivate *>(d)->synchronousMappingRefresh();
503 }
504 return d->m_rowCount;
505}
506
507QModelIndex KDescendantsProxyModel::index(int row, int column, const QModelIndex &parent) const
508{
509 if (parent.isValid()) {
510 return QModelIndex();
511 }
512
513 if (!hasIndex(row, column, parent)) {
514 return QModelIndex();
515 }
516
517 return createIndex(arow: row, acolumn: column);
518}
519
520QModelIndex KDescendantsProxyModel::mapToSource(const QModelIndex &proxyIndex) const
521{
522 Q_D(const KDescendantsProxyModel);
523 if (d->m_mapping.isEmpty() || !proxyIndex.isValid() || !sourceModel()) {
524 return QModelIndex();
525 }
526
527 const Mapping::right_const_iterator result = d->m_mapping.rightLowerBound(key: proxyIndex.row());
528 Q_ASSERT(result != d->m_mapping.rightEnd());
529
530 const int proxyLastRow = result.key();
531 const QModelIndex sourceLastChild = result.value();
532 Q_ASSERT(sourceLastChild.isValid());
533
534 // proxyLastRow is greater than proxyIndex.row().
535 // sourceLastChild is vertically below the result we're looking for
536 // and not necessarily in the correct parent.
537 // We travel up through its parent hierarchy until we are in the
538 // right parent, then return the correct sibling.
539
540 // Source: Proxy: Row
541 // - A - A - 0
542 // - B - B - 1
543 // - C - C - 2
544 // - D - D - 3
545 // - - E - E - 4
546 // - - F - F - 5
547 // - - G - G - 6
548 // - - H - H - 7
549 // - - I - I - 8
550 // - - - J - J - 9
551 // - - - K - K - 10
552 // - - - L - L - 11
553 // - - M - M - 12
554 // - - N - N - 13
555 // - O - O - 14
556
557 // Note that L, N and O are lastChildIndexes, and therefore have a mapping. If we
558 // are trying to map G from the proxy to the source, We at this point have an iterator
559 // pointing to (L -> 11). The proxy row of G is 6. (proxyIndex.row() == 6). We seek the
560 // sourceIndex which is vertically above L by the distance proxyLastRow - proxyIndex.row().
561 // In this case the verticalDistance is 5.
562
563 int verticalDistance = proxyLastRow - proxyIndex.row();
564
565 // We traverse the ancestors of L, until we can index the desired row in the source.
566
567 QModelIndex ancestor = sourceLastChild;
568 while (ancestor.isValid()) {
569 const int ancestorRow = ancestor.row();
570 if (verticalDistance <= ancestorRow) {
571 return ancestor.sibling(arow: ancestorRow - verticalDistance, acolumn: proxyIndex.column());
572 }
573 verticalDistance -= (ancestorRow + 1);
574 ancestor = ancestor.parent();
575 }
576 Q_ASSERT(!"Didn't find target row.");
577 return QModelIndex();
578}
579
580QModelIndex KDescendantsProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
581{
582 Q_D(const KDescendantsProxyModel);
583
584 if (!sourceModel()) {
585 return QModelIndex();
586 }
587
588 if (d->m_mapping.isEmpty()) {
589 return QModelIndex();
590 }
591
592 {
593 // TODO: Consider a parent Mapping to speed this up.
594
595 Mapping::right_const_iterator it = d->m_mapping.rightConstBegin();
596 const Mapping::right_const_iterator end = d->m_mapping.rightConstEnd();
597 const QModelIndex sourceParent = sourceIndex.parent();
598 Mapping::right_const_iterator result = end;
599
600 if (!isSourceIndexVisible(sourceIndex)) {
601 return QModelIndex();
602 }
603
604 for (; it != end; ++it) {
605 QModelIndex index = it.value();
606 bool found_block = false;
607 while (index.isValid()) {
608 const QModelIndex ancestor = index.parent();
609 if (ancestor == sourceParent && index.row() >= sourceIndex.row()) {
610 found_block = true;
611 if (result == end || it.key() < result.key()) {
612 result = it;
613 break; // Leave the while loop. index is still valid.
614 }
615 }
616 index = ancestor;
617 }
618 if (found_block && !index.isValid())
619 // Looked through the ascendants of it.key() without finding sourceParent.
620 // That means we've already got the result we need.
621 {
622 break;
623 }
624 }
625 Q_ASSERT(result != end);
626 const QModelIndex sourceLastChild = result.value();
627 int proxyRow = result.key();
628 QModelIndex index = sourceLastChild;
629 while (index.isValid()) {
630 const QModelIndex ancestor = index.parent();
631 if (ancestor == sourceParent) {
632 return createIndex(arow: proxyRow - (index.row() - sourceIndex.row()), acolumn: sourceIndex.column());
633 }
634 proxyRow -= (index.row() + 1);
635 index = ancestor;
636 }
637 Q_ASSERT(!"Didn't find valid proxy mapping.");
638 return QModelIndex();
639 }
640}
641
642int KDescendantsProxyModel::columnCount(const QModelIndex &parent) const
643{
644 if (parent.isValid() /* || rowCount(parent) == 0 */ || !sourceModel()) {
645 return 0;
646 }
647
648 return sourceModel()->columnCount();
649}
650
651QVariant KDescendantsProxyModel::data(const QModelIndex &index, int role) const
652{
653 Q_D(const KDescendantsProxyModel);
654
655 if (!sourceModel()) {
656 return QVariant();
657 }
658
659 if (!index.isValid()) {
660 return sourceModel()->data(index, role);
661 }
662
663 QModelIndex sourceIndex = mapToSource(proxyIndex: index);
664
665 if ((d->m_displayAncestorData) && (role == Qt::DisplayRole)) {
666 if (!sourceIndex.isValid()) {
667 return QVariant();
668 }
669 QString displayData = sourceIndex.data().toString();
670 sourceIndex = sourceIndex.parent();
671 while (sourceIndex.isValid()) {
672 displayData.prepend(s: d->m_ancestorSeparator);
673 displayData.prepend(s: sourceIndex.data().toString());
674 sourceIndex = sourceIndex.parent();
675 }
676 return displayData;
677 } else if (role == LevelRole) {
678 QModelIndex sourceIndex = mapToSource(proxyIndex: index);
679 int level = 0;
680 while (sourceIndex.isValid()) {
681 sourceIndex = sourceIndex.parent();
682 ++level;
683 }
684 return level;
685 } else if (role == ExpandableRole) {
686 QModelIndex sourceIndex = mapToSource(proxyIndex: index);
687 return sourceModel()->hasChildren(parent: sourceIndex);
688 } else if (role == ExpandedRole) {
689 return isSourceIndexExpanded(sourceIndex: mapToSource(proxyIndex: index));
690 } else if (role == HasSiblingsRole) {
691 QModelIndex sourceIndex = mapToSource(proxyIndex: index);
692 QList<bool> hasSibling;
693 while (sourceIndex.isValid()) {
694 hasSibling.prepend(t: sourceModel()->rowCount(parent: sourceIndex.parent()) > sourceIndex.row() + 1);
695 sourceIndex = sourceIndex.parent();
696 }
697 return QVariant::fromValue(value: hasSibling);
698 } else {
699 return sourceIndex.data(arole: role);
700 }
701}
702
703QVariant KDescendantsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
704{
705 if (!sourceModel() || columnCount() <= section) {
706 return QVariant();
707 }
708
709 // Here is safe to do sourceModel()->headerData, as in this proxy we neither filter out nor reorder columns
710 return sourceModel()->headerData(section, orientation, role);
711}
712
713Qt::ItemFlags KDescendantsProxyModel::flags(const QModelIndex &index) const
714{
715 if (!index.isValid() || !sourceModel()) {
716 return QAbstractProxyModel::flags(index);
717 }
718
719 const QModelIndex srcIndex = mapToSource(proxyIndex: index);
720 Q_ASSERT(srcIndex.isValid());
721 return sourceModel()->flags(index: srcIndex);
722}
723
724void KDescendantsProxyModelPrivate::notifyhasSiblings(const QModelIndex &parent)
725{
726 Q_Q(KDescendantsProxyModel);
727
728 if (!parent.isValid()) {
729 return;
730 }
731
732 QModelIndex localParent = q->mapFromSource(sourceIndex: parent);
733 Q_EMIT q->dataChanged(topLeft: localParent, bottomRight: localParent, roles: {KDescendantsProxyModel::HasSiblingsRole});
734 for (int i = 0; i < q->sourceModel()->rowCount(parent); ++i) {
735 notifyhasSiblings(parent: q->sourceModel()->index(row: i, column: 0, parent));
736 }
737}
738
739void KDescendantsProxyModelPrivate::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
740{
741 Q_Q(KDescendantsProxyModel);
742
743 if (parent.isValid() && (!q->isSourceIndexExpanded(sourceIndex: parent) || !q->isSourceIndexVisible(sourceIndex: parent))) {
744 return;
745 }
746
747 if (!q->sourceModel()->hasChildren(parent)) {
748 Q_ASSERT(q->sourceModel()->rowCount(parent) == 0);
749 // parent was not a parent before.
750 return;
751 }
752
753 int proxyStart = -1;
754
755 const int rowCount = q->sourceModel()->rowCount(parent);
756
757 if (rowCount > start) {
758 const QModelIndex belowStart = q->sourceModel()->index(row: start, column: 0, parent);
759 proxyStart = q->mapFromSource(sourceIndex: belowStart).row();
760 } else if (rowCount == 0) {
761 proxyStart = q->mapFromSource(sourceIndex: parent).row() + 1;
762 } else {
763 Q_ASSERT(rowCount == start);
764 static const int column = 0;
765 QModelIndex idx = q->sourceModel()->index(row: rowCount - 1, column, parent);
766 while (q->isSourceIndexExpanded(sourceIndex: idx) && q->sourceModel()->hasChildren(parent: idx) && q->sourceModel()->rowCount(parent: idx) > 0) {
767 idx = q->sourceModel()->index(row: q->sourceModel()->rowCount(parent: idx) - 1, column, parent: idx);
768 }
769 // The last item in the list is getting a sibling below it.
770 proxyStart = q->mapFromSource(sourceIndex: idx).row() + 1;
771 }
772 const int proxyEnd = proxyStart + (end - start);
773
774 m_insertPair = qMakePair(value1&: proxyStart, value2: proxyEnd);
775 q->beginInsertRows(parent: QModelIndex(), first: proxyStart, last: proxyEnd);
776}
777
778void KDescendantsProxyModelPrivate::sourceRowsInserted(const QModelIndex &parent, int start, int end)
779{
780 Q_Q(KDescendantsProxyModel);
781 if (parent.isValid() && (!q->isSourceIndexExpanded(sourceIndex: parent) || !q->isSourceIndexVisible(sourceIndex: parent))) {
782 const QModelIndex index = q->mapFromSource(sourceIndex: parent);
783 Q_EMIT q->dataChanged(topLeft: index,
784 bottomRight: index,
785 roles: {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole, KDescendantsProxyModel::HasSiblingsRole});
786 if (start > 0) {
787 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
788 }
789 return;
790 }
791 Q_ASSERT(q->sourceModel()->index(start, 0, parent).isValid());
792
793 const int rowCount = q->sourceModel()->rowCount(parent);
794 Q_ASSERT(rowCount > 0);
795
796 const int difference = end - start + 1;
797
798 if (rowCount == difference) {
799 const QModelIndex index = q->mapFromSource(sourceIndex: parent);
800 if (parent.isValid()) {
801 Q_EMIT q->dataChanged(topLeft: index,
802 bottomRight: index,
803 roles: {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole, KDescendantsProxyModel::HasSiblingsRole});
804 }
805 // @p parent was not a parent before.
806 m_pendingParents.append(t: parent);
807 scheduleProcessPendingParents();
808 if (start > 0) {
809 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
810 }
811 return;
812 }
813
814 const int proxyStart = m_insertPair.first;
815
816 Q_ASSERT(proxyStart >= 0);
817
818 updateInternalIndexes(start: proxyStart, offset: difference);
819
820 if (rowCount - 1 == end) {
821 // The previously last row (the mapped one) is no longer the last.
822 // For example,
823
824 // - A - A 0
825 // - - B - B 1
826 // - - C - C 2
827 // - - - D - D 3
828 // - - - E -> - E 4
829 // - - F - F 5
830 // - - G -> - G 6
831 // - H - H 7
832 // - I -> - I 8
833
834 // As last children, E, F and G have mappings.
835 // Consider that 'J' is appended to the children of 'C', below 'E'.
836
837 // - A - A 0
838 // - - B - B 1
839 // - - C - C 2
840 // - - - D - D 3
841 // - - - E -> - E 4
842 // - - - J - ??? 5
843 // - - F - F 6
844 // - - G -> - G 7
845 // - H - H 8
846 // - I -> - I 9
847
848 // The updateInternalIndexes call above will have updated the F and G mappings correctly because proxyStart is 5.
849 // That means that E -> 4 was not affected by the updateInternalIndexes call.
850 // Now the mapping for E -> 4 needs to be updated so that it's a mapping for J -> 5.
851
852 Q_ASSERT(!m_mapping.isEmpty());
853 static const int column = 0;
854 const QModelIndex oldIndex = q->sourceModel()->index(row: rowCount - 1 - difference, column, parent);
855 Q_ASSERT(m_mapping.leftContains(oldIndex));
856
857 const QModelIndex newIndex = q->sourceModel()->index(row: rowCount - 1, column, parent);
858
859 QModelIndex indexAbove = oldIndex;
860
861 if (start > 0) {
862 // If we have something like this:
863 //
864 // - A
865 // - - B
866 // - - C
867 //
868 // and we then insert D as a sibling of A below it, we need to remove the mapping for A,
869 // and the row number used for D must take into account the descendants of A.
870
871 while (q->isSourceIndexExpanded(sourceIndex: indexAbove) && q->sourceModel()->hasChildren(parent: indexAbove)) {
872 Q_ASSERT(q->sourceModel()->rowCount(indexAbove) > 0);
873 indexAbove = q->sourceModel()->index(row: q->sourceModel()->rowCount(parent: indexAbove) - 1, column, parent: indexAbove);
874 }
875 Q_ASSERT(!q->isSourceIndexExpanded(indexAbove) || q->sourceModel()->rowCount(indexAbove) == 0);
876 }
877
878 Q_ASSERT(m_mapping.leftContains(indexAbove));
879
880 const int newProxyRow = m_mapping.leftToRight(t: indexAbove) + difference;
881
882 // oldIndex is E in the source. proxyRow is 4.
883 m_mapping.removeLeft(t: oldIndex);
884
885 // newIndex is J. (proxyRow + difference) is 5.
886 m_mapping.insert(t: newIndex, u: newProxyRow);
887 }
888
889 for (int row = start; row <= end; ++row) {
890 static const int column = 0;
891 const QModelIndex idx = q->sourceModel()->index(row, column, parent);
892 Q_ASSERT(idx.isValid());
893
894 if (q->isSourceIndexExpanded(sourceIndex: idx) && q->sourceModel()->hasChildren(parent: idx) && q->sourceModel()->rowCount(parent: idx) > 0) {
895 m_pendingParents.append(t: idx);
896 }
897 }
898
899 m_rowCount += difference;
900
901 q->endInsertRows();
902 scheduleProcessPendingParents();
903 if (parent.isValid()) {
904 const QModelIndex index = q->mapFromSource(sourceIndex: parent);
905 Q_EMIT q->dataChanged(topLeft: index,
906 bottomRight: index,
907 roles: {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole, KDescendantsProxyModel::HasSiblingsRole});
908 }
909
910 if (start > 0) {
911 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
912 }
913}
914
915void KDescendantsProxyModelPrivate::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
916{
917 Q_Q(KDescendantsProxyModel);
918
919 if (!q->isSourceIndexExpanded(sourceIndex: parent) || !q->isSourceIndexVisible(sourceIndex: parent)) {
920 return;
921 }
922
923 const int proxyStart = q->mapFromSource(sourceIndex: q->sourceModel()->index(row: start, column: 0, parent)).row();
924
925 static const int column = 0;
926 QModelIndex idx = q->sourceModel()->index(row: end, column, parent);
927 while (q->sourceModel()->hasChildren(parent: idx) && q->sourceModel()->rowCount(parent: idx) > 0) {
928 idx = q->sourceModel()->index(row: q->sourceModel()->rowCount(parent: idx) - 1, column, parent: idx);
929 }
930 const int proxyEnd = q->mapFromSource(sourceIndex: idx).row();
931
932 for (int i = start; i <= end; ++i) {
933 QModelIndex idx = q->sourceModel()->index(row: i, column, parent);
934 m_expandedSourceIndexes.remove(value: QPersistentModelIndex(idx));
935 }
936
937 m_removePair = qMakePair(value1: proxyStart, value2: proxyEnd);
938
939 q->beginRemoveRows(parent: QModelIndex(), first: proxyStart, last: proxyEnd);
940}
941
942static QModelIndex getFirstDeepest(QAbstractItemModel *model, const QModelIndex &parent, int *count)
943{
944 static const int column = 0;
945 Q_ASSERT(model->hasChildren(parent));
946 Q_ASSERT(model->rowCount(parent) > 0);
947 for (int row = 0; row < model->rowCount(parent); ++row) {
948 (*count)++;
949 const QModelIndex child = model->index(row, column, parent);
950 Q_ASSERT(child.isValid());
951 if (model->hasChildren(parent: child)) {
952 return getFirstDeepest(model, parent: child, count);
953 }
954 }
955 return model->index(row: model->rowCount(parent) - 1, column, parent);
956}
957
958void KDescendantsProxyModelPrivate::sourceRowsRemoved(const QModelIndex &parent, int start, int end)
959{
960 Q_Q(KDescendantsProxyModel);
961 Q_UNUSED(end)
962
963 if (!q->isSourceIndexExpanded(sourceIndex: parent) || !q->isSourceIndexVisible(sourceIndex: parent)) {
964 if (parent.isValid()) {
965 const QModelIndex index = q->mapFromSource(sourceIndex: parent);
966 Q_EMIT q->dataChanged(topLeft: index, bottomRight: index, roles: {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole});
967 }
968 return;
969 }
970
971 const int rowCount = q->sourceModel()->rowCount(parent);
972
973 const int proxyStart = m_removePair.first;
974 const int proxyEnd = m_removePair.second;
975
976 const int difference = proxyEnd - proxyStart + 1;
977 {
978 Mapping::right_iterator it = m_mapping.rightLowerBound(key: proxyStart);
979 const Mapping::right_iterator endIt = m_mapping.rightUpperBound(key: proxyEnd);
980
981 if (endIt != m_mapping.rightEnd()) {
982 while (it != endIt) {
983 it = m_mapping.eraseRight(it);
984 }
985 } else {
986 while (it != m_mapping.rightUpperBound(key: proxyEnd)) {
987 it = m_mapping.eraseRight(it);
988 }
989 }
990 }
991
992 m_removePair = qMakePair(value1: -1, value2: -1);
993 m_rowCount -= difference;
994 Q_ASSERT(m_rowCount >= 0);
995
996 updateInternalIndexes(start: proxyStart, offset: -1 * difference);
997
998 if (rowCount != start || rowCount == 0) {
999 q->endRemoveRows();
1000 if (parent.isValid()) {
1001 const QModelIndex index = q->mapFromSource(sourceIndex: parent);
1002 Q_EMIT q->dataChanged(topLeft: index, bottomRight: index, roles: {KDescendantsProxyModel::ExpandableRole, KDescendantsProxyModel::ExpandedRole});
1003 }
1004 if (start > 0) {
1005 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
1006 }
1007 return;
1008 }
1009
1010 static const int column = 0;
1011 const QModelIndex newEnd = q->sourceModel()->index(row: rowCount - 1, column, parent);
1012 Q_ASSERT(newEnd.isValid());
1013
1014 if (m_mapping.isEmpty()) {
1015 m_mapping.insert(t: newEnd, u: newEnd.row());
1016 q->endRemoveRows();
1017 if (start > 0) {
1018 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
1019 }
1020 return;
1021 }
1022 if (q->sourceModel()->hasChildren(parent: newEnd)) {
1023 int count = 0;
1024 const QModelIndex firstDeepest = getFirstDeepest(model: q->sourceModel(), parent: newEnd, count: &count);
1025 Q_ASSERT(firstDeepest.isValid());
1026 const int firstDeepestProxy = m_mapping.leftToRight(t: firstDeepest);
1027
1028 m_mapping.insert(t: newEnd, u: firstDeepestProxy - count);
1029 q->endRemoveRows();
1030 if (start > 0) {
1031 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
1032 }
1033 return;
1034 }
1035 Mapping::right_iterator lowerBound = m_mapping.rightLowerBound(key: proxyStart);
1036 if (lowerBound == m_mapping.rightEnd()) {
1037 int proxyRow = std::prev(x: lowerBound).key();
1038
1039 for (int row = newEnd.row(); row >= 0; --row) {
1040 const QModelIndex newEndSibling = q->sourceModel()->index(row, column, parent);
1041 if (!q->sourceModel()->hasChildren(parent: newEndSibling)) {
1042 ++proxyRow;
1043 } else {
1044 break;
1045 }
1046 }
1047 m_mapping.insert(t: newEnd, u: proxyRow);
1048 q->endRemoveRows();
1049 if (start > 0) {
1050 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
1051 }
1052 return;
1053 } else if (lowerBound == m_mapping.rightBegin()) {
1054 int proxyRow = rowCount - 1;
1055 QModelIndex trackedParent = parent;
1056 while (trackedParent.isValid()) {
1057 proxyRow += (trackedParent.row() + 1);
1058 trackedParent = trackedParent.parent();
1059 }
1060 m_mapping.insert(t: newEnd, u: proxyRow);
1061 q->endRemoveRows();
1062 if (start > 0) {
1063 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
1064 }
1065 return;
1066 }
1067 const Mapping::right_iterator boundAbove = std::prev(x: lowerBound);
1068
1069 QList<QModelIndex> targetParents;
1070 targetParents.push_back(t: parent);
1071 {
1072 QModelIndex target = parent;
1073 int count = 0;
1074 while (target.isValid()) {
1075 if (target == boundAbove.value()) {
1076 m_mapping.insert(t: newEnd, u: count + boundAbove.key() + newEnd.row() + 1);
1077 q->endRemoveRows();
1078 if (start > 0) {
1079 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
1080 }
1081 return;
1082 }
1083 count += (target.row() + 1);
1084 target = target.parent();
1085 if (target.isValid()) {
1086 targetParents.push_back(t: target);
1087 }
1088 }
1089 }
1090
1091 QModelIndex boundParent = boundAbove.value().parent();
1092 QModelIndex prevParent = boundParent;
1093 Q_ASSERT(boundParent.isValid());
1094 while (boundParent.isValid()) {
1095 prevParent = boundParent;
1096 boundParent = boundParent.parent();
1097
1098 if (targetParents.contains(t: prevParent)) {
1099 break;
1100 }
1101
1102 if (!m_mapping.leftContains(t: prevParent)) {
1103 break;
1104 }
1105
1106 if (m_mapping.leftToRight(t: prevParent) > boundAbove.key()) {
1107 break;
1108 }
1109 }
1110
1111 QModelIndex trackedParent = parent;
1112
1113 int proxyRow = boundAbove.key();
1114
1115 Q_ASSERT(prevParent.isValid());
1116 proxyRow -= prevParent.row();
1117 while (trackedParent != boundParent) {
1118 proxyRow += (trackedParent.row() + 1);
1119 trackedParent = trackedParent.parent();
1120 }
1121 m_mapping.insert(t: newEnd, u: proxyRow + newEnd.row());
1122 q->endRemoveRows();
1123
1124 if (parent.isValid()) {
1125 const QModelIndex oindex = q->mapFromSource(sourceIndex: parent);
1126 QList<int> rolesChanged({KDescendantsProxyModel::ExpandableRole});
1127
1128 if (!q->sourceModel()->hasChildren(parent)) {
1129 rolesChanged << KDescendantsProxyModel::ExpandedRole;
1130 } else if (q->sourceModel()->rowCount(parent) <= start) {
1131 const QModelIndex index = q->mapFromSource(sourceIndex: q->sourceModel()->index(row: q->sourceModel()->rowCount(parent) - 1, column: 0, parent));
1132 Q_EMIT q->dataChanged(topLeft: index, bottomRight: index, roles: {KDescendantsProxyModel::ExpandedRole});
1133 }
1134
1135 Q_EMIT q->dataChanged(topLeft: oindex, bottomRight: oindex, roles: rolesChanged);
1136 }
1137
1138 if (start > 0) {
1139 notifyhasSiblings(parent: q->sourceModel()->index(row: start - 1, column: 0, parent));
1140 }
1141}
1142
1143void KDescendantsProxyModelPrivate::sourceRowsAboutToBeMoved(const QModelIndex &srcParent,
1144 int srcStart,
1145 int srcEnd,
1146 const QModelIndex &destParent,
1147 int destStart)
1148{
1149 Q_Q(KDescendantsProxyModel);
1150
1151 if (q->isSourceIndexExpanded(sourceIndex: srcParent) && q->isSourceIndexVisible(sourceIndex: srcParent)
1152 && (!q->isSourceIndexExpanded(sourceIndex: destParent) || !q->isSourceIndexVisible(sourceIndex: destParent))) {
1153 const QModelIndex proxySrcParent = q->mapFromSource(sourceIndex: srcParent);
1154 const int proxyParentRow = proxySrcParent.isValid() ? proxySrcParent.row() : 0;
1155 q->beginRemoveRows(parent: QModelIndex(), first: proxyParentRow + srcStart, last: proxyParentRow + srcEnd);
1156
1157 } else if ((!q->isSourceIndexExpanded(sourceIndex: srcParent) || !q->isSourceIndexVisible(sourceIndex: srcParent)) && q->isSourceIndexExpanded(sourceIndex: destParent)
1158 && q->isSourceIndexVisible(sourceIndex: destParent)) {
1159 const QModelIndex proxyDestParent = q->mapFromSource(sourceIndex: srcParent);
1160 const int proxyParentRow = proxyDestParent.isValid() ? proxyDestParent.row() : 0;
1161
1162 q->beginInsertRows(parent: QModelIndex(), first: proxyParentRow + destStart, last: proxyParentRow + destStart + (srcEnd - srcStart));
1163 }
1164
1165 sourceLayoutAboutToBeChanged();
1166}
1167
1168void KDescendantsProxyModelPrivate::sourceRowsMoved(const QModelIndex &srcParent, int srcStart, int /*srcEnd*/, const QModelIndex &destParent, int destStart)
1169{
1170 Q_Q(KDescendantsProxyModel);
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 m_relayouting = true;
1206}
1207
1208void KDescendantsProxyModelPrivate::sourceModelReset()
1209{
1210 Q_Q(KDescendantsProxyModel);
1211 resetInternalData();
1212 if (q->sourceModel()->hasChildren() && q->sourceModel()->rowCount() > 0) {
1213 m_pendingParents.append(t: QModelIndex());
1214 scheduleProcessPendingParents();
1215 }
1216 m_relayouting = false;
1217 q->endResetModel();
1218}
1219
1220void KDescendantsProxyModelPrivate::sourceLayoutAboutToBeChanged()
1221{
1222 Q_Q(KDescendantsProxyModel);
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(proxyIndex: proxyPersistentIndex);
1241 Q_ASSERT(srcPersistentIndex.isValid());
1242 m_layoutChangePersistentIndexes << srcPersistentIndex;
1243 }
1244}
1245
1246void KDescendantsProxyModelPrivate::sourceLayoutChanged()
1247{
1248 Q_Q(KDescendantsProxyModel);
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(from: m_proxyIndexes.at(i), to: q->mapFromSource(sourceIndex: 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{
1275 Q_Q(KDescendantsProxyModel);
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(sourceIndex: topLeft.parent()) || !q->isSourceIndexVisible(sourceIndex: 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(row: i, column: topLeft.column(), parent: topLeft.parent());
1296
1297 Q_ASSERT(sourceTopLeft.isValid());
1298 const QModelIndex proxyTopLeft = q->mapFromSource(sourceIndex: 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(row: i, column: bottomRight.column(), parent: bottomRight.parent());
1302 const QModelIndex proxyBottomRight = q->mapFromSource(sourceIndex: sourceBottomRight);
1303 Q_ASSERT(proxyTopLeft.isValid());
1304 Q_ASSERT(proxyBottomRight.isValid());
1305 Q_EMIT q->dataChanged(topLeft: proxyTopLeft, bottomRight: 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(proxyIndex: index);
1323 }
1324 return sourceModel()->mimeData(indexes: sourceIndexes);
1325}
1326
1327QStringList KDescendantsProxyModel::mimeTypes() const
1328{
1329 if (!sourceModel()) {
1330 return QAbstractProxyModel::mimeTypes();
1331 }
1332 Q_ASSERT(sourceModel());
1333 return sourceModel()->mimeTypes();
1334}
1335
1336Qt::DropActions KDescendantsProxyModel::supportedDropActions() const
1337{
1338 if (!sourceModel()) {
1339 return QAbstractProxyModel::supportedDropActions();
1340 }
1341 return sourceModel()->supportedDropActions();
1342}
1343
1344#include "moc_kdescendantsproxymodel.cpp"
1345

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