Marble

kdescendantsproxymodel.cpp
1/*
2 SPDX-FileCopyrightText: 2009 Stephen Kelly <steveire@gmail.com>
3 SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB a KDAB Group company <info@kdab.net>
4 SPDX-FileContributor: Stephen Kelly <stephen@kdab.com>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "kdescendantsproxymodel.h"
10
11#include <QStringList>
12#include <QTimer>
13
14#include "kbihash_p.h"
15
16namespace Marble
17{
18
19using Mapping = KHash2Map<QPersistentModelIndex, int>;
20
21class KDescendantsProxyModelPrivate
22{
23 KDescendantsProxyModelPrivate(KDescendantsProxyModel *qq)
24 : q_ptr(qq)
25 , m_rowCount(0)
26 , m_ignoreNextLayoutAboutToBeChanged(false)
27 , m_ignoreNextLayoutChanged(false)
28 , m_relayouting(false)
29 , m_displayAncestorData(false)
30 , m_ancestorSeparator(QStringLiteral(" / "))
31 {
32 }
33
34 Q_DECLARE_PUBLIC(KDescendantsProxyModel)
35 KDescendantsProxyModel *const q_ptr;
36
37 mutable QList<QPersistentModelIndex> m_pendingParents;
38
39 void scheduleProcessPendingParents() const;
40 void processPendingParents();
41
42 void synchronousMappingRefresh();
43
44 void updateInternalIndexes(int start, int offset);
45
46 void resetInternalData();
47
48 void sourceRowsAboutToBeInserted(const QModelIndex &, int, int);
49 void sourceRowsInserted(const QModelIndex &, int, int);
50 void sourceRowsAboutToBeRemoved(const QModelIndex &, int, int);
51 void sourceRowsRemoved(const QModelIndex &, int, int);
52 void sourceRowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int);
53 void sourceRowsMoved(const QModelIndex &, int, int, const QModelIndex &, int);
54 void sourceModelAboutToBeReset();
55 void sourceModelReset();
56 void sourceLayoutAboutToBeChanged();
57 void sourceLayoutChanged();
58 void sourceDataChanged(const QModelIndex &, const QModelIndex &);
59 void sourceModelDestroyed();
60
61 Mapping m_mapping;
62 int m_rowCount;
63 QPair<int, int> m_removePair;
64 QPair<int, int> m_insertPair;
65
66 bool m_ignoreNextLayoutAboutToBeChanged;
67 bool m_ignoreNextLayoutChanged;
68 bool m_relayouting;
69
70 bool m_displayAncestorData;
71 QString m_ancestorSeparator;
72
73 QList<QPersistentModelIndex> m_layoutChangePersistentIndexes;
74 QModelIndexList m_proxyIndexes;
75};
76
77void KDescendantsProxyModelPrivate::resetInternalData()
78{
79 m_rowCount = 0;
80 m_mapping.clear();
81 m_layoutChangePersistentIndexes.clear();
82 m_proxyIndexes.clear();
83}
84
85void KDescendantsProxyModelPrivate::synchronousMappingRefresh()
86{
87 m_rowCount = 0;
88 m_mapping.clear();
89 m_pendingParents.clear();
90
91 m_pendingParents.append(QModelIndex());
92
93 m_relayouting = true;
94 while (!m_pendingParents.isEmpty()) {
95 processPendingParents();
96 }
97 m_relayouting = false;
98}
99
100void KDescendantsProxyModelPrivate::scheduleProcessPendingParents() const
101{
102 const_cast<KDescendantsProxyModelPrivate *>(this)->processPendingParents();
103}
104
105void KDescendantsProxyModelPrivate::processPendingParents()
106{
108 const QList<QPersistentModelIndex>::iterator begin = m_pendingParents.begin();
110
111 const QList<QPersistentModelIndex>::iterator end = m_pendingParents.end();
112
113 QList<QPersistentModelIndex> newPendingParents;
114
115 while (it != end && it != m_pendingParents.end()) {
116 const QModelIndex sourceParent = *it;
117 if (!sourceParent.isValid() && m_rowCount > 0) {
118 // It was removed from the source model before it was inserted.
119 it = m_pendingParents.erase(it);
120 continue;
121 }
122 const int rowCount = q->sourceModel()->rowCount(sourceParent);
123
124 Q_ASSERT(rowCount > 0);
125 const QPersistentModelIndex sourceIndex = q->sourceModel()->index(rowCount - 1, 0, sourceParent);
126
127 Q_ASSERT(sourceIndex.isValid());
128
129 const QModelIndex proxyParent = q->mapFromSource(sourceParent);
130
131 Q_ASSERT(sourceParent.isValid() == proxyParent.isValid());
132 const int proxyEndRow = proxyParent.row() + rowCount;
133 const int proxyStartRow = proxyEndRow - rowCount + 1;
134
135 if (!m_relayouting) {
136 q->beginInsertRows(QModelIndex(), proxyStartRow, proxyEndRow);
137 }
138
139 updateInternalIndexes(proxyStartRow, rowCount);
140 m_mapping.insert(sourceIndex, proxyEndRow);
141 it = m_pendingParents.erase(it);
142 m_rowCount += rowCount;
143
144 if (!m_relayouting) {
145 q->endInsertRows();
146 }
147
148 for (int sourceRow = 0; sourceRow < rowCount; ++sourceRow) {
149 static const int column = 0;
150 const QModelIndex child = q->sourceModel()->index(sourceRow, column, sourceParent);
151 Q_ASSERT(child.isValid());
152
153 if (q->sourceModel()->hasChildren(child)) {
154 Q_ASSERT(q->sourceModel()->rowCount(child) > 0);
155 newPendingParents.append(child);
156 }
157 }
158 }
159 m_pendingParents += newPendingParents;
160 if (!m_pendingParents.isEmpty()) {
161 processPendingParents();
162 }
163 // scheduleProcessPendingParents();
164}
165
166void KDescendantsProxyModelPrivate::updateInternalIndexes(int start, int offset)
167{
168 // TODO: Make KHash2Map support key updates and do this backwards.
170 {
171 Mapping::right_iterator it = m_mapping.rightLowerBound(start);
172 const Mapping::right_iterator end = m_mapping.rightEnd();
173
174 while (it != end) {
175 updates.insert(it.key() + offset, *it);
176 ++it;
177 }
178 }
179
180 {
183
184 for (; it != end; ++it) {
185 m_mapping.insert(it.value(), it.key());
186 }
187 }
188}
189
191 : QAbstractProxyModel(parent)
192 , d_ptr(new KDescendantsProxyModelPrivate(this))
193{
194}
195
200
201#if 0
202void KDescendantsProxyModel::setRootIndex(const QModelIndex &index)
203{
204 Q_UNUSED(index)
205}
206#endif
207
208QModelIndexList KDescendantsProxyModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
209{
210 return QAbstractProxyModel::match(start, role, value, hits, flags);
211}
212
214{
216 d->m_displayAncestorData = display;
217}
218
220{
222 return d->m_displayAncestorData;
223}
224
226{
228 d->m_ancestorSeparator = separator;
229}
230
232{
234 return d->m_ancestorSeparator;
235}
236
238{
240
242
243 static const char *const modelSignals[] = {SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)),
244 SIGNAL(rowsInserted(QModelIndex, int, int)),
245 SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)),
246 SIGNAL(rowsRemoved(QModelIndex, int, int)),
247 SIGNAL(rowsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)),
248 SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)),
249 SIGNAL(modelAboutToBeReset()),
250 SIGNAL(modelReset()),
252 SIGNAL(layoutAboutToBeChanged()),
253 SIGNAL(layoutChanged()),
254 SIGNAL(destroyed())};
255 static const char *const proxySlots[] = {SLOT(sourceRowsAboutToBeInserted(QModelIndex, int, int)),
256 SLOT(sourceRowsInserted(QModelIndex, int, int)),
257 SLOT(sourceRowsAboutToBeRemoved(QModelIndex, int, int)),
258 SLOT(sourceRowsRemoved(QModelIndex, int, int)),
259 SLOT(sourceRowsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)),
260 SLOT(sourceRowsMoved(QModelIndex, int, int, QModelIndex, int)),
261 SLOT(sourceModelAboutToBeReset()),
262 SLOT(sourceModelReset()),
263 SLOT(sourceDataChanged(QModelIndex, QModelIndex)),
264 SLOT(sourceLayoutAboutToBeChanged()),
265 SLOT(sourceLayoutChanged()),
266 SLOT(sourceModelDestroyed())};
267
268 if (sourceModel()) {
269 for (int i = 0; i < int(sizeof modelSignals / sizeof *modelSignals); ++i) {
270 disconnect(sourceModel(), modelSignals[i], this, proxySlots[i]);
271 }
272 }
273
275
276 if (_sourceModel) {
277 for (int i = 0; i < int(sizeof modelSignals / sizeof *modelSignals); ++i) {
278 connect(_sourceModel, modelSignals[i], this, proxySlots[i]);
279 }
280 }
281
283 if (_sourceModel && _sourceModel->hasChildren()) {
284 d->synchronousMappingRefresh();
285 }
286
288}
289
291{
292 Q_UNUSED(index)
293 return {};
294}
295
296bool KDescendantsProxyModel::hasChildren(const QModelIndex &parent) const
297{
299 return !(d->m_mapping.isEmpty() || parent.isValid());
300}
301
302int KDescendantsProxyModel::rowCount(const QModelIndex &parent) const
303{
305 if (d->m_pendingParents.contains(parent) || parent.isValid() || !sourceModel()) {
306 return 0;
307 }
308
309 if (d->m_mapping.isEmpty() && sourceModel()->hasChildren()) {
310 Q_ASSERT(sourceModel()->rowCount() > 0);
311 const_cast<KDescendantsProxyModelPrivate *>(d)->synchronousMappingRefresh();
312 }
313 return d->m_rowCount;
314}
315
316QModelIndex KDescendantsProxyModel::index(int row, int column, const QModelIndex &parent) const
317{
318 if (parent.isValid()) {
319 return {};
320 }
321
322 if (!hasIndex(row, column, parent)) {
323 return {};
324 }
325
326 return createIndex(row, column);
327}
328
330{
332 if (d->m_mapping.isEmpty() || !proxyIndex.isValid() || !sourceModel()) {
333 return {};
334 }
335
336 const Mapping::right_const_iterator result = d->m_mapping.rightLowerBound(proxyIndex.row());
337 Q_ASSERT(result != d->m_mapping.rightEnd());
338
339 const int proxyLastRow = result.key();
340 const QModelIndex sourceLastChild = result.value();
341 Q_ASSERT(sourceLastChild.isValid());
342
343 // proxyLastRow is greater than proxyIndex.row().
344 // sourceLastChild is vertically below the result we're looking for
345 // and not necessarily in the correct parent.
346 // We travel up through its parent hierarchy until we are in the
347 // right parent, then return the correct sibling.
348
349 // Source: Proxy: Row
350 // - A - A - 0
351 // - B - B - 1
352 // - C - C - 2
353 // - D - D - 3
354 // - - E - E - 4
355 // - - F - F - 5
356 // - - G - G - 6
357 // - - H - H - 7
358 // - - I - I - 8
359 // - - - J - J - 9
360 // - - - K - K - 10
361 // - - - L - L - 11
362 // - - M - M - 12
363 // - - N - N - 13
364 // - O - O - 14
365
366 // Note that L, N and O are lastChildIndexes, and therefore have a mapping. If we
367 // are trying to map G from the proxy to the source, We at this point have an iterator
368 // pointing to (L -> 11). The proxy row of G is 6. (proxyIndex.row() == 6). We seek the
369 // sourceIndex which is vertically above L by the distance proxyLastRow - proxyIndex.row().
370 // In this case the verticalDistance is 5.
371
372 int verticalDistance = proxyLastRow - proxyIndex.row();
373
374 // We traverse the ancestors of L, until we can index the desired row in the source.
375
376 QModelIndex ancestor = sourceLastChild;
377 while (ancestor.isValid()) {
378 const int ancestorRow = ancestor.row();
379 if (verticalDistance <= ancestorRow) {
380 return ancestor.sibling(ancestorRow - verticalDistance, proxyIndex.column());
381 }
382 verticalDistance -= (ancestorRow + 1);
383 ancestor = ancestor.parent();
384 }
385 Q_ASSERT(!"Didn't find target row.");
386 return {};
387}
388
390{
392
393 if (!sourceModel()) {
394 return {};
395 }
396
397 if (d->m_mapping.isEmpty()) {
398 return {};
399 }
400
401 {
402 // TODO: Consider a parent Mapping to speed this up.
403
404 Mapping::right_const_iterator it = d->m_mapping.rightConstBegin();
405 const Mapping::right_const_iterator end = d->m_mapping.rightConstEnd();
406 const QModelIndex sourceParent = sourceIndex.parent();
407 Mapping::right_const_iterator result = end;
408
409 for (; it != end; ++it) {
410 QModelIndex index = it.value();
411 bool found_block = false;
412 while (index.isValid()) {
413 const QModelIndex ancestor = index.parent();
414 if (ancestor == sourceParent && index.row() >= sourceIndex.row()) {
415 found_block = true;
416 if (result == end || it.key() < result.key()) {
417 result = it;
418 break; // Leave the while loop. index is still valid.
419 }
420 }
421 index = ancestor;
422 }
423 if (found_block && !index.isValid())
424 // Looked through the ascendants of it.key() without finding sourceParent.
425 // That means we've already got the result we need.
426 {
427 break;
428 }
429 }
430 Q_ASSERT(result != end);
431 const QModelIndex sourceLastChild = result.value();
432 int proxyRow = result.key();
433 QModelIndex index = sourceLastChild;
434 while (index.isValid()) {
435 const QModelIndex ancestor = index.parent();
436 if (ancestor == sourceParent) {
437 return createIndex(proxyRow - (index.row() - sourceIndex.row()), sourceIndex.column());
438 }
439 proxyRow -= (index.row() + 1);
440 index = ancestor;
441 }
442 Q_ASSERT(!"Didn't find valid proxy mapping.");
443 return {};
444 }
445}
446
448{
449 if (parent.isValid() /* || rowCount(parent) == 0 */ || !sourceModel()) {
450 return 0;
451 }
452
453 return sourceModel()->columnCount();
454}
455
456QVariant KDescendantsProxyModel::data(const QModelIndex &index, int role) const
457{
459
460 if (!sourceModel()) {
461 return {};
462 }
463
464 if (!index.isValid()) {
465 return sourceModel()->data(index, role);
466 }
467
468 QModelIndex sourceIndex = mapToSource(index);
469
470 if ((d->m_displayAncestorData) && (role == Qt::DisplayRole)) {
471 if (!sourceIndex.isValid()) {
472 return {};
473 }
474 QString displayData = sourceIndex.data().toString();
475 sourceIndex = sourceIndex.parent();
476 while (sourceIndex.isValid()) {
477 displayData.prepend(d->m_ancestorSeparator);
478 displayData.prepend(sourceIndex.data().toString());
479 sourceIndex = sourceIndex.parent();
480 }
481 return displayData;
482 } else {
483 return sourceIndex.data(role);
484 }
485}
486
487QVariant KDescendantsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
488{
489 if (!sourceModel() || columnCount() <= section) {
490 return {};
491 }
492
493 return QAbstractProxyModel::headerData(section, orientation, role);
494}
495
497{
498 if (!index.isValid() || !sourceModel()) {
499 return QAbstractProxyModel::flags(index);
500 }
501
502 const QModelIndex srcIndex = mapToSource(index);
503 Q_ASSERT(srcIndex.isValid());
504 return sourceModel()->flags(srcIndex);
505}
506
507void KDescendantsProxyModelPrivate::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
508{
510
511 if (!q->sourceModel()->hasChildren(parent)) {
512 Q_ASSERT(q->sourceModel()->rowCount(parent) == 0);
513 // parent was not a parent before.
514 return;
515 }
516
517 int proxyStart = -1;
518
519 const int rowCount = q->sourceModel()->rowCount(parent);
520
521 if (rowCount > start) {
522 const QModelIndex belowStart = q->sourceModel()->index(start, 0, parent);
523 proxyStart = q->mapFromSource(belowStart).row();
524 } else if (rowCount == 0) {
525 proxyStart = q->mapFromSource(parent).row() + 1;
526 } else {
527 Q_ASSERT(rowCount == start);
528 static const int column = 0;
529 QModelIndex idx = q->sourceModel()->index(rowCount - 1, column, parent);
530 while (q->sourceModel()->hasChildren(idx)) {
531 Q_ASSERT(q->sourceModel()->rowCount(idx) > 0);
532 idx = q->sourceModel()->index(q->sourceModel()->rowCount(idx) - 1, column, idx);
533 }
534 // The last item in the list is getting a sibling below it.
535 proxyStart = q->mapFromSource(idx).row() + 1;
536 }
537 const int proxyEnd = proxyStart + (end - start);
538
539 m_insertPair = qMakePair(proxyStart, proxyEnd);
540 q->beginInsertRows(QModelIndex(), proxyStart, proxyEnd);
541}
542
543void KDescendantsProxyModelPrivate::sourceRowsInserted(const QModelIndex &parent, int start, int end)
544{
546
547 Q_ASSERT(q->sourceModel()->index(start, 0, parent).isValid());
548
549 const int rowCount = q->sourceModel()->rowCount(parent);
550 Q_ASSERT(rowCount > 0);
551
552 const int difference = end - start + 1;
553
554 if (rowCount == difference) {
555 // @p parent was not a parent before.
556 m_pendingParents.append(parent);
557 scheduleProcessPendingParents();
558 return;
559 }
560
561 const int proxyStart = m_insertPair.first;
562
563 Q_ASSERT(proxyStart >= 0);
564
565 updateInternalIndexes(proxyStart, difference);
566
567 if (rowCount - 1 == end) {
568 // The previously last row (the mapped one) is no longer the last.
569 // For example,
570
571 // - A - A 0
572 // - - B - B 1
573 // - - C - C 2
574 // - - - D - D 3
575 // - - - E -> - E 4
576 // - - F - F 5
577 // - - G -> - G 6
578 // - H - H 7
579 // - I -> - I 8
580
581 // As last children, E, F and G have mappings.
582 // Consider that 'J' is appended to the children of 'C', below 'E'.
583
584 // - A - A 0
585 // - - B - B 1
586 // - - C - C 2
587 // - - - D - D 3
588 // - - - E -> - E 4
589 // - - - J - ??? 5
590 // - - F - F 6
591 // - - G -> - G 7
592 // - H - H 8
593 // - I -> - I 9
594
595 // The updateInternalIndexes call above will have updated the F and G mappings correctly because proxyStart is 5.
596 // That means that E -> 4 was not affected by the updateInternalIndexes call.
597 // Now the mapping for E -> 4 needs to be updated so that it's a mapping for J -> 5.
598
599 Q_ASSERT(!m_mapping.isEmpty());
600 static const int column = 0;
601 const QModelIndex oldIndex = q->sourceModel()->index(rowCount - 1 - difference, column, parent);
602 Q_ASSERT(m_mapping.leftContains(oldIndex));
603
604 const QModelIndex newIndex = q->sourceModel()->index(rowCount - 1, column, parent);
605
606 QModelIndex indexAbove = oldIndex;
607
608 if (start > 0) {
609 // If we have something like this:
610 //
611 // - A
612 // - - B
613 // - - C
614 //
615 // and we then insert D as a sibling of A below it, we need to remove the mapping for A,
616 // and the row number used for D must take into account the descendants of A.
617
618 while (q->sourceModel()->hasChildren(indexAbove)) {
619 Q_ASSERT(q->sourceModel()->rowCount(indexAbove) > 0);
620 indexAbove = q->sourceModel()->index(q->sourceModel()->rowCount(indexAbove) - 1, column, indexAbove);
621 }
622 Q_ASSERT(q->sourceModel()->rowCount(indexAbove) == 0);
623 }
624
625 Q_ASSERT(m_mapping.leftContains(indexAbove));
626
627 const int newProxyRow = m_mapping.leftToRight(indexAbove) + difference;
628
629 // oldIndex is E in the source. proxyRow is 4.
630 m_mapping.removeLeft(oldIndex);
631
632 // newIndex is J. (proxyRow + difference) is 5.
633 m_mapping.insert(newIndex, newProxyRow);
634 }
635
636 for (int row = start; row <= end; ++row) {
637 static const int column = 0;
638 const QModelIndex idx = q->sourceModel()->index(row, column, parent);
639 Q_ASSERT(idx.isValid());
640 if (q->sourceModel()->hasChildren(idx)) {
641 Q_ASSERT(q->sourceModel()->rowCount(idx) > 0);
642 m_pendingParents.append(idx);
643 }
644 }
645
646 m_rowCount += difference;
647
648 q->endInsertRows();
649 scheduleProcessPendingParents();
650}
651
652void KDescendantsProxyModelPrivate::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
653{
655
656 const int proxyStart = q->mapFromSource(q->sourceModel()->index(start, 0, parent)).row();
657
658 static const int column = 0;
659 QModelIndex idx = q->sourceModel()->index(end, column, parent);
660 while (q->sourceModel()->hasChildren(idx)) {
661 Q_ASSERT(q->sourceModel()->rowCount(idx) > 0);
662 idx = q->sourceModel()->index(q->sourceModel()->rowCount(idx) - 1, column, idx);
663 }
664 const int proxyEnd = q->mapFromSource(idx).row();
665
666 m_removePair = qMakePair(proxyStart, proxyEnd);
667
668 q->beginRemoveRows(QModelIndex(), proxyStart, proxyEnd);
669}
670
671static QModelIndex getFirstDeepest(QAbstractItemModel *model, const QModelIndex &parent, int *count)
672{
673 static const int column = 0;
674 Q_ASSERT(model->hasChildren(parent));
675 Q_ASSERT(model->rowCount(parent) > 0);
676 for (int row = 0; row < model->rowCount(parent); ++row) {
677 (*count)++;
678 const QModelIndex child = model->index(row, column, parent);
679 Q_ASSERT(child.isValid());
680 if (model->hasChildren(child)) {
681 return getFirstDeepest(model, child, count);
682 }
683 }
684 return model->index(model->rowCount(parent) - 1, column, parent);
685}
686
687void KDescendantsProxyModelPrivate::sourceRowsRemoved(const QModelIndex &parent, int start, int end)
688{
690 Q_UNUSED(end)
691
692 const int rowCount = q->sourceModel()->rowCount(parent);
693
694 const int proxyStart = m_removePair.first;
695 const int proxyEnd = m_removePair.second;
696
697 const int difference = proxyEnd - proxyStart + 1;
698 {
699 Mapping::right_iterator it = m_mapping.rightLowerBound(proxyStart);
700 const Mapping::right_iterator endIt = m_mapping.rightUpperBound(proxyEnd);
701
702 if (endIt != m_mapping.rightEnd())
703 while (it != endIt) {
704 it = m_mapping.eraseRight(it);
705 }
706 else
707 while (it != m_mapping.rightUpperBound(proxyEnd)) {
708 it = m_mapping.eraseRight(it);
709 }
710 }
711
712 m_removePair = qMakePair(-1, -1);
713 m_rowCount -= difference;
714 Q_ASSERT(m_rowCount >= 0);
715
716 updateInternalIndexes(proxyStart, -1 * difference);
717
718 if (rowCount != start || rowCount == 0) {
719 q->endRemoveRows();
720 return;
721 }
722
723 static const int column = 0;
724 const QModelIndex newEnd = q->sourceModel()->index(rowCount - 1, column, parent);
725 Q_ASSERT(newEnd.isValid());
726
727 if (m_mapping.isEmpty()) {
728 m_mapping.insert(newEnd, newEnd.row());
729 q->endRemoveRows();
730 return;
731 }
732 if (q->sourceModel()->hasChildren(newEnd)) {
733 int count = 0;
734 const QModelIndex firstDeepest = getFirstDeepest(q->sourceModel(), newEnd, &count);
735 Q_ASSERT(firstDeepest.isValid());
736 const int firstDeepestProxy = m_mapping.leftToRight(firstDeepest);
737
738 m_mapping.insert(newEnd, firstDeepestProxy - count);
739 q->endRemoveRows();
740 return;
741 }
742 Mapping::right_iterator lowerBound = m_mapping.rightLowerBound(proxyStart);
743 if (lowerBound == m_mapping.rightEnd()) {
744 int proxyRow = (lowerBound - 1).key();
745
746 for (int row = newEnd.row(); row >= 0; --row) {
747 const QModelIndex newEndSibling = q->sourceModel()->index(row, column, parent);
748 if (!q->sourceModel()->hasChildren(newEndSibling)) {
749 ++proxyRow;
750 } else {
751 break;
752 }
753 }
754 m_mapping.insert(newEnd, proxyRow);
755 q->endRemoveRows();
756 return;
757 } else if (lowerBound == m_mapping.rightBegin()) {
758 int proxyRow = rowCount - 1;
759 QModelIndex trackedParent = parent;
760 while (trackedParent.isValid()) {
761 proxyRow += (trackedParent.row() + 1);
762 trackedParent = trackedParent.parent();
763 }
764 m_mapping.insert(newEnd, proxyRow);
765 q->endRemoveRows();
766 return;
767 }
768 const Mapping::right_iterator boundAbove = lowerBound - 1;
769
770 QList<QModelIndex> targetParents;
771 targetParents.push_back(parent);
772 {
773 QModelIndex target = parent;
774 int count = 0;
775 while (target.isValid()) {
776 if (target == boundAbove.value()) {
777 m_mapping.insert(newEnd, count + boundAbove.key() + newEnd.row() + 1);
778 q->endRemoveRows();
779 return;
780 }
781 count += (target.row() + 1);
782 target = target.parent();
783 if (target.isValid()) {
784 targetParents.push_back(target);
785 }
786 }
787 }
788
789 QModelIndex boundParent = boundAbove.value().parent();
790 QModelIndex prevParent = boundParent;
791 Q_ASSERT(boundParent.isValid());
792 while (boundParent.isValid()) {
793 prevParent = boundParent;
794 boundParent = boundParent.parent();
795
796 if (targetParents.contains(prevParent)) {
797 break;
798 }
799
800 if (!m_mapping.leftContains(prevParent)) {
801 break;
802 }
803
804 if (m_mapping.leftToRight(prevParent) > boundAbove.key()) {
805 break;
806 }
807 }
808
809 QModelIndex trackedParent = parent;
810
811 int proxyRow = boundAbove.key();
812
813 Q_ASSERT(prevParent.isValid());
814 proxyRow -= prevParent.row();
815 while (trackedParent != boundParent) {
816 proxyRow += (trackedParent.row() + 1);
817 trackedParent = trackedParent.parent();
818 }
819 m_mapping.insert(newEnd, proxyRow + newEnd.row());
820 q->endRemoveRows();
821}
822
823void KDescendantsProxyModelPrivate::sourceRowsAboutToBeMoved(const QModelIndex &srcParent,
824 int srcStart,
825 int srcEnd,
826 const QModelIndex &destParent,
827 int destStart)
828{
829 Q_UNUSED(srcParent)
830 Q_UNUSED(srcStart)
831 Q_UNUSED(srcEnd)
832 Q_UNUSED(destParent)
833 Q_UNUSED(destStart)
834 sourceLayoutAboutToBeChanged();
835}
836
837void KDescendantsProxyModelPrivate::sourceRowsMoved(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destStart)
838{
839 Q_UNUSED(srcParent)
840 Q_UNUSED(srcStart)
841 Q_UNUSED(srcEnd)
842 Q_UNUSED(destParent)
843 Q_UNUSED(destStart)
844 sourceLayoutChanged();
845}
846
847void KDescendantsProxyModelPrivate::sourceModelAboutToBeReset()
848{
850 q->beginResetModel();
851}
852
853void KDescendantsProxyModelPrivate::sourceModelReset()
854{
856 resetInternalData();
857 if (q->sourceModel()->hasChildren()) {
858 Q_ASSERT(q->sourceModel()->rowCount() > 0);
859 m_pendingParents.append(QModelIndex());
860 scheduleProcessPendingParents();
861 }
862 q->endResetModel();
863}
864
865void KDescendantsProxyModelPrivate::sourceLayoutAboutToBeChanged()
866{
868
869 if (m_ignoreNextLayoutChanged) {
870 m_ignoreNextLayoutChanged = false;
871 return;
872 }
873
874 if (m_mapping.isEmpty()) {
875 return;
876 }
877
878 QPersistentModelIndex srcPersistentIndex;
879 for (const QPersistentModelIndex &proxyPersistentIndex : q->persistentIndexList()) {
880 m_proxyIndexes << proxyPersistentIndex;
881 Q_ASSERT(proxyPersistentIndex.isValid());
882 srcPersistentIndex = q->mapToSource(proxyPersistentIndex);
883 Q_ASSERT(srcPersistentIndex.isValid());
884 m_layoutChangePersistentIndexes << srcPersistentIndex;
885 }
886
887 q->layoutAboutToBeChanged();
888}
889
890void KDescendantsProxyModelPrivate::sourceLayoutChanged()
891{
893
894 if (m_ignoreNextLayoutAboutToBeChanged) {
895 m_ignoreNextLayoutAboutToBeChanged = false;
896 return;
897 }
898
899 if (m_mapping.isEmpty()) {
900 return;
901 }
902
903 m_rowCount = 0;
904
905 synchronousMappingRefresh();
906
907 for (int i = 0; i < m_proxyIndexes.size(); ++i) {
908 q->changePersistentIndex(m_proxyIndexes.at(i), q->mapFromSource(m_layoutChangePersistentIndexes.at(i)));
909 }
910
911 m_layoutChangePersistentIndexes.clear();
912 m_proxyIndexes.clear();
913
914 q->layoutChanged();
915}
916
917void KDescendantsProxyModelPrivate::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
918{
920 Q_ASSERT(topLeft.model() == q->sourceModel());
921 Q_ASSERT(bottomRight.model() == q->sourceModel());
922
923 const int topRow = topLeft.row();
924 const int bottomRow = bottomRight.row();
925
926 for (int i = topRow; i <= bottomRow; ++i) {
927 const QModelIndex sourceTopLeft = q->sourceModel()->index(i, topLeft.column(), topLeft.parent());
928 Q_ASSERT(sourceTopLeft.isValid());
929 const QModelIndex proxyTopLeft = q->mapFromSource(sourceTopLeft);
930 // TODO. If an index does not have any descendants, then we can Q_EMIT in blocks of rows.
931 // As it is we Q_EMIT once for each row.
932 const QModelIndex sourceBottomRight = q->sourceModel()->index(i, bottomRight.column(), bottomRight.parent());
933 const QModelIndex proxyBottomRight = q->mapFromSource(sourceBottomRight);
934 Q_ASSERT(proxyTopLeft.isValid());
935 Q_ASSERT(proxyBottomRight.isValid());
936 Q_EMIT q->dataChanged(proxyTopLeft, proxyBottomRight);
937 }
938}
939
940void KDescendantsProxyModelPrivate::sourceModelDestroyed()
941{
942 resetInternalData();
943}
944
945QMimeData *KDescendantsProxyModel::mimeData(const QModelIndexList &indexes) const
946{
947 if (!sourceModel()) {
948 return QAbstractProxyModel::mimeData(indexes);
949 }
950 Q_ASSERT(sourceModel());
951 QModelIndexList sourceIndexes;
952 for (const QModelIndex &index : indexes) {
953 sourceIndexes << mapToSource(index);
954 }
955 return sourceModel()->mimeData(sourceIndexes);
956}
957
959{
960 if (!sourceModel()) {
962 }
963 Q_ASSERT(sourceModel());
964 return sourceModel()->mimeTypes();
965}
966
968{
969 if (!sourceModel()) {
971 }
972 return sourceModel()->supportedDropActions();
973}
974
975}
976
977#include "moc_kdescendantsproxymodel.cpp"
Proxy Model for restructuring a Tree into a list.
KDescendantsProxyModel(QObject *parent=nullptr)
Creates a new descendant entities proxy model.
~KDescendantsProxyModel() override
Destroys the descendant entities proxy model.
void setAncestorSeparator(const QString &separator)
Sets the ancestor separator used between data of ancestors.
QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits=1, Qt::MatchFlags flags=Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const override
Reimplemented to match all descendants.
void setDisplayAncestorData(bool display)
Set whether to show ancestor data in the model.
void setSourceModel(QAbstractItemModel *model) override
Sets the source model of the proxy.
bool displayAncestorData() const
Whether ancestor data will be displayed.
QString ancestorSeparator() const
Separator used between data of ancestors.
Q_SCRIPTABLE Q_NOREPLY void start()
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
const QList< QKeySequence > & begin()
Binds a QML item to a specific geodetic location in screen coordinates.
virtual int columnCount(const QModelIndex &parent) const const=0
QModelIndex createIndex(int row, int column, const void *ptr) const const
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
virtual bool hasChildren(const QModelIndex &parent) const const
bool hasIndex(int row, int column, const QModelIndex &parent) const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
void layoutAboutToBeChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
void layoutChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const const
void modelAboutToBeReset()
virtual void resetInternalData()
virtual int rowCount(const QModelIndex &parent) const const=0
void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
void rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow)
void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
void rowsInserted(const QModelIndex &parent, int first, int last)
void rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow)
void rowsRemoved(const QModelIndex &parent, int first, int last)
virtual QVariant data(const QModelIndex &proxyIndex, int role) const const override
virtual Qt::ItemFlags flags(const QModelIndex &index) const const override
virtual bool hasChildren(const QModelIndex &parent) const const override
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const const override
virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const const=0
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const const=0
virtual QMimeData * mimeData(const QModelIndexList &indexes) const const override
virtual QStringList mimeTypes() const const override
virtual void setSourceModel(QAbstractItemModel *sourceModel)
virtual Qt::DropActions supportedDropActions() const const override
const_iterator constBegin() const const
const_iterator constEnd() const const
iterator insert(const Key &key, const T &value)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
iterator begin()
void clear()
bool contains(const AT &value) const const
iterator end()
iterator erase(const_iterator begin, const_iterator end)
bool isEmpty() const const
void push_back(parameter_type value)
int column() const const
QVariant data(int role) const const
bool isValid() const const
const QAbstractItemModel * model() const const
QModelIndex parent() const const
int row() const const
QModelIndex sibling(int row, int column) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void destroyed(QObject *obj)
bool disconnect(const QMetaObject::Connection &connection)
QObject * parent() const const
bool isValid() const const
QString & prepend(QChar ch)
typedef DropActions
DisplayRole
typedef ItemFlags
typedef MatchFlags
Orientation
QString toString() const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 8 2024 12:02:44 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.