/**************************************************************************** ** ** Copyright (C) 2009 Stephen Kelly ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "dynamictreemodel.h" #include #include #include #include DynamicTreeModel::DynamicTreeModel(QObject *parent) : QAbstractItemModel(parent), nextId(1) { } QModelIndex DynamicTreeModel::index(int row, int column, const QModelIndex &parent) const { // if (column != 0) // return QModelIndex(); if (column < 0 || row < 0) return QModelIndex(); QList > childIdColumns = m_childItems.value(parent.internalId()); const qint64 grandParent = findParentId(parent.internalId()); if (grandParent >= 0) { QList > parentTable = m_childItems.value(grandParent); if (parent.column() >= parentTable.size()) qFatal("%s: parent.column() must be less than parentTable.size()", Q_FUNC_INFO); QList parentSiblings = parentTable.at(parent.column()); if (parent.row() >= parentSiblings.size()) qFatal("%s: parent.row() must be less than parentSiblings.size()", Q_FUNC_INFO); } if (childIdColumns.size() == 0) return QModelIndex(); if (column >= childIdColumns.size()) return QModelIndex(); QList rowIds = childIdColumns.at(column); if (row >= rowIds.size()) return QModelIndex(); qint64 id = rowIds.at(row); return createIndex(row, column, reinterpret_cast(id)); } qint64 DynamicTreeModel::findParentId(qint64 searchId) const { if (searchId <= 0) return -1; for (auto i = m_childItems.cbegin(), end = m_childItems.cend(); i != end; ++i) { for (const auto &list : i.value()) { if (list.contains(searchId)) return i.key(); } } return -1; } QModelIndex DynamicTreeModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); qint64 searchId = index.internalId(); qint64 parentId = findParentId(searchId); // Will never happen for valid index, but what the hey... if (parentId <= 0) return QModelIndex(); qint64 grandParentId = findParentId(parentId); if (grandParentId < 0) grandParentId = 0; int column = 0; QList childList = m_childItems.value(grandParentId).at(column); int row = childList.indexOf(parentId); return createIndex(row, column, reinterpret_cast(parentId)); } int DynamicTreeModel::rowCount(const QModelIndex &index) const { QList > cols = m_childItems.value(index.internalId()); if (cols.size() == 0) return 0; if (index.column() > 0) return 0; return cols.at(0).size(); } int DynamicTreeModel::columnCount(const QModelIndex &index) const { // Q_UNUSED(index); return m_childItems.value(index.internalId()).size(); } QVariant DynamicTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (Qt::DisplayRole == role) return m_items.value(index.internalId()); return QVariant(); } void DynamicTreeModel::clear() { beginResetModel(); m_items.clear(); m_childItems.clear(); nextId = 1; endResetModel(); } ModelChangeCommand::ModelChangeCommand(DynamicTreeModel *model, QObject *parent) : QObject(parent), m_model(model), m_numCols(1), m_startRow(-1), m_endRow(-1) { } QModelIndex ModelChangeCommand::findIndex(const QList &rows) const { const int col = 0; QModelIndex parent = QModelIndex(); for (int row : rows) { parent = m_model->index(row, col, parent); if (!parent.isValid()) qFatal("%s: parent must be valid", Q_FUNC_INFO); } return parent; } ModelInsertCommand::ModelInsertCommand(DynamicTreeModel *model, QObject *parent) : ModelChangeCommand(model, parent) { } void ModelInsertCommand::doCommand() { QModelIndex parent = findIndex(m_rowNumbers); m_model->beginInsertRows(parent, m_startRow, m_endRow); qint64 parentId = parent.internalId(); for (int row = m_startRow; row <= m_endRow; row++) { for (int col = 0; col < m_numCols; col++) { if (m_model->m_childItems[parentId].size() <= col) m_model->m_childItems[parentId].append(QList()); // QString name = QUuid::createUuid().toString(); qint64 id = m_model->newId(); QString name = QString::number(id); m_model->m_items.insert(id, name); m_model->m_childItems[parentId][col].insert(row, id); } } m_model->endInsertRows(); } ModelMoveCommand::ModelMoveCommand(DynamicTreeModel *model, QObject *parent) : ModelChangeCommand(model, parent) { } bool ModelMoveCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) { return m_model->beginMoveRows(srcParent, srcStart, srcEnd, destParent, destRow); } void ModelMoveCommand::doCommand() { QModelIndex srcParent = findIndex(m_rowNumbers); QModelIndex destParent = findIndex(m_destRowNumbers); if (!emitPreSignal(srcParent, m_startRow, m_endRow, destParent, m_destRow)) return; for (int column = 0; column < m_numCols; ++column) { QList l = m_model->m_childItems.value(srcParent.internalId())[column].mid( m_startRow, m_endRow - m_startRow + 1); for (int i = m_startRow; i <= m_endRow; i++) m_model->m_childItems[srcParent.internalId()][column].removeAt(m_startRow); int d; if (m_destRow < m_startRow) { d = m_destRow; } else { if (srcParent == destParent) d = m_destRow - (m_endRow - m_startRow + 1); else d = m_destRow; } foreach (const qint64 id, l) m_model->m_childItems[destParent.internalId()][column].insert(d++, id); } emitPostSignal(); } void ModelMoveCommand::emitPostSignal() { m_model->endMoveRows(); } ModelResetCommand::ModelResetCommand(DynamicTreeModel *model, QObject *parent) : ModelMoveCommand(model, parent) { } ModelResetCommand::~ModelResetCommand() { } bool ModelResetCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) { Q_UNUSED(srcParent); Q_UNUSED(srcStart); Q_UNUSED(srcEnd); Q_UNUSED(destParent); Q_UNUSED(destRow); return true; } void ModelResetCommand::emitPostSignal() { m_model->beginResetModel(); m_model->endResetModel(); } ModelResetCommandFixed::ModelResetCommandFixed(DynamicTreeModel *model, QObject *parent) : ModelMoveCommand(model, parent) { } ModelResetCommandFixed::~ModelResetCommandFixed() { } bool ModelResetCommandFixed::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) { Q_UNUSED(srcParent); Q_UNUSED(srcStart); Q_UNUSED(srcEnd); Q_UNUSED(destParent); Q_UNUSED(destRow); m_model->beginResetModel(); return true; } void ModelResetCommandFixed::emitPostSignal() { m_model->endResetModel(); } ModelChangeChildrenLayoutsCommand::ModelChangeChildrenLayoutsCommand(DynamicTreeModel *model, QObject *parent) : ModelChangeCommand(model, parent) { } void ModelChangeChildrenLayoutsCommand::doCommand() { const QPersistentModelIndex parent1 = findIndex(m_rowNumbers); const QPersistentModelIndex parent2 = findIndex(m_secondRowNumbers); QList parents; parents << parent1; parents << parent2; emit m_model->layoutAboutToBeChanged(parents); int rowSize1 = -1; int rowSize2 = -1; for (int column = 0; column < m_numCols; ++column) { { QList &l = m_model->m_childItems[parent1.internalId()][column]; rowSize1 = l.size(); l.prepend(l.takeLast()); } { QList &l = m_model->m_childItems[parent2.internalId()][column]; rowSize2 = l.size(); l.append(l.takeFirst()); } } // If we're changing one of the parent indexes, we need to ensure that we do that before // changing any children of that parent. The reason is that we're keeping parent1 and parent2 // around as QPersistentModelIndex instances, and we query idx.parent() in the loop. QModelIndexList persistent = m_model->persistentIndexList(); foreach (const QModelIndex &parent, parents) { int idx = persistent.indexOf(parent); if (idx != -1) persistent.move(idx, 0); } foreach (const QModelIndex &idx, persistent) { if (idx.parent() == parent1) { if (idx.row() == rowSize1 - 1) { m_model->changePersistentIndex(idx, m_model->createIndex(0, idx.column(), idx.internalPointer())); } else { m_model->changePersistentIndex(idx, m_model->createIndex(idx.row() + 1, idx.column(), idx.internalPointer())); } } else if (idx.parent() == parent2) { if (idx.row() == 0) { m_model->changePersistentIndex(idx, m_model->createIndex(rowSize2 - 1, idx.column(), idx.internalPointer())); } else { m_model->changePersistentIndex(idx, m_model->createIndex(idx.row() - 1, idx.column(), idx.internalPointer())); } } } emit m_model->layoutChanged(parents); }