9#include "kdescendantsproxymodel.h"
19using Mapping = KHash2Map<QPersistentModelIndex, int>;
21class KDescendantsProxyModelPrivate
26 , m_ignoreNextLayoutAboutToBeChanged(false)
27 , m_ignoreNextLayoutChanged(false)
28 , m_relayouting(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());
192 , d_ptr(new KDescendantsProxyModelPrivate(this))
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;
255 static const char *
const proxySlots[] = {SLOT(sourceRowsAboutToBeInserted(
QModelIndex,
int,
int)),
257 SLOT(sourceRowsAboutToBeRemoved(
QModelIndex,
int,
int)),
261 SLOT(sourceModelAboutToBeReset()),
262 SLOT(sourceModelReset()),
264 SLOT(sourceLayoutAboutToBeChanged()),
265 SLOT(sourceLayoutChanged()),
266 SLOT(sourceModelDestroyed())};
269 for (
int i = 0; i < int(
sizeof modelSignals /
sizeof *modelSignals); ++i) {
277 for (
int i = 0; i < int(
sizeof modelSignals /
sizeof *modelSignals); ++i) {
278 connect(_sourceModel, modelSignals[i],
this, proxySlots[i]);
284 d->synchronousMappingRefresh();
299 return !(d->m_mapping.isEmpty() ||
parent.isValid());
309 if (d->m_mapping.isEmpty() &&
sourceModel()->hasChildren()) {
311 const_cast<KDescendantsProxyModelPrivate *
>(d)->synchronousMappingRefresh();
313 return d->m_rowCount;
336 const Mapping::right_const_iterator result = d->m_mapping.rightLowerBound(proxyIndex.
row());
337 Q_ASSERT(result != d->m_mapping.rightEnd());
339 const int proxyLastRow = result.key();
340 const QModelIndex sourceLastChild = result.value();
341 Q_ASSERT(sourceLastChild.
isValid());
372 int verticalDistance = proxyLastRow - proxyIndex.
row();
378 const int ancestorRow = ancestor.
row();
379 if (verticalDistance <= ancestorRow) {
380 return ancestor.
sibling(ancestorRow - verticalDistance, proxyIndex.
column());
382 verticalDistance -= (ancestorRow + 1);
383 ancestor = ancestor.
parent();
385 Q_ASSERT(!
"Didn't find target row.");
397 if (d->m_mapping.isEmpty()) {
404 Mapping::right_const_iterator it = d->m_mapping.rightConstBegin();
405 const Mapping::right_const_iterator
end = d->m_mapping.rightConstEnd();
407 Mapping::right_const_iterator result =
end;
409 for (; it !=
end; ++it) {
411 bool found_block =
false;
414 if (ancestor == sourceParent && index.
row() >= sourceIndex.
row()) {
416 if (result == end || it.key() < result.key()) {
423 if (found_block && !index.
isValid())
430 Q_ASSERT(result != end);
431 const QModelIndex sourceLastChild = result.value();
432 int proxyRow = result.key();
436 if (ancestor == sourceParent) {
439 proxyRow -= (index.
row() + 1);
442 Q_ASSERT(!
"Didn't find valid proxy mapping.");
475 sourceIndex = sourceIndex.
parent();
476 while (sourceIndex.
isValid()) {
477 displayData.
prepend(d->m_ancestorSeparator);
479 sourceIndex = sourceIndex.
parent();
483 return sourceIndex.
data(role);
507void KDescendantsProxyModelPrivate::sourceRowsAboutToBeInserted(
const QModelIndex &parent,
int start,
int end)
511 if (!q->sourceModel()->hasChildren(parent)) {
512 Q_ASSERT(q->sourceModel()->rowCount(parent) == 0);
519 const int rowCount = q->sourceModel()->rowCount(parent);
521 if (rowCount >
start) {
523 proxyStart = q->mapFromSource(belowStart).row();
524 }
else if (rowCount == 0) {
525 proxyStart = q->mapFromSource(parent).row() + 1;
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);
535 proxyStart = q->mapFromSource(idx).row() + 1;
537 const int proxyEnd = proxyStart + (
end -
start);
539 m_insertPair = qMakePair(proxyStart, proxyEnd);
540 q->beginInsertRows(
QModelIndex(), proxyStart, proxyEnd);
543void KDescendantsProxyModelPrivate::sourceRowsInserted(
const QModelIndex &parent,
int start,
int end)
547 Q_ASSERT(q->sourceModel()->index(
start, 0, parent).isValid());
549 const int rowCount = q->sourceModel()->rowCount(parent);
550 Q_ASSERT(rowCount > 0);
552 const int difference =
end -
start + 1;
554 if (rowCount == difference) {
556 m_pendingParents.
append(parent);
557 scheduleProcessPendingParents();
561 const int proxyStart = m_insertPair.first;
563 Q_ASSERT(proxyStart >= 0);
565 updateInternalIndexes(proxyStart, difference);
567 if (rowCount - 1 == end) {
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));
604 const QModelIndex newIndex = q->sourceModel()->index(rowCount - 1, column, parent);
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);
622 Q_ASSERT(q->sourceModel()->rowCount(indexAbove) == 0);
625 Q_ASSERT(m_mapping.leftContains(indexAbove));
627 const int newProxyRow = m_mapping.leftToRight(indexAbove) + difference;
630 m_mapping.removeLeft(oldIndex);
633 m_mapping.insert(newIndex, newProxyRow);
636 for (
int row =
start; row <=
end; ++row) {
637 static const int column = 0;
638 const QModelIndex idx = q->sourceModel()->index(row, column, parent);
640 if (q->sourceModel()->hasChildren(idx)) {
641 Q_ASSERT(q->sourceModel()->rowCount(idx) > 0);
642 m_pendingParents.
append(idx);
646 m_rowCount += difference;
649 scheduleProcessPendingParents();
652void KDescendantsProxyModelPrivate::sourceRowsAboutToBeRemoved(
const QModelIndex &parent,
int start,
int end)
656 const int proxyStart = q->mapFromSource(q->sourceModel()->index(
start, 0, parent)).row();
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);
664 const int proxyEnd = q->mapFromSource(idx).row();
666 m_removePair = qMakePair(proxyStart, proxyEnd);
668 q->beginRemoveRows(
QModelIndex(), proxyStart, proxyEnd);
673 static const int column = 0;
675 Q_ASSERT(model->
rowCount(parent) > 0);
676 for (
int row = 0; row < model->
rowCount(parent); ++row) {
681 return getFirstDeepest(model, child, count);
684 return model->
index(model->
rowCount(parent) - 1, column, parent);
687void KDescendantsProxyModelPrivate::sourceRowsRemoved(
const QModelIndex &parent,
int start,
int end)
692 const int rowCount = q->sourceModel()->rowCount(parent);
694 const int proxyStart = m_removePair.first;
695 const int proxyEnd = m_removePair.second;
697 const int difference = proxyEnd - proxyStart + 1;
699 Mapping::right_iterator it = m_mapping.rightLowerBound(proxyStart);
700 const Mapping::right_iterator endIt = m_mapping.rightUpperBound(proxyEnd);
702 if (endIt != m_mapping.rightEnd())
703 while (it != endIt) {
704 it = m_mapping.eraseRight(it);
707 while (it != m_mapping.rightUpperBound(proxyEnd)) {
708 it = m_mapping.eraseRight(it);
712 m_removePair = qMakePair(-1, -1);
713 m_rowCount -= difference;
714 Q_ASSERT(m_rowCount >= 0);
716 updateInternalIndexes(proxyStart, -1 * difference);
718 if (rowCount !=
start || rowCount == 0) {
723 static const int column = 0;
724 const QModelIndex newEnd = q->sourceModel()->index(rowCount - 1, column, parent);
727 if (m_mapping.isEmpty()) {
728 m_mapping.insert(newEnd, newEnd.
row());
732 if (q->sourceModel()->hasChildren(newEnd)) {
734 const QModelIndex firstDeepest = getFirstDeepest(q->sourceModel(), newEnd, &count);
735 Q_ASSERT(firstDeepest.
isValid());
736 const int firstDeepestProxy = m_mapping.leftToRight(firstDeepest);
738 m_mapping.insert(newEnd, firstDeepestProxy - count);
742 Mapping::right_iterator lowerBound = m_mapping.rightLowerBound(proxyStart);
743 if (lowerBound == m_mapping.rightEnd()) {
744 int proxyRow = (lowerBound - 1).key();
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)) {
754 m_mapping.insert(newEnd, proxyRow);
757 }
else if (lowerBound == m_mapping.rightBegin()) {
758 int proxyRow = rowCount - 1;
760 while (trackedParent.
isValid()) {
761 proxyRow += (trackedParent.
row() + 1);
762 trackedParent = trackedParent.
parent();
764 m_mapping.insert(newEnd, proxyRow);
768 const Mapping::right_iterator boundAbove = lowerBound - 1;
776 if (target == boundAbove.value()) {
777 m_mapping.insert(newEnd, count + boundAbove.key() + newEnd.
row() + 1);
781 count += (target.
row() + 1);
791 Q_ASSERT(boundParent.
isValid());
792 while (boundParent.
isValid()) {
793 prevParent = boundParent;
794 boundParent = boundParent.
parent();
796 if (targetParents.
contains(prevParent)) {
800 if (!m_mapping.leftContains(prevParent)) {
804 if (m_mapping.leftToRight(prevParent) > boundAbove.key()) {
811 int proxyRow = boundAbove.key();
813 Q_ASSERT(prevParent.
isValid());
814 proxyRow -= prevParent.
row();
815 while (trackedParent != boundParent) {
816 proxyRow += (trackedParent.
row() + 1);
817 trackedParent = trackedParent.
parent();
819 m_mapping.insert(newEnd, proxyRow + newEnd.
row());
823void KDescendantsProxyModelPrivate::sourceRowsAboutToBeMoved(
const QModelIndex &srcParent,
834 sourceLayoutAboutToBeChanged();
837void KDescendantsProxyModelPrivate::sourceRowsMoved(
const QModelIndex &srcParent,
int srcStart,
int srcEnd,
const QModelIndex &destParent,
int destStart)
844 sourceLayoutChanged();
847void KDescendantsProxyModelPrivate::sourceModelAboutToBeReset()
850 q->beginResetModel();
853void KDescendantsProxyModelPrivate::sourceModelReset()
857 if (q->sourceModel()->hasChildren()) {
858 Q_ASSERT(q->sourceModel()->rowCount() > 0);
860 scheduleProcessPendingParents();
865void KDescendantsProxyModelPrivate::sourceLayoutAboutToBeChanged()
869 if (m_ignoreNextLayoutChanged) {
870 m_ignoreNextLayoutChanged =
false;
874 if (m_mapping.isEmpty()) {
880 m_proxyIndexes << proxyPersistentIndex;
881 Q_ASSERT(proxyPersistentIndex.isValid());
882 srcPersistentIndex = q->mapToSource(proxyPersistentIndex);
883 Q_ASSERT(srcPersistentIndex.
isValid());
884 m_layoutChangePersistentIndexes << srcPersistentIndex;
887 q->layoutAboutToBeChanged();
890void KDescendantsProxyModelPrivate::sourceLayoutChanged()
894 if (m_ignoreNextLayoutAboutToBeChanged) {
895 m_ignoreNextLayoutAboutToBeChanged =
false;
899 if (m_mapping.isEmpty()) {
905 synchronousMappingRefresh();
907 for (
int i = 0; i < m_proxyIndexes.size(); ++i) {
908 q->changePersistentIndex(m_proxyIndexes.at(i), q->mapFromSource(m_layoutChangePersistentIndexes.
at(i)));
911 m_layoutChangePersistentIndexes.
clear();
912 m_proxyIndexes.clear();
917void KDescendantsProxyModelPrivate::sourceDataChanged(
const QModelIndex &topLeft,
const QModelIndex &bottomRight)
920 Q_ASSERT(topLeft.
model() == q->sourceModel());
921 Q_ASSERT(bottomRight.
model() == q->sourceModel());
923 const int topRow = topLeft.
row();
924 const int bottomRow = bottomRight.
row();
926 for (
int i = topRow; i <= bottomRow; ++i) {
928 Q_ASSERT(sourceTopLeft.
isValid());
929 const QModelIndex proxyTopLeft = q->mapFromSource(sourceTopLeft);
933 const QModelIndex proxyBottomRight = q->mapFromSource(sourceBottomRight);
934 Q_ASSERT(proxyTopLeft.
isValid());
935 Q_ASSERT(proxyBottomRight.
isValid());
936 Q_EMIT q->dataChanged(proxyTopLeft, proxyBottomRight);
940void KDescendantsProxyModelPrivate::sourceModelDestroyed()
951 QModelIndexList sourceIndexes;
953 sourceIndexes << mapToSource(index);
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()
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 bool hasChildren(const QModelIndex &parent) 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
iterator erase(const_iterator begin, const_iterator end)
bool isEmpty() const const
void push_back(parameter_type value)
QVariant data(int role) const const
bool isValid() const const
const QAbstractItemModel * model() const const
QModelIndex parent() 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)
QString toString() const const