9#include "kdescendantsproxymodel.h"
19typedef KHash2Map<QPersistentModelIndex, int> Mapping;
21class KDescendantsProxyModelPrivate
26 m_ignoreNextLayoutAboutToBeChanged(false),
27 m_ignoreNextLayoutChanged(false),
29 m_displayAncestorData(false),
30 m_ancestorSeparator(QStringLiteral(
" / "))
39 void scheduleProcessPendingParents()
const;
40 void processPendingParents();
42 void synchronousMappingRefresh();
44 void updateInternalIndexes(
int start,
int offset);
46 void resetInternalData();
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);
54 void sourceModelAboutToBeReset();
55 void sourceModelReset();
56 void sourceLayoutAboutToBeChanged();
57 void sourceLayoutChanged();
59 void sourceModelDestroyed();
63 QPair<int, int> m_removePair;
64 QPair<int, int> m_insertPair;
66 bool m_ignoreNextLayoutAboutToBeChanged;
67 bool m_ignoreNextLayoutChanged;
70 bool m_displayAncestorData;
74 QModelIndexList m_proxyIndexes;
77void KDescendantsProxyModelPrivate::resetInternalData()
81 m_layoutChangePersistentIndexes.
clear();
82 m_proxyIndexes.clear();
85void KDescendantsProxyModelPrivate::synchronousMappingRefresh()
89 m_pendingParents.clear();
94 while (!m_pendingParents.isEmpty()) {
95 processPendingParents();
97 m_relayouting =
false;
100void KDescendantsProxyModelPrivate::scheduleProcessPendingParents()
const
102 const_cast<KDescendantsProxyModelPrivate *
>(
this)->processPendingParents();
105void KDescendantsProxyModelPrivate::processPendingParents()
115 while (it != end && it != m_pendingParents.end()) {
117 if (!sourceParent.
isValid() && m_rowCount > 0) {
119 it = m_pendingParents.erase(it);
122 const int rowCount = q->sourceModel()->rowCount(sourceParent);
124 Q_ASSERT(rowCount > 0);
127 Q_ASSERT(sourceIndex.
isValid());
129 const QModelIndex proxyParent = q->mapFromSource(sourceParent);
132 const int proxyEndRow = proxyParent.
row() + rowCount;
133 const int proxyStartRow = proxyEndRow - rowCount + 1;
135 if (!m_relayouting) {
136 q->beginInsertRows(
QModelIndex(), proxyStartRow, proxyEndRow);
139 updateInternalIndexes(proxyStartRow, rowCount);
140 m_mapping.insert(sourceIndex, proxyEndRow);
141 it = m_pendingParents.erase(it);
142 m_rowCount += rowCount;
144 if (!m_relayouting) {
148 for (
int sourceRow = 0; sourceRow < rowCount; ++sourceRow) {
149 static const int column = 0;
150 const QModelIndex child = q->sourceModel()->index(sourceRow, column, sourceParent);
153 if (q->sourceModel()->hasChildren(child)) {
154 Q_ASSERT(q->sourceModel()->rowCount(child) > 0);
155 newPendingParents.
append(child);
159 m_pendingParents += newPendingParents;
160 if (!m_pendingParents.isEmpty()) {
161 processPendingParents();
166void KDescendantsProxyModelPrivate::updateInternalIndexes(
int start,
int offset)
171 Mapping::right_iterator it = m_mapping.rightLowerBound(
start);
172 const Mapping::right_iterator
end = m_mapping.rightEnd();
175 updates.
insert(it.key() + offset, *it);
184 for (; it !=
end; ++it) {
185 m_mapping.insert(it.value(), it.key());
202void KDescendantsProxyModel::setRootIndex(
const QModelIndex &index)
216 d->m_displayAncestorData = display;
222 return d->m_displayAncestorData;
228 d->m_ancestorSeparator = separator;
234 return d->m_ancestorSeparator;
264 SLOT(sourceModelAboutToBeReset()),
265 SLOT(sourceModelReset()),
267 SLOT(sourceLayoutAboutToBeChanged()),
268 SLOT(sourceLayoutChanged()),
269 SLOT(sourceModelDestroyed())
288 d->synchronousMappingRefresh();
300bool KDescendantsProxyModel::hasChildren(
const QModelIndex &parent)
const
303 return !(d->m_mapping.isEmpty() ||
parent.isValid());
313 if (d->m_mapping.isEmpty() &&
sourceModel()->hasChildren()) {
315 const_cast<KDescendantsProxyModelPrivate *
>(d)->synchronousMappingRefresh();
317 return d->m_rowCount;
340 const Mapping::right_const_iterator result = d->m_mapping.rightLowerBound(
proxyIndex.row());
341 Q_ASSERT(result != d->m_mapping.rightEnd());
389 Q_ASSERT(!
"Didn't find target row.");
401 if (d->m_mapping.isEmpty()) {
408 Mapping::right_const_iterator
it = d->m_mapping.rightConstBegin();
409 const Mapping::right_const_iterator
end = d->m_mapping.rightConstEnd();
411 Mapping::right_const_iterator result =
end;
420 if (result == end ||
it.key() < result.key()) {
446 Q_ASSERT(!
"Didn't find valid proxy mapping.");
512void KDescendantsProxyModelPrivate::sourceRowsAboutToBeInserted(
const QModelIndex &parent,
int start,
int end)
516 if (!q->sourceModel()->hasChildren(parent)) {
517 Q_ASSERT(q->sourceModel()->rowCount(parent) == 0);
524 const int rowCount = q->sourceModel()->rowCount(parent);
526 if (rowCount >
start) {
528 proxyStart = q->mapFromSource(belowStart).row();
529 }
else if (rowCount == 0) {
530 proxyStart = q->mapFromSource(parent).row() + 1;
532 Q_ASSERT(rowCount ==
start);
533 static const int column = 0;
534 QModelIndex idx = q->sourceModel()->index(rowCount - 1, column, parent);
535 while (q->sourceModel()->hasChildren(idx)) {
536 Q_ASSERT(q->sourceModel()->rowCount(idx) > 0);
537 idx = q->sourceModel()->index(q->sourceModel()->rowCount(idx) - 1, column, idx);
540 proxyStart = q->mapFromSource(idx).row() + 1;
542 const int proxyEnd = proxyStart + (
end -
start);
544 m_insertPair = qMakePair(proxyStart, proxyEnd);
545 q->beginInsertRows(
QModelIndex(), proxyStart, proxyEnd);
548void KDescendantsProxyModelPrivate::sourceRowsInserted(
const QModelIndex &parent,
int start,
int end)
552 Q_ASSERT(q->sourceModel()->index(
start, 0, parent).isValid());
554 const int rowCount = q->sourceModel()->rowCount(parent);
555 Q_ASSERT(rowCount > 0);
557 const int difference =
end -
start + 1;
559 if (rowCount == difference) {
561 m_pendingParents.append(parent);
562 scheduleProcessPendingParents();
566 const int proxyStart = m_insertPair.first;
568 Q_ASSERT(proxyStart >= 0);
570 updateInternalIndexes(proxyStart, difference);
572 if (rowCount - 1 == end) {
604 Q_ASSERT(!m_mapping.isEmpty());
605 static const int column = 0;
606 const QModelIndex oldIndex = q->sourceModel()->index(rowCount - 1 - difference, column, parent);
607 Q_ASSERT(m_mapping.leftContains(oldIndex));
609 const QModelIndex newIndex = q->sourceModel()->index(rowCount - 1, column, parent);
623 while (q->sourceModel()->hasChildren(indexAbove)) {
624 Q_ASSERT(q->sourceModel()->rowCount(indexAbove) > 0);
625 indexAbove = q->sourceModel()->index(q->sourceModel()->rowCount(indexAbove) - 1, column, indexAbove);
627 Q_ASSERT(q->sourceModel()->rowCount(indexAbove) == 0);
630 Q_ASSERT(m_mapping.leftContains(indexAbove));
632 const int newProxyRow = m_mapping.leftToRight(indexAbove) + difference;
635 m_mapping.removeLeft(oldIndex);
638 m_mapping.insert(newIndex, newProxyRow);
641 for (
int row =
start; row <=
end; ++row) {
642 static const int column = 0;
643 const QModelIndex idx = q->sourceModel()->index(row, column, parent);
645 if (q->sourceModel()->hasChildren(idx)) {
646 Q_ASSERT(q->sourceModel()->rowCount(idx) > 0);
647 m_pendingParents.append(idx);
651 m_rowCount += difference;
654 scheduleProcessPendingParents();
657void KDescendantsProxyModelPrivate::sourceRowsAboutToBeRemoved(
const QModelIndex &parent,
int start,
int end)
661 const int proxyStart = q->mapFromSource(q->sourceModel()->index(
start, 0, parent)).row();
663 static const int column = 0;
664 QModelIndex idx = q->sourceModel()->index(end, column, parent);
665 while (q->sourceModel()->hasChildren(idx)) {
666 Q_ASSERT(q->sourceModel()->rowCount(idx) > 0);
667 idx = q->sourceModel()->index(q->sourceModel()->rowCount(idx) - 1, column, idx);
669 const int proxyEnd = q->mapFromSource(idx).row();
671 m_removePair = qMakePair(proxyStart, proxyEnd);
673 q->beginRemoveRows(
QModelIndex(), proxyStart, proxyEnd);
678 static const int column = 0;
680 Q_ASSERT(model->
rowCount(parent) > 0);
681 for (
int row = 0; row < model->
rowCount(parent); ++row) {
686 return getFirstDeepest(model, child, count);
689 return model->
index(model->
rowCount(parent) - 1, column, parent);
692void KDescendantsProxyModelPrivate::sourceRowsRemoved(
const QModelIndex &parent,
int start,
int end)
697 const int rowCount = q->sourceModel()->rowCount(parent);
699 const int proxyStart = m_removePair.first;
700 const int proxyEnd = m_removePair.second;
702 const int difference = proxyEnd - proxyStart + 1;
704 Mapping::right_iterator it = m_mapping.rightLowerBound(proxyStart);
705 const Mapping::right_iterator endIt = m_mapping.rightUpperBound(proxyEnd);
707 if (endIt != m_mapping.rightEnd())
708 while (it != endIt) {
709 it = m_mapping.eraseRight(it);
712 while (it != m_mapping.rightUpperBound(proxyEnd)) {
713 it = m_mapping.eraseRight(it);
717 m_removePair = qMakePair(-1, -1);
718 m_rowCount -= difference;
719 Q_ASSERT(m_rowCount >= 0);
721 updateInternalIndexes(proxyStart, -1 * difference);
723 if (rowCount !=
start || rowCount == 0) {
728 static const int column = 0;
729 const QModelIndex newEnd = q->sourceModel()->index(rowCount - 1, column, parent);
732 if (m_mapping.isEmpty()) {
733 m_mapping.insert(newEnd, newEnd.
row());
737 if (q->sourceModel()->hasChildren(newEnd)) {
739 const QModelIndex firstDeepest = getFirstDeepest(q->sourceModel(), newEnd, &count);
740 Q_ASSERT(firstDeepest.
isValid());
741 const int firstDeepestProxy = m_mapping.leftToRight(firstDeepest);
743 m_mapping.insert(newEnd, firstDeepestProxy - count);
747 Mapping::right_iterator lowerBound = m_mapping.rightLowerBound(proxyStart);
748 if (lowerBound == m_mapping.rightEnd()) {
749 int proxyRow = (lowerBound - 1).key();
751 for (
int row = newEnd.
row(); row >= 0; --row) {
752 const QModelIndex newEndSibling = q->sourceModel()->index(row, column, parent);
753 if (!q->sourceModel()->hasChildren(newEndSibling)) {
759 m_mapping.insert(newEnd, proxyRow);
762 }
else if (lowerBound == m_mapping.rightBegin()) {
763 int proxyRow = rowCount - 1;
765 while (trackedParent.
isValid()) {
766 proxyRow += (trackedParent.
row() + 1);
767 trackedParent = trackedParent.
parent();
769 m_mapping.insert(newEnd, proxyRow);
773 const Mapping::right_iterator boundAbove = lowerBound - 1;
781 if (target == boundAbove.value()) {
782 m_mapping.insert(newEnd, count + boundAbove.key() + newEnd.
row() + 1);
786 count += (target.
row() + 1);
796 Q_ASSERT(boundParent.
isValid());
797 while (boundParent.
isValid()) {
798 prevParent = boundParent;
799 boundParent = boundParent.
parent();
801 if (targetParents.
contains(prevParent)) {
805 if (!m_mapping.leftContains(prevParent)) {
809 if (m_mapping.leftToRight(prevParent) > boundAbove.key()) {
816 int proxyRow = boundAbove.key();
818 Q_ASSERT(prevParent.
isValid());
819 proxyRow -= prevParent.
row();
820 while (trackedParent != boundParent) {
821 proxyRow += (trackedParent.
row() + 1);
822 trackedParent = trackedParent.
parent();
824 m_mapping.insert(newEnd, proxyRow + newEnd.
row());
828void KDescendantsProxyModelPrivate::sourceRowsAboutToBeMoved(
const QModelIndex &srcParent,
int srcStart,
int srcEnd,
const QModelIndex &destParent,
int destStart)
835 sourceLayoutAboutToBeChanged();
838void KDescendantsProxyModelPrivate::sourceRowsMoved(
const QModelIndex &srcParent,
int srcStart,
int srcEnd,
const QModelIndex &destParent,
int destStart)
845 sourceLayoutChanged();
848void KDescendantsProxyModelPrivate::sourceModelAboutToBeReset()
851 q->beginResetModel();
854void KDescendantsProxyModelPrivate::sourceModelReset()
858 if (q->sourceModel()->hasChildren()) {
859 Q_ASSERT(q->sourceModel()->rowCount() > 0);
861 scheduleProcessPendingParents();
866void KDescendantsProxyModelPrivate::sourceLayoutAboutToBeChanged()
870 if (m_ignoreNextLayoutChanged) {
871 m_ignoreNextLayoutChanged =
false;
875 if (m_mapping.isEmpty()) {
881 m_proxyIndexes << proxyPersistentIndex;
882 Q_ASSERT(proxyPersistentIndex.isValid());
883 srcPersistentIndex = q->mapToSource(proxyPersistentIndex);
884 Q_ASSERT(srcPersistentIndex.
isValid());
885 m_layoutChangePersistentIndexes << srcPersistentIndex;
888 q->layoutAboutToBeChanged();
891void KDescendantsProxyModelPrivate::sourceLayoutChanged()
895 if (m_ignoreNextLayoutAboutToBeChanged) {
896 m_ignoreNextLayoutAboutToBeChanged =
false;
900 if (m_mapping.isEmpty()) {
906 synchronousMappingRefresh();
908 for (
int i = 0; i < m_proxyIndexes.size(); ++i) {
909 q->changePersistentIndex(m_proxyIndexes.at(i), q->mapFromSource(m_layoutChangePersistentIndexes.
at(i)));
912 m_layoutChangePersistentIndexes.
clear();
913 m_proxyIndexes.clear();
918void KDescendantsProxyModelPrivate::sourceDataChanged(
const QModelIndex &topLeft,
const QModelIndex &bottomRight)
921 Q_ASSERT(topLeft.
model() == q->sourceModel());
922 Q_ASSERT(bottomRight.
model() == q->sourceModel());
924 const int topRow = topLeft.
row();
925 const int bottomRow = bottomRight.
row();
927 for (
int i = topRow; i <= bottomRow; ++i) {
929 Q_ASSERT(sourceTopLeft.
isValid());
930 const QModelIndex proxyTopLeft = q->mapFromSource(sourceTopLeft);
934 const QModelIndex proxyBottomRight = q->mapFromSource(sourceBottomRight);
935 Q_ASSERT(proxyTopLeft.
isValid());
936 Q_ASSERT(proxyBottomRight.
isValid());
937 emit q->dataChanged(proxyTopLeft, proxyBottomRight);
941void KDescendantsProxyModelPrivate::sourceModelDestroyed()
978#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()
const QList< QKeySequence > & begin()
const QList< QKeySequence > & end()
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 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
bool contains(const AT &value) const const
void push_back(parameter_type value)
bool isValid() const const
const QAbstractItemModel * model() const const
QModelIndex parent() 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
T qobject_cast(QObject *object)
bool isValid() const const