6#include <QtCore/qstack.h>
7#include <QtCore/qdebug.h>
14#if defined(QQMLTREEMODELADAPTOR_DEBUG) && !defined(QT_TESTLIB_LIB)
15# define ASSERT_CONSISTENCY() Q_ASSERT_X(testConsistency(true ), Q_FUNC_INFO, "Consistency test failed")
17# define ASSERT_CONSISTENCY qt_noop
20QQmlTreeModelToTableModel::QQmlTreeModelToTableModel(QObject *parent)
21 : QAbstractItemModel(parent)
25QAbstractItemModel *QQmlTreeModelToTableModel::model()
const
30void QQmlTreeModelToTableModel::connectToModel()
33 QObject::connect(m_model, &QAbstractItemModel::destroyed,
34 this, &QQmlTreeModelToTableModel::modelHasBeenDestroyed),
35 QObject::connect(m_model, &QAbstractItemModel::modelReset,
36 this, &QQmlTreeModelToTableModel::modelHasBeenReset),
37 QObject::connect(m_model, &QAbstractItemModel::dataChanged,
38 this, &QQmlTreeModelToTableModel::modelDataChanged),
40 QObject::connect(m_model, &QAbstractItemModel::layoutAboutToBeChanged,
41 this, &QQmlTreeModelToTableModel::modelLayoutAboutToBeChanged),
42 QObject::connect(m_model, &QAbstractItemModel::layoutChanged,
43 this, &QQmlTreeModelToTableModel::modelLayoutChanged),
45 QObject::connect(m_model, &QAbstractItemModel::rowsAboutToBeInserted,
46 this, &QQmlTreeModelToTableModel::modelRowsAboutToBeInserted),
47 QObject::connect(m_model, &QAbstractItemModel::rowsInserted,
48 this, &QQmlTreeModelToTableModel::modelRowsInserted),
49 QObject::connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
50 this, &QQmlTreeModelToTableModel::modelRowsAboutToBeRemoved),
51 QObject::connect(m_model, &QAbstractItemModel::rowsRemoved,
52 this, &QQmlTreeModelToTableModel::modelRowsRemoved),
53 QObject::connect(m_model, &QAbstractItemModel::rowsAboutToBeMoved,
54 this, &QQmlTreeModelToTableModel::modelRowsAboutToBeMoved),
55 QObject::connect(m_model, &QAbstractItemModel::rowsMoved,
56 this, &QQmlTreeModelToTableModel::modelRowsMoved),
58 QObject::connect(m_model, &QAbstractItemModel::columnsAboutToBeInserted,
59 this, &QQmlTreeModelToTableModel::modelColumnsAboutToBeInserted),
60 QObject::connect(m_model, &QAbstractItemModel::columnsAboutToBeRemoved,
61 this, &QQmlTreeModelToTableModel::modelColumnsAboutToBeRemoved),
62 QObject::connect(m_model, &QAbstractItemModel::columnsInserted,
63 this, &QQmlTreeModelToTableModel::modelColumnsInserted),
64 QObject::connect(m_model, &QAbstractItemModel::columnsRemoved,
65 this, &QQmlTreeModelToTableModel::modelColumnsRemoved)
69void QQmlTreeModelToTableModel::setModel(QAbstractItemModel *arg)
73 for (
const auto &c : m_connections)
74 QObject::disconnect(c);
75 m_connections.fill({});
81 if (m_rootIndex.isValid() && m_rootIndex.model() != m_model)
82 m_rootIndex = QModelIndex();
86 showModelTopLevelItems();
89 emit modelChanged(arg);
93void QQmlTreeModelToTableModel::clearModelData()
97 m_expandedItems.clear();
101QModelIndex QQmlTreeModelToTableModel::rootIndex()
const
106void QQmlTreeModelToTableModel::setRootIndex(
const QModelIndex &idx)
108 if (m_rootIndex == idx)
115 showModelTopLevelItems();
116 emit rootIndexChanged();
119void QQmlTreeModelToTableModel::resetRootIndex()
121 setRootIndex(QModelIndex());
124QModelIndex QQmlTreeModelToTableModel::index(
int row,
int column,
const QModelIndex &parent)
const
126 return hasIndex(row, column, parent) ? createIndex(row, column) : QModelIndex();
129QModelIndex QQmlTreeModelToTableModel::parent(
const QModelIndex &child)
const
132 return QModelIndex();
135QHash<
int, QByteArray> QQmlTreeModelToTableModel::roleNames()
const
138 return QHash<
int, QByteArray>();
139 return m_model->roleNames();
142int QQmlTreeModelToTableModel::rowCount(
const QModelIndex &)
const
146 return m_items.size();
149int QQmlTreeModelToTableModel::columnCount(
const QModelIndex &parent)
const
153 return m_model->columnCount(parent);
156QVariant QQmlTreeModelToTableModel::data(
const QModelIndex &index,
int role)
const
161 return m_model->data(mapToModel(index), role);
164bool QQmlTreeModelToTableModel::setData(
const QModelIndex &index,
const QVariant &value,
int role)
169 return m_model->setData(mapToModel(index), value, role);
172QVariant QQmlTreeModelToTableModel::headerData(
int section, Qt::Orientation orientation,
int role)
const
174 return m_model->headerData(section, orientation, role);
177Qt::ItemFlags QQmlTreeModelToTableModel::flags(
const QModelIndex &index)
const
179 return m_model->flags(mapToModel(index));
182int QQmlTreeModelToTableModel::depthAtRow(
int row)
const
184 if (row < 0 || row >= m_items.size())
186 return m_items.at(row).depth;
189int QQmlTreeModelToTableModel::itemIndex(
const QModelIndex &index)
const
192 if (!index.isValid() || index == m_rootIndex || m_items.isEmpty())
195 const int totalCount = m_items.size();
198 int localCount = qMin(m_lastItemIndex - 1, totalCount - m_lastItemIndex);
200 for (
int i = 0; i < localCount; ++i) {
201 const TreeItem &item1 = m_items.at(m_lastItemIndex + i);
202 if (item1.index == index) {
203 m_lastItemIndex = m_lastItemIndex + i;
204 return m_lastItemIndex;
206 const TreeItem &item2 = m_items.at(m_lastItemIndex - i - 1);
207 if (item2.index == index) {
208 m_lastItemIndex = m_lastItemIndex - i - 1;
209 return m_lastItemIndex;
213 for (
int j = qMax(0, m_lastItemIndex + localCount); j < totalCount; ++j) {
214 const TreeItem &item = m_items.at(j);
215 if (item.index == index) {
221 for (
int j = qMin(totalCount, m_lastItemIndex - localCount) - 1; j >= 0; --j) {
222 const TreeItem &item = m_items.at(j);
223 if (item.index == index) {
233bool QQmlTreeModelToTableModel::isVisible(
const QModelIndex &index)
235 return itemIndex(index) != -1;
238bool QQmlTreeModelToTableModel::childrenVisible(
const QModelIndex &index)
240 return (index == m_rootIndex && !m_items.isEmpty())
241 || (m_expandedItems.contains(index) && isVisible(index));
244QModelIndex QQmlTreeModelToTableModel::mapToModel(
const QModelIndex &index)
const
246 if (!index.isValid())
247 return QModelIndex();
249 const int row = index.row();
250 if (row < 0 || row > m_items.size() - 1)
251 return QModelIndex();
253 const QModelIndex sourceIndex = m_items.at(row).index;
254 return m_model->index(sourceIndex.row(), index.column(), sourceIndex.parent());
257QModelIndex QQmlTreeModelToTableModel::mapFromModel(
const QModelIndex &index)
const
259 if (!index.isValid())
260 return QModelIndex();
263 for (
int i = 0; i < m_items.size(); ++i) {
264 const QModelIndex proxyIndex = m_items[i].index;
265 if (proxyIndex.row() == index.row() && proxyIndex.parent() == index.parent()) {
272 return QModelIndex();
274 return this->index(row, index.column());
277QModelIndex QQmlTreeModelToTableModel::mapToModel(
int row)
const
279 if (row < 0 || row >= m_items.size())
280 return QModelIndex();
281 return m_items.at(row).index;
284QItemSelection QQmlTreeModelToTableModel::selectionForRowRange(
const QModelIndex &fromIndex,
const QModelIndex &toIndex)
const
286 int from = itemIndex(fromIndex);
287 int to = itemIndex(toIndex);
290 return QItemSelection();
291 return QItemSelection(toIndex, toIndex);
298 typedef std::pair<QModelIndex, QModelIndex> MIPair;
299 typedef QHash<QModelIndex, MIPair> MI2MIPairHash;
300 MI2MIPairHash ranges;
301 QModelIndex firstIndex = m_items.at(from).index;
302 QModelIndex lastIndex = firstIndex;
303 QModelIndex previousParent = firstIndex.parent();
304 bool selectLastRow =
false;
305 for (
int i = from + 1; i <= to || (selectLastRow =
true); i++) {
311 if (!selectLastRow) {
312 index = m_items.at(i).index;
313 parent = index.parent();
315 if (selectLastRow || previousParent != parent) {
316 const MI2MIPairHash::iterator &it = ranges.find(previousParent);
317 if (it == ranges.end())
318 ranges.insert(previousParent, MIPair(firstIndex, lastIndex));
320 it->second = lastIndex;
326 previousParent = parent;
332 sel.reserve(ranges.size());
333 for (
const MIPair &pair : std::as_const(ranges))
334 sel.append(QItemSelectionRange(pair.first, pair.second));
339void QQmlTreeModelToTableModel::showModelTopLevelItems(
bool doInsertRows)
344 if (m_model->hasChildren(m_rootIndex) && m_model->canFetchMore(m_rootIndex))
345 m_model->fetchMore(m_rootIndex);
346 const long topLevelRowCount = m_model->rowCount(m_rootIndex);
347 if (topLevelRowCount == 0)
350 showModelChildItems(TreeItem(m_rootIndex), 0, topLevelRowCount - 1, doInsertRows);
353void QQmlTreeModelToTableModel::showModelChildItems(
const TreeItem &parentItem,
int start,
int end,
bool doInsertRows,
bool doExpandPendingRows)
355 const QModelIndex &parentIndex = parentItem.index;
356 int rowIdx = parentIndex.isValid() && parentIndex != m_rootIndex ? itemIndex(parentIndex) + 1 : 0;
357 Q_ASSERT(rowIdx == 0 || parentItem.expanded);
358 if (parentIndex.isValid() && parentIndex != m_rootIndex && (rowIdx == 0 || !parentItem.expanded))
361 if (m_model->rowCount(parentIndex) == 0) {
362 if (m_model->hasChildren(parentIndex) && m_model->canFetchMore(parentIndex))
363 m_model->fetchMore(parentIndex);
367 int insertCount = end - start + 1;
374 const QModelIndex &nextSiblingIdx = m_model->index(end + 1, 0, parentIndex);
375 if (nextSiblingIdx.isValid()) {
376 startIdx = itemIndex(nextSiblingIdx);
378 const QModelIndex &prevSiblingIdx = m_model->index(start - 1, 0, parentIndex);
379 startIdx = lastChildIndex(prevSiblingIdx) + 1;
383 int rowDepth = rowIdx == 0 ? 0 : parentItem.depth + 1;
385 beginInsertRows(QModelIndex(), startIdx, startIdx + insertCount - 1);
386 m_items.reserve(m_items.size() + insertCount);
388 for (
int i = 0; i < insertCount; i++) {
389 const QModelIndex &cmi = m_model->index(start + i, 0, parentIndex);
390 const bool expanded = m_expandedItems.contains(cmi);
391 const TreeItem treeItem(cmi, rowDepth, expanded);
392 m_items.insert(startIdx + i, treeItem);
395 m_itemsToExpand.append(treeItem);
401 if (doExpandPendingRows)
402 expandPendingRows(doInsertRows);
406void QQmlTreeModelToTableModel::expand(
const QModelIndex &idx)
412 Q_ASSERT(!idx.isValid() || idx.model() == m_model);
414 if (!idx.isValid() || !m_model->hasChildren(idx))
416 if (m_expandedItems.contains(idx))
419 int row = itemIndex(idx);
423 m_expandedItems.insert(idx);
429void QQmlTreeModelToTableModel::collapse(
const QModelIndex &idx)
435 Q_ASSERT(!idx.isValid() || idx.model() == m_model);
437 if (!idx.isValid() || !m_model->hasChildren(idx))
439 if (!m_expandedItems.contains(idx))
442 int row = itemIndex(idx);
446 m_expandedItems.remove(idx);
452bool QQmlTreeModelToTableModel::isExpanded(
const QModelIndex &index)
const
458 Q_ASSERT(!index.isValid() || index.model() == m_model);
459 return !index.isValid() || m_expandedItems.contains(index);
462bool QQmlTreeModelToTableModel::isExpanded(
int row)
const
464 if (row < 0 || row >= m_items.size())
466 return m_items.at(row).expanded;
469bool QQmlTreeModelToTableModel::hasChildren(
int row)
const
471 if (row < 0 || row >= m_items.size())
473 return m_model->hasChildren(m_items[row].index);
476bool QQmlTreeModelToTableModel::hasSiblings(
int row)
const
478 const QModelIndex &index = mapToModel(row);
479 return index.row() != m_model->rowCount(index.parent()) - 1;
482void QQmlTreeModelToTableModel::expandRow(
int n)
484 if (!m_model || isExpanded(n))
487 TreeItem &item = m_items[n];
488 if ((item.index.flags() & Qt::ItemNeverHasChildren) || !m_model->hasChildren(item.index))
490 item.expanded =
true;
491 m_expandedItems.insert(item.index);
492 emit dataChanged(index(n, 0), index(n, 0), {ExpandedRole});
494 m_itemsToExpand.append(item);
498void QQmlTreeModelToTableModel::expandRecursively(
int row,
int depth)
500 Q_ASSERT(depth == -1 || depth > 0);
501 const int startDepth = depthAtRow(row);
503 auto expandHelp = [
this, depth, startDepth] (
const auto expandHelp,
const QModelIndex &index) ->
void {
504 const int rowToExpand = itemIndex(index);
505 if (!m_expandedItems.contains(index))
506 expandRow(rowToExpand);
508 if (depth != -1 && depthAtRow(rowToExpand) == startDepth + depth - 1)
511 const int childCount = m_model->rowCount(index);
512 for (
int childRow = 0; childRow < childCount; ++childRow) {
513 const QModelIndex childIndex = m_model->index(childRow, 0, index);
514 if (m_model->hasChildren(childIndex))
515 expandHelp(expandHelp, childIndex);
519 const QModelIndex index = m_items[row].index;
521 expandHelp(expandHelp, index);
524void QQmlTreeModelToTableModel::expandPendingRows(
bool doInsertRows)
526 while (!m_itemsToExpand.isEmpty()) {
527 const TreeItem item = m_itemsToExpand.takeFirst();
528 Q_ASSERT(item.expanded);
529 const QModelIndex &index = item.index;
530 int childrenCount = m_model->rowCount(index);
531 if (childrenCount == 0) {
532 if (m_model->hasChildren(index) && m_model->canFetchMore(index))
533 m_model->fetchMore(index);
540 showModelChildItems(item, 0, childrenCount - 1, doInsertRows,
false);
544void QQmlTreeModelToTableModel::collapseRecursively(
int row)
546 auto collapseHelp = [
this] (
const auto collapseHelp,
const QModelIndex &index) ->
void {
547 if (m_expandedItems.contains(index)) {
548 const int rowToCollapse = itemIndex(index);
549 if (rowToCollapse != -1)
550 collapseRow(rowToCollapse);
552 m_expandedItems.remove(index);
555 const int childCount = m_model->rowCount(index);
556 for (
int childRow = 0; childRow < childCount; ++childRow) {
557 const QModelIndex childIndex = m_model->index(childRow, 0, index);
558 if (m_model->hasChildren(childIndex))
559 collapseHelp(collapseHelp, childIndex);
563 const QModelIndex index = m_items[row].index;
565 collapseHelp(collapseHelp, index);
568void QQmlTreeModelToTableModel::collapseRow(
int n)
570 if (!m_model || !isExpanded(n))
573 SignalFreezer aggregator(
this);
575 TreeItem &item = m_items[n];
576 item.expanded =
false;
577 m_expandedItems.remove(item.index);
578 queueDataChanged(n, n, {ExpandedRole});
579 int childrenCount = m_model->rowCount(item.index);
580 if ((item.index.flags() & Qt::ItemNeverHasChildren) || !m_model->hasChildren(item.index) || childrenCount == 0)
583 const QModelIndex &emi = m_model->index(childrenCount - 1, 0, item.index);
584 int lastIndex = lastChildIndex(emi);
585 removeVisibleRows(n + 1, lastIndex);
588int QQmlTreeModelToTableModel::lastChildIndex(
const QModelIndex &index)
const
595 if (!m_expandedItems.contains(index))
596 return itemIndex(index);
598 QModelIndex parent = index.parent();
599 QModelIndex nextSiblingIndex;
600 while (parent.isValid()) {
601 nextSiblingIndex = parent.sibling(parent.row() + 1, 0);
602 if (nextSiblingIndex.isValid())
604 parent = parent.parent();
607 int firstIndex = nextSiblingIndex.isValid() ? itemIndex(nextSiblingIndex) : m_items.size();
608 return firstIndex - 1;
611void QQmlTreeModelToTableModel::removeVisibleRows(
int startIndex,
int endIndex,
bool doRemoveRows)
613 if (startIndex < 0 || endIndex < 0 || startIndex > endIndex)
617 beginRemoveRows(QModelIndex(), startIndex, endIndex);
618 m_items.erase(m_items.begin() + startIndex, m_items.begin() + endIndex + 1);
623 int lastIndex = m_items.size() - 1;
624 if (startIndex <= lastIndex)
625 queueDataChanged(startIndex, lastIndex, {ModelIndexRole});
629void QQmlTreeModelToTableModel::modelHasBeenDestroyed()
633 emit modelChanged(
nullptr);
636void QQmlTreeModelToTableModel::modelHasBeenReset()
640 showModelTopLevelItems();
644void QQmlTreeModelToTableModel::modelDataChanged(
const QModelIndex &topLeft,
const QModelIndex &bottomRight,
const QList<
int> &roles)
646 Q_ASSERT(topLeft.parent() == bottomRight.parent());
647 const QModelIndex &parent = topLeft.parent();
648 if (parent.isValid() && !childrenVisible(parent)) {
653 int topIndex = itemIndex(topLeft.siblingAtColumn(0));
656 for (
int i = topLeft.row(); i <= bottomRight.row(); i++) {
658 int bottomIndex = topIndex;
659 while (bottomIndex < m_items.size()) {
660 const QModelIndex &idx = m_items.at(bottomIndex).index;
661 if (idx.parent() != parent) {
665 if (idx.row() == bottomRight.row())
669 emit dataChanged(index(topIndex, topLeft.column()), index(bottomIndex, bottomRight.column()), roles);
671 i += bottomIndex - topIndex;
672 if (i == bottomRight.row())
674 topIndex = bottomIndex + 1;
675 while (topIndex < m_items.size()
676 && m_items.at(topIndex).index.parent() != parent)
682void QQmlTreeModelToTableModel::modelLayoutAboutToBeChanged(
const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint)
695 m_modelLayoutChanged =
false;
697 if (parents.isEmpty() || !parents[0].isValid()) {
699 emit layoutAboutToBeChanged();
700 m_modelLayoutChanged =
true;
705 for (
const QPersistentModelIndex &pmi : parents) {
706 if (!m_expandedItems.contains(pmi))
708 const int row = itemIndex(pmi);
711 const int rowCount = m_model->rowCount(pmi);
715 if (!m_modelLayoutChanged) {
716 emit layoutAboutToBeChanged();
717 m_modelLayoutChanged =
true;
720 const QModelIndex &lmi = m_model->index(rowCount - 1, 0, pmi);
721 const int lastRow = lastChildIndex(lmi);
722 removeVisibleRows(row + 1, lastRow,
false );
728void QQmlTreeModelToTableModel::modelLayoutChanged(
const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint)
732 if (!m_modelLayoutChanged) {
737 if (m_items.isEmpty()) {
739 showModelTopLevelItems(
false );
740 const QModelIndex &mi = m_model->index(0, 0);
741 const int columnCount = m_model->columnCount(mi);
742 emit dataChanged(index(0, 0), index(m_items.size() - 1, columnCount - 1));
743 emit layoutChanged();
747 for (
const QPersistentModelIndex &pmi : parents) {
748 if (!m_expandedItems.contains(pmi))
750 const int row = itemIndex(pmi);
753 const int rowCount = m_model->rowCount(pmi);
757 const QModelIndex &lmi = m_model->index(rowCount - 1, 0, pmi);
758 const int columnCount = m_model->columnCount(lmi);
759 showModelChildItems(m_items.at(row), 0, rowCount - 1,
false );
760 const int lastRow = lastChildIndex(lmi);
761 emit dataChanged(index(row + 1, 0), index(lastRow, columnCount - 1));
764 emit layoutChanged();
769void QQmlTreeModelToTableModel::modelRowsAboutToBeInserted(
const QModelIndex & parent,
int start,
int end)
777void QQmlTreeModelToTableModel::modelRowsInserted(
const QModelIndex & parent,
int start,
int end)
780 int parentRow = itemIndex(parent);
781 if (parentRow >= 0) {
782 queueDataChanged(parentRow, parentRow, {HasChildrenRole});
783 item = m_items.at(parentRow);
784 if (!item.expanded) {
788 }
else if (parent == m_rootIndex) {
789 item = TreeItem(parent);
794 showModelChildItems(item, start, end);
798void QQmlTreeModelToTableModel::modelRowsAboutToBeRemoved(
const QModelIndex & parent,
int start,
int end)
801 enableSignalAggregation();
802 if (parent == m_rootIndex || childrenVisible(parent)) {
803 const QModelIndex &smi = m_model->index(start, 0, parent);
804 int startIndex = itemIndex(smi);
805 const QModelIndex &emi = m_model->index(end, 0, parent);
807 if (isExpanded(emi)) {
808 int rowCount = m_model->rowCount(emi);
810 const QModelIndex &idx = m_model->index(rowCount - 1, 0, emi);
811 endIndex = lastChildIndex(idx);
815 endIndex = itemIndex(emi);
817 removeVisibleRows(startIndex, endIndex);
820 for (
int r = start; r <= end; r++) {
821 const QModelIndex &cmi = m_model->index(r, 0, parent);
822 m_expandedItems.remove(cmi);
826void QQmlTreeModelToTableModel::modelRowsRemoved(
const QModelIndex & parent,
int start,
int end)
830 int parentRow = itemIndex(parent);
832 queueDataChanged(parentRow, parentRow, {HasChildrenRole});
833 disableSignalAggregation();
837void QQmlTreeModelToTableModel::modelRowsAboutToBeMoved(
const QModelIndex & sourceParent,
int sourceStart,
int sourceEnd,
const QModelIndex & destinationParent,
int destinationRow)
840 enableSignalAggregation();
841 m_visibleRowsMoved =
false;
842 if (!childrenVisible(sourceParent))
845 if (!childrenVisible(destinationParent)) {
846 modelRowsAboutToBeRemoved(sourceParent, sourceStart, sourceEnd);
848
849 if (isVisible(destinationParent) && m_model->rowCount(destinationParent) == 0) {
850 const int parentRow = itemIndex(destinationParent);
851 queueDataChanged(parentRow, parentRow, {HasChildrenRole});
854 int depthDifference = -1;
855 if (destinationParent.isValid()) {
856 int destParentIndex = itemIndex(destinationParent);
857 depthDifference = m_items.at(destParentIndex).depth;
859 if (sourceParent.isValid()) {
860 int sourceParentIndex = itemIndex(sourceParent);
861 depthDifference -= m_items.at(sourceParentIndex).depth;
866 int startIndex = itemIndex(m_model->index(sourceStart, 0, sourceParent));
867 const QModelIndex &emi = m_model->index(sourceEnd, 0, sourceParent);
869 if (isExpanded(emi)) {
870 int rowCount = m_model->rowCount(emi);
872 endIndex = lastChildIndex(m_model->index(rowCount - 1, 0, emi));
875 endIndex = itemIndex(emi);
878 if (destinationRow == m_model->rowCount(destinationParent)) {
879 const QModelIndex &emi = m_model->index(destinationRow - 1, 0, destinationParent);
880 destIndex = lastChildIndex(emi) + 1;
882 destIndex = itemIndex(m_model->index(destinationRow, 0, destinationParent));
885 int totalMovedCount = endIndex - startIndex + 1;
888
889 m_visibleRowsMoved = startIndex != destIndex &&
890 beginMoveRows(QModelIndex(), startIndex, endIndex, QModelIndex(), destIndex);
892 const QList<TreeItem> &buffer = m_items.mid(startIndex, totalMovedCount);
893 int bufferCopyOffset;
894 if (destIndex > endIndex) {
895 for (
int i = endIndex + 1; i < destIndex; i++) {
896 m_items.swapItemsAt(i, i - totalMovedCount);
898 bufferCopyOffset = destIndex - totalMovedCount;
901 for (
int i = startIndex - 1; i >= destIndex; i--) {
902 m_items.swapItemsAt(i, i + totalMovedCount);
904 bufferCopyOffset = destIndex;
906 for (
int i = 0; i < buffer.size(); i++) {
907 TreeItem item = buffer.at(i);
908 item.depth += depthDifference;
909 m_items.replace(bufferCopyOffset + i, item);
913
914
915
916
917
918
919
920
921
922 const int top = qMin(startIndex, bufferCopyOffset);
923 int bottom = qMax(endIndex, bufferCopyOffset + totalMovedCount - 1);
924 if (sourceParent != destinationParent) {
925 const QModelIndex &bottomParent =
926 bottom == endIndex ? sourceParent : destinationParent;
928 const int rowCount = m_model->rowCount(bottomParent);
930 bottom = qMax(bottom, lastChildIndex(m_model->index(rowCount - 1, 0, bottomParent)));
932 queueDataChanged(top, bottom, {ModelIndexRole});
934 if (depthDifference != 0)
935 queueDataChanged(bufferCopyOffset, bufferCopyOffset + totalMovedCount - 1, {DepthRole});
939void QQmlTreeModelToTableModel::modelRowsMoved(
const QModelIndex & sourceParent,
int sourceStart,
int sourceEnd,
const QModelIndex & destinationParent,
int destinationRow)
941 if (!childrenVisible(sourceParent)) {
942 modelRowsInserted(destinationParent, destinationRow, destinationRow + sourceEnd - sourceStart);
943 }
else if (!childrenVisible(destinationParent)) {
944 modelRowsRemoved(sourceParent, sourceStart, sourceEnd);
947 if (m_visibleRowsMoved)
950 if (isVisible(sourceParent) && m_model->rowCount(sourceParent) == 0) {
951 int parentRow = itemIndex(sourceParent);
952 collapseRow(parentRow);
953 queueDataChanged(parentRow, parentRow, {ExpandedRole, HasChildrenRole});
956 disableSignalAggregation();
961void QQmlTreeModelToTableModel::modelColumnsAboutToBeInserted(
const QModelIndex & parent,
int start,
int end)
964 beginInsertColumns({}, start, end);
967void QQmlTreeModelToTableModel::modelColumnsAboutToBeRemoved(
const QModelIndex & parent,
int start,
int end)
970 beginRemoveColumns({}, start, end);
973void QQmlTreeModelToTableModel::modelColumnsInserted(
const QModelIndex & parent,
int start,
int end)
980 showModelTopLevelItems();
984void QQmlTreeModelToTableModel::modelColumnsRemoved(
const QModelIndex & parent,
int start,
int end)
991 showModelTopLevelItems();
995void QQmlTreeModelToTableModel::dump()
const
999 int count = m_items.size();
1002 int countWidth = floor(log10(
double(count))) + 1;
1003 qInfo() <<
"Dumping" <<
this;
1004 for (
int i = 0; i < count; i++) {
1005 const TreeItem &item = m_items.at(i);
1006 bool hasChildren = m_model->hasChildren(item.index);
1007 int children = m_model->rowCount(item.index);
1008 qInfo().noquote().nospace()
1009 << QStringLiteral(
"%1 ").arg(i, countWidth) << QString(4 * item.depth, QChar::fromLatin1(
'.'))
1010 << QLatin1String(!hasChildren ?
".. " : item.expanded ?
" v " :
" > ")
1011 << item.index << children;
1015bool QQmlTreeModelToTableModel::testConsistency(
bool dumpOnFail)
const
1018 if (!m_items.isEmpty()) {
1019 qWarning() <<
"Model inconsistency: No model but stored visible items";
1022 if (!m_expandedItems.isEmpty()) {
1023 qWarning() <<
"Model inconsistency: No model but stored expanded items";
1028 QModelIndex parent = m_rootIndex;
1029 QStack<QModelIndex> ancestors;
1030 QModelIndex idx = m_model->index(0, 0, parent);
1031 for (
int i = 0; i < m_items.size(); i++) {
1032 bool isConsistent =
true;
1033 const TreeItem &item = m_items.at(i);
1034 if (item.index != idx) {
1035 qWarning() <<
"QModelIndex inconsistency" << i << item.index;
1036 qWarning() <<
" expected" << idx;
1037 isConsistent =
false;
1039 if (item.index.parent() != parent) {
1040 qWarning() <<
"Parent inconsistency" << i << item.index;
1041 qWarning() <<
" stored index parent" << item.index.parent() <<
"model parent" << parent;
1042 isConsistent =
false;
1044 if (item.depth != ancestors.size()) {
1045 qWarning() <<
"Depth inconsistency" << i << item.index;
1046 qWarning() <<
" item depth" << item.depth <<
"ancestors stack" << ancestors.size();
1047 isConsistent =
false;
1049 if (item.expanded && !m_expandedItems.contains(item.index)) {
1050 qWarning() <<
"Expanded inconsistency" << i << item.index;
1051 qWarning() <<
" set" << m_expandedItems.contains(item.index) <<
"item" << item.expanded;
1052 isConsistent =
false;
1054 if (!isConsistent) {
1059 QModelIndex firstChildIndex;
1061 firstChildIndex = m_model->index(0, 0, idx);
1062 if (firstChildIndex.isValid()) {
1063 ancestors.push(parent);
1065 idx = m_model->index(0, 0, parent);
1067 while (idx.row() == m_model->rowCount(parent) - 1) {
1068 if (ancestors.isEmpty())
1071 parent = ancestors.pop();
1073 idx = m_model->index(idx.row() + 1, 0, parent);
1080void QQmlTreeModelToTableModel::enableSignalAggregation() {
1081 m_signalAggregatorStack++;
1084void QQmlTreeModelToTableModel::disableSignalAggregation() {
1085 m_signalAggregatorStack--;
1086 Q_ASSERT(m_signalAggregatorStack >= 0);
1087 if (m_signalAggregatorStack == 0) {
1088 emitQueuedSignals();
1092void QQmlTreeModelToTableModel::queueDataChanged(
int top,
int bottom,
1093 std::initializer_list<
int> roles)
1095 if (isAggregatingSignals())
1096 m_queuedDataChanged.append(DataChangedParams { top, bottom, roles });
1098 emit dataChanged(index(top, 0), index(bottom, 0), roles);
1101void QQmlTreeModelToTableModel::emitQueuedSignals()
1103 QVarLengthArray<DataChangedParams> combinedUpdates;
1105
1106
1107
1108
1109 for (
const DataChangedParams &dataChange : std::as_const(m_queuedDataChanged)) {
1110 const int startRow = dataChange.top;
1111 const int endRow = dataChange.bottom;
1112 bool merged =
false;
1113 for (DataChangedParams &combined : combinedUpdates) {
1114 int combinedStartRow = combined.top;
1115 int combinedEndRow = combined.bottom;
1116 if ((startRow <= combinedStartRow && endRow >= combinedStartRow) ||
1117 (startRow <= combinedEndRow && endRow >= combinedEndRow)) {
1118 if (startRow < combinedStartRow) {
1119 combined.top = dataChange.top;
1121 if (endRow > combinedEndRow) {
1122 combined.bottom = dataChange.bottom;
1124 for (
int role : dataChange.roles) {
1125 if (!combined.roles.contains(role))
1126 combined.roles.append(role);
1133 combinedUpdates.append(dataChange);
1138 for (
const DataChangedParams &dataChange : combinedUpdates) {
1139 const QModelIndex topLeft = index(dataChange.top, 0);
1140 const QModelIndex bottomRight = index(dataChange.bottom, 0);
1141 emit dataChanged(topLeft, bottomRight, {dataChange.roles.begin(), dataChange.roles.end()});
1143 m_queuedDataChanged.clear();
1148#include "moc_qqmltreemodeltotablemodel_p_p.cpp"
#define ASSERT_CONSISTENCY