File indexing completed on 2024-04-28 07:44:52

0001 /*
0002     SPDX-FileCopyrightText: 2009 Stephen Kelly <steveire@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "dynamictreemodel.h"
0008 
0009 #include <QMimeData>
0010 
0011 #include <QDebug>
0012 
0013 #include <QStringList>
0014 
0015 // If DUMPTREE is defined, ModelInsertCommand dumps the tree of what it is inserting.
0016 // #define DUMPTREE
0017 #ifdef DUMPTREE
0018 #include <iostream>
0019 #endif
0020 
0021 DynamicTreeModel::DynamicTreeModel(QObject *parent)
0022     : QAbstractItemModel(parent)
0023     , nextId(1)
0024 {
0025 }
0026 
0027 QModelIndex DynamicTreeModel::index(int row, int column, const QModelIndex &parent) const
0028 {
0029     //   if (column != 0)
0030     //     return QModelIndex();
0031 
0032     if (column < 0 || row < 0) {
0033         return QModelIndex();
0034     }
0035 
0036     QList<QList<qint64>> childIdColumns = m_childItems.value(parent.internalId());
0037 
0038     const qint64 grandParent = findParentId(parent.internalId());
0039     if (grandParent >= 0) {
0040         QList<QList<qint64>> parentTable = m_childItems.value(grandParent);
0041         Q_ASSERT(parent.column() < parentTable.size());
0042         QList<qint64> parentSiblings = parentTable.at(parent.column());
0043         Q_ASSERT(parent.row() < parentSiblings.size());
0044     }
0045 
0046     if (childIdColumns.size() == 0) {
0047         return QModelIndex();
0048     }
0049 
0050     if (column >= childIdColumns.size()) {
0051         return QModelIndex();
0052     }
0053 
0054     QList<qint64> rowIds = childIdColumns.at(column);
0055 
0056     if (row >= rowIds.size()) {
0057         return QModelIndex();
0058     }
0059 
0060     qint64 id = rowIds.at(row);
0061 
0062     return createIndex(row, column, reinterpret_cast<void *>(id));
0063 }
0064 
0065 qint64 DynamicTreeModel::findParentId(qint64 searchId) const
0066 {
0067     if (searchId <= 0) {
0068         return -1;
0069     }
0070 
0071     QHashIterator<qint64, QList<QList<qint64>>> i(m_childItems);
0072     while (i.hasNext()) {
0073         i.next();
0074         QListIterator<QList<qint64>> j(i.value());
0075         while (j.hasNext()) {
0076             QList<qint64> l = j.next();
0077             if (l.contains(searchId)) {
0078                 return i.key();
0079             }
0080         }
0081     }
0082     return -1;
0083 }
0084 
0085 QModelIndex DynamicTreeModel::parent(const QModelIndex &index) const
0086 {
0087     if (!index.isValid()) {
0088         return QModelIndex();
0089     }
0090 
0091     qint64 searchId = index.internalId();
0092     qint64 parentId = findParentId(searchId);
0093     // Will never happen for valid index, but what the hey...
0094     if (parentId <= 0) {
0095         return QModelIndex();
0096     }
0097 
0098     qint64 grandParentId = findParentId(parentId);
0099     if (grandParentId < 0) {
0100         grandParentId = 0;
0101     }
0102 
0103     int column = 0;
0104     QList<qint64> childList = m_childItems.value(grandParentId).at(column);
0105 
0106     int row = childList.indexOf(parentId);
0107 
0108     return createIndex(row, column, reinterpret_cast<void *>(parentId));
0109 }
0110 
0111 int DynamicTreeModel::rowCount(const QModelIndex &index) const
0112 {
0113     QList<QList<qint64>> cols = m_childItems.value(index.internalId());
0114 
0115     if (cols.size() == 0) {
0116         return 0;
0117     }
0118 
0119     if (index.column() > 0) {
0120         return 0;
0121     }
0122 
0123     return cols.at(0).size();
0124 }
0125 
0126 int DynamicTreeModel::columnCount(const QModelIndex &index) const
0127 {
0128     //   Q_UNUSED(index);
0129     return m_childItems.value(index.internalId()).size();
0130 }
0131 
0132 QVariant DynamicTreeModel::data(const QModelIndex &index, int role) const
0133 {
0134     if (!index.isValid()) {
0135         return QVariant();
0136     }
0137 
0138     if (DynamicTreeModelId == role) {
0139         return index.internalId();
0140     }
0141 
0142     if (Qt::DisplayRole == role || Qt::EditRole == role) {
0143         return m_items.value(index.internalId());
0144     }
0145     return QVariant();
0146 }
0147 
0148 bool DynamicTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
0149 {
0150     if (role == Qt::EditRole) {
0151         m_items[index.internalId()] = value.toString();
0152         dataChanged(index, index);
0153         return true;
0154     }
0155 
0156     return QAbstractItemModel::setData(index, value, role);
0157 }
0158 
0159 void DynamicTreeModel::clear()
0160 {
0161     beginResetModel();
0162     m_items.clear();
0163     m_childItems.clear();
0164     nextId = 1;
0165     endResetModel();
0166 }
0167 
0168 bool DynamicTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int _column, const QModelIndex &parent)
0169 {
0170     Q_UNUSED(action);
0171     Q_UNUSED(_column);
0172     QByteArray encoded = data->data(mimeTypes().at(0));
0173 
0174     QHash<QModelIndex, QList<int>> movedItems;
0175     bool ok;
0176     qint64 id;
0177     int _row;
0178     static const int column = 0;
0179     QHash<qint64, QList<QList<qint64>>>::const_iterator it;
0180     const auto lst = encoded.split('\0');
0181     for (const QByteArray &ba : lst) {
0182         id = ba.toInt(&ok);
0183         if (!ok) {
0184             qDebug() << ba;
0185         }
0186         Q_ASSERT(ok);
0187 
0188         _row = -1;
0189         for (it = m_childItems.constBegin(); it != m_childItems.constEnd(); ++it) {
0190             _row = it.value().first().indexOf(id);
0191             if (_row < 0) {
0192                 continue;
0193             }
0194             movedItems[createIndex(_row, column, reinterpret_cast<void *>(id)).parent()].append(_row);
0195             break;
0196         }
0197         Q_ASSERT(_row >= 0);
0198         if (_row < 0) {
0199             return false;
0200         }
0201     }
0202 
0203     const int destRow = row < 0 ? 0 : row;
0204     const QList<int> destPath = indexToPath(parent);
0205 
0206     QList<int> srcPath;
0207     QModelIndex srcParent;
0208     QHash<QModelIndex, QList<int>>::iterator src_parent_it = movedItems.begin();
0209     int startRow = 0;
0210     int endRow = 0;
0211     int nextMovedRow = 0;
0212 
0213     QList<int> rowsMoved;
0214     QList<int>::iterator src_row_it;
0215     QList<int>::iterator rows_moved_end;
0216     QList<ModelMoveCommand *> moveCommands;
0217 
0218     for (; src_parent_it != movedItems.end(); ++src_parent_it) {
0219         srcParent = src_parent_it.key();
0220         srcPath = indexToPath(srcParent);
0221 
0222         rowsMoved = src_parent_it.value();
0223         std::sort(rowsMoved.begin(), rowsMoved.end());
0224         src_row_it = rowsMoved.begin();
0225         rows_moved_end = rowsMoved.end();
0226         startRow = *src_row_it;
0227         endRow = startRow;
0228         ++src_row_it;
0229 
0230         if (src_row_it == rows_moved_end) {
0231             moveCommands.prepend(getMoveCommand(srcPath, startRow, endRow));
0232             continue;
0233         }
0234 
0235         for (; src_row_it != rows_moved_end; ++src_row_it) {
0236             nextMovedRow = *src_row_it;
0237 
0238             if (nextMovedRow == endRow + 1) {
0239                 ++endRow;
0240             } else {
0241                 Q_ASSERT(nextMovedRow > endRow + 1);
0242                 moveCommands.prepend(getMoveCommand(srcPath, startRow, endRow));
0243                 startRow = nextMovedRow;
0244                 endRow = nextMovedRow;
0245 
0246                 if ((src_row_it + 1) == rows_moved_end) {
0247                     moveCommands.prepend(getMoveCommand(srcPath, startRow, endRow));
0248                 }
0249             }
0250         }
0251     }
0252 
0253     QPersistentModelIndex destParent = parent;
0254     QPersistentModelIndex destRowIndex = index(destRow, column, parent);
0255 
0256     ModelMoveCommand *firstCommand = moveCommands.takeFirst();
0257     firstCommand->setDestAncestors(indexToPath(parent));
0258     firstCommand->setDestRow(destRow);
0259     firstCommand->doCommand();
0260 
0261     if (!destRowIndex.isValid()) {
0262         destRowIndex = index(destRow, column, parent);
0263     }
0264 
0265     int offset = firstCommand->endRow() - firstCommand->startRow() + 1;
0266     for (ModelMoveCommand *moveCommand : std::as_const(moveCommands)) {
0267         moveCommand->setDestAncestors(indexToPath(destParent));
0268         moveCommand->setDestRow(destRowIndex.row() + offset);
0269         moveCommand->doCommand();
0270         offset = moveCommand->endRow() - moveCommand->startRow() + 1;
0271     }
0272 
0273     return false;
0274 }
0275 
0276 ModelMoveCommand *DynamicTreeModel::getMoveCommand(const QList<int> &srcPath, int startRow, int endRow)
0277 {
0278     ModelMoveCommand *moveCommand = new ModelMoveCommand(this, this);
0279     moveCommand->setAncestorRowNumbers(srcPath);
0280     moveCommand->setStartRow(startRow);
0281     moveCommand->setEndRow(endRow);
0282     return moveCommand;
0283 }
0284 
0285 Qt::ItemFlags DynamicTreeModel::flags(const QModelIndex &index) const
0286 {
0287     Qt::ItemFlags flags = QAbstractItemModel::flags(index);
0288     if (index.isValid()) {
0289         return flags | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
0290     }
0291     return flags;
0292 }
0293 
0294 Qt::DropActions DynamicTreeModel::supportedDropActions() const
0295 {
0296     return Qt::MoveAction;
0297 }
0298 
0299 QStringList DynamicTreeModel::mimeTypes() const
0300 {
0301     QStringList types;
0302     types << QStringLiteral("application/x-dynamictreemodel-itemlist");
0303     return types;
0304 }
0305 
0306 QMimeData *DynamicTreeModel::mimeData(const QModelIndexList &indexes) const
0307 {
0308     QMimeData *data = new QMimeData();
0309     QByteArray itemData;
0310     QModelIndexList::const_iterator it = indexes.begin();
0311     const QModelIndexList::const_iterator end = indexes.end();
0312     while (it != end) {
0313         itemData.append(QByteArray::number(it->internalId()));
0314         ++it;
0315         if (it != end) {
0316             itemData.append('\0');
0317         }
0318     }
0319     data->setData(mimeTypes().at(0), itemData);
0320     return data;
0321 }
0322 
0323 QList<int> DynamicTreeModel::indexToPath(const QModelIndex &_idx) const
0324 {
0325     QList<int> list;
0326     QModelIndex idx = _idx;
0327     while (idx.isValid()) {
0328         list.prepend(idx.row());
0329         idx = idx.parent();
0330     }
0331     return list;
0332 }
0333 
0334 QModelIndexList DynamicTreeModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
0335 {
0336     if (role != DynamicTreeModelId && role != Qt::DisplayRole) {
0337         return QAbstractItemModel::match(start, role, value, hits, flags);
0338     }
0339 
0340     qint64 id = value.toLongLong();
0341     if (role == Qt::DisplayRole) {
0342         id = m_items.key(value.toString());
0343     }
0344 
0345     QHash<qint64, QList<QList<qint64>>>::const_iterator it = m_childItems.constBegin();
0346     const QHash<qint64, QList<QList<qint64>>>::const_iterator end = m_childItems.constEnd();
0347 
0348     QList<QList<qint64>> items;
0349     QList<QList<qint64>>::const_iterator itemIt;
0350     QList<QList<qint64>>::const_iterator itemEnd;
0351     int foundIndexRow;
0352     for (; it != end; ++it) {
0353         items = it.value();
0354         itemEnd = items.constEnd();
0355         for (itemIt = items.constBegin(); itemIt != itemEnd; ++itemIt) {
0356             foundIndexRow = itemIt->indexOf(id);
0357             if (foundIndexRow != -1) {
0358                 static const int column = 0;
0359                 return QModelIndexList() << createIndex(foundIndexRow, column, reinterpret_cast<void *>(id));
0360             }
0361         }
0362     }
0363     return QModelIndexList();
0364 }
0365 
0366 ModelChangeCommand::ModelChangeCommand(DynamicTreeModel *model, QObject *parent)
0367     : QObject(parent)
0368     , m_model(model)
0369     , m_startRow(-1)
0370     , m_endRow(-1)
0371     , m_numCols(1)
0372 {
0373 }
0374 
0375 QModelIndex ModelChangeCommand::findIndex(const QList<int> &rows) const
0376 {
0377     const int col = 0;
0378     QModelIndex parent = QModelIndex();
0379     QListIterator<int> i(rows);
0380     while (i.hasNext()) {
0381         parent = m_model->index(i.next(), col, parent);
0382         Q_ASSERT(parent.isValid());
0383     }
0384     return parent;
0385 }
0386 
0387 ModelInsertCommand::ModelInsertCommand(DynamicTreeModel *model, QObject *parent)
0388     : ModelChangeCommand(model, parent)
0389 {
0390 }
0391 
0392 QList<ModelInsertCommand::Token> ModelInsertCommand::tokenize(const QString &treeString) const
0393 {
0394     QStringList parts = treeString.split(QStringLiteral("-"));
0395 
0396     QList<Token> tokens;
0397 
0398     const QStringList::const_iterator begin = parts.constBegin();
0399     const QStringList::const_iterator end = parts.constEnd();
0400 
0401     QStringList::const_iterator it = begin;
0402     ++it;
0403     for (; it != end; ++it) {
0404         Token token;
0405         if (it->trimmed().isEmpty()) {
0406             token.type = Token::Branch;
0407         } else {
0408             token.type = Token::Leaf;
0409             token.content = *it;
0410         }
0411         tokens.append(token);
0412     }
0413     return tokens;
0414 }
0415 
0416 void ModelInsertCommand::interpret(const QString &treeString)
0417 {
0418     m_treeString = treeString;
0419 
0420     const QList<int> depths = getDepths(m_treeString);
0421 
0422     const int size = std::count(depths.begin(), depths.end(), 0);
0423     Q_ASSERT(size != 0);
0424 
0425     m_endRow = m_startRow + size - 1;
0426 }
0427 
0428 QList<int> ModelInsertCommand::getDepths(const QString &treeString) const
0429 {
0430     int depth = 0;
0431     QList<int> depths;
0432 
0433 #ifdef DUMPTREE
0434     int id = 1;
0435 #endif
0436 
0437     QList<Token> tokens = tokenize(treeString);
0438     while (!tokens.isEmpty()) {
0439         Token token = tokens.takeFirst();
0440 
0441         if (token.type == Token::Branch) {
0442             ++depth;
0443             continue;
0444         }
0445         Q_ASSERT(token.type == Token::Leaf);
0446 
0447         depths.append(depth);
0448 #ifdef DUMPTREE
0449         std::cout << "\"";
0450         for (int i = 0; i <= depth; ++i) {
0451             std::cout << " -";
0452         }
0453         std::cout << " " << id++ << "\"" << std::endl;
0454 #endif
0455         depth = 0;
0456     }
0457 
0458     return depths;
0459 }
0460 
0461 void ModelInsertCommand::doCommand()
0462 {
0463     QModelIndex parent = findIndex(m_rowNumbers);
0464 
0465     if (!m_treeString.isEmpty()) {
0466         const QList<int> depths = getDepths(m_treeString);
0467         const int size = std::count(depths.begin(), depths.end(), 0);
0468         Q_ASSERT(size != 0);
0469         m_endRow = m_startRow + size - 1;
0470     }
0471     m_model->beginInsertRows(parent, m_startRow, m_endRow);
0472     if (!m_treeString.isEmpty()) {
0473         doInsertTree(parent);
0474     } else {
0475         qint64 parentId = parent.internalId();
0476 
0477         for (int row = m_startRow; row <= m_endRow; row++) {
0478             for (int col = 0; col < m_numCols; col++) {
0479                 if (m_model->m_childItems[parentId].size() <= col) {
0480                     m_model->m_childItems[parentId].append(QList<qint64>());
0481                 }
0482                 qint64 id = m_model->newId();
0483                 QString name = QString::number(id);
0484 
0485                 m_model->m_items.insert(id, name);
0486                 m_model->m_childItems[parentId][col].insert(row, id);
0487             }
0488         }
0489     }
0490     m_model->endInsertRows();
0491 }
0492 
0493 void ModelInsertCommand::doInsertTree(const QModelIndex &fragmentParent)
0494 {
0495     const QList<int> depths = getDepths(m_treeString);
0496 
0497     qint64 fragmentParentIdentifier = fragmentParent.internalId();
0498 
0499     QList<int>::const_iterator it = depths.constBegin();
0500     const QList<int>::const_iterator end = depths.constEnd();
0501 
0502     QList<qint64> recentParents;
0503     recentParents.append(fragmentParentIdentifier);
0504 
0505     qint64 lastId = 0;
0506     qint64 id;
0507     QString name;
0508     int depth = 0;
0509     int row = m_startRow;
0510     Q_ASSERT(*it == depth);
0511 
0512     QList<int> rows;
0513     rows.append(row);
0514 
0515     for (; it != end; ++it) {
0516         if (*it > depth) {
0517             Q_ASSERT(*it == depth + 1);
0518             fragmentParentIdentifier = lastId;
0519             if (recentParents.size() == *it) {
0520                 recentParents.append(fragmentParentIdentifier);
0521             } else {
0522                 recentParents[*it] = fragmentParentIdentifier;
0523             }
0524 
0525             ++depth;
0526 
0527         } else if (*it < depth) {
0528             fragmentParentIdentifier = recentParents.at(*it);
0529             depth = (*it);
0530         }
0531 
0532         if (rows.size() == depth) {
0533             rows.append(0);
0534         }
0535 
0536         id = m_model->newId();
0537         lastId = id;
0538         for (int column = 0; column < m_numCols; ++column) {
0539             QList<QList<qint64>> &children = m_model->m_childItems[fragmentParentIdentifier];
0540             if (children.size() <= column) {
0541                 children.append(QList<qint64>());
0542             }
0543             m_model->m_items.insert(id, QString::number(id));
0544             const int rowForDepth = rows[depth];
0545             if (rowForDepth >= children[column].size()) {
0546                 children[column].append(id);
0547             } else {
0548                 children[column].insert(rowForDepth, id);
0549             }
0550             if (column != m_numCols - 1) {
0551                 id = m_model->newId();
0552             }
0553         }
0554         rows[depth]++;
0555     }
0556 }
0557 
0558 ModelInsertAndRemoveQueuedCommand::ModelInsertAndRemoveQueuedCommand(DynamicTreeModel *model, QObject *parent)
0559     : ModelChangeCommand(model, parent)
0560 {
0561     qRegisterMetaType<QModelIndex>("QModelIndex");
0562 }
0563 
0564 void ModelInsertAndRemoveQueuedCommand::queuedBeginInsertRows(const QModelIndex &parent, int start, int end)
0565 {
0566     m_model->beginInsertRows(parent, start, end);
0567 }
0568 
0569 void ModelInsertAndRemoveQueuedCommand::queuedEndInsertRows()
0570 {
0571     m_model->endInsertRows();
0572 }
0573 
0574 void ModelInsertAndRemoveQueuedCommand::queuedBeginRemoveRows(const QModelIndex &parent, int start, int end)
0575 {
0576     m_model->beginRemoveRows(parent, start, end);
0577 }
0578 
0579 void ModelInsertAndRemoveQueuedCommand::queuedEndRemoveRows()
0580 {
0581     m_model->endRemoveRows();
0582 }
0583 
0584 void ModelInsertAndRemoveQueuedCommand::purgeItem(qint64 parent)
0585 {
0586     QList<QList<qint64>> childItemRows = m_model->m_childItems.value(parent);
0587 
0588     if (!childItemRows.isEmpty()) {
0589         for (int col = 0; col < m_numCols; col++) {
0590             const QList<qint64> childItems = childItemRows[col];
0591             for (qint64 item : childItems) {
0592                 purgeItem(item);
0593                 m_model->m_childItems[parent][col].removeOne(item);
0594             }
0595         }
0596     }
0597     m_model->m_items.remove(parent);
0598 }
0599 
0600 void ModelInsertAndRemoveQueuedCommand::doCommand()
0601 {
0602     QModelIndex parent = findIndex(m_rowNumbers);
0603 
0604     connect(this, &ModelInsertAndRemoveQueuedCommand::beginInsertRows, this, &ModelInsertAndRemoveQueuedCommand::queuedBeginInsertRows, Qt::QueuedConnection);
0605     connect(this, &ModelInsertAndRemoveQueuedCommand::endInsertRows, this, &ModelInsertAndRemoveQueuedCommand::queuedEndInsertRows, Qt::QueuedConnection);
0606     connect(this, &ModelInsertAndRemoveQueuedCommand::beginRemoveRows, this, &ModelInsertAndRemoveQueuedCommand::queuedBeginRemoveRows, Qt::QueuedConnection);
0607     connect(this, &ModelInsertAndRemoveQueuedCommand::endRemoveRows, this, &ModelInsertAndRemoveQueuedCommand::queuedEndRemoveRows, Qt::QueuedConnection);
0608 
0609     Q_EMIT beginInsertRows(parent, m_startRow, m_endRow);
0610     //   m_model->beginInsertRows(parent, m_startRow, m_endRow);
0611     qint64 parentId = parent.internalId();
0612     for (int row = m_startRow; row <= m_endRow; row++) {
0613         for (int col = 0; col < m_numCols; col++) {
0614             if (m_model->m_childItems[parentId].size() <= col) {
0615                 m_model->m_childItems[parentId].append(QList<qint64>());
0616             }
0617             qint64 id = m_model->newId();
0618             QString name = QString::number(id);
0619 
0620             m_model->m_items.insert(id, name);
0621             m_model->m_childItems[parentId][col].insert(row, id);
0622         }
0623     }
0624 
0625     Q_EMIT endInsertRows();
0626     //   m_model->endInsertRows();
0627 
0628     Q_EMIT beginRemoveRows(parent, m_startRow, m_endRow);
0629     //   m_model->beginRemoveRows(parent, m_startRow, m_endRow);
0630     for (int col = 0; col < m_numCols; col++) {
0631         QList<qint64> childItems = m_model->m_childItems.value(parentId).value(col);
0632         for (int row = m_startRow; row <= m_endRow; row++) {
0633             qint64 item = childItems[row];
0634             purgeItem(item);
0635             m_model->m_childItems[parentId][col].removeOne(item);
0636         }
0637     }
0638     Q_EMIT endRemoveRows();
0639     //   m_model->endRemoveRows();
0640 }
0641 
0642 ModelRemoveCommand::ModelRemoveCommand(DynamicTreeModel *model, QObject *parent)
0643     : ModelChangeCommand(model, parent)
0644 {
0645 }
0646 
0647 void ModelRemoveCommand::doCommand()
0648 {
0649     QModelIndex parent = findIndex(m_rowNumbers);
0650     m_model->beginRemoveRows(parent, m_startRow, m_endRow);
0651     qint64 parentId = parent.internalId();
0652     for (int col = 0; col < m_numCols; col++) {
0653         QList<qint64> childItems = m_model->m_childItems.value(parentId).value(col);
0654         for (int row = m_startRow; row <= m_endRow; row++) {
0655             qint64 item = childItems[row];
0656             purgeItem(item);
0657             m_model->m_childItems[parentId][col].removeOne(item);
0658         }
0659     }
0660     m_model->endRemoveRows();
0661 }
0662 
0663 void ModelRemoveCommand::purgeItem(qint64 parent)
0664 {
0665     const QList<QList<qint64>> childItemRows = m_model->m_childItems.value(parent);
0666 
0667     if (!childItemRows.isEmpty()) {
0668         for (int col = 0; col < m_numCols; col++) {
0669             const QList<qint64> childItems = childItemRows[col];
0670             for (qint64 item : childItems) {
0671                 purgeItem(item);
0672                 m_model->m_childItems[parent][col].removeOne(item);
0673             }
0674         }
0675     }
0676     m_model->m_items.remove(parent);
0677 }
0678 
0679 ModelDataChangeCommand::ModelDataChangeCommand(DynamicTreeModel *model, QObject *parent)
0680     : ModelChangeCommand(model, parent)
0681     , m_startColumn(0)
0682 {
0683 }
0684 
0685 void ModelDataChangeCommand::doCommand()
0686 {
0687     QModelIndex parent = findIndex(m_rowNumbers);
0688     QModelIndex topLeft = m_model->index(m_startRow, m_startColumn, parent);
0689     QModelIndex bottomRight = m_model->index(m_endRow, m_numCols - 1, parent);
0690 
0691     QList<QList<qint64>> childItems = m_model->m_childItems[parent.internalId()];
0692 
0693     for (int col = m_startColumn; col < m_startColumn + m_numCols; col++) {
0694         for (int row = m_startRow; row <= m_endRow; row++) {
0695             QString name = QString::number(m_model->newId());
0696             m_model->m_items[childItems[col][row]] = name;
0697         }
0698     }
0699     m_model->dataChanged(topLeft, bottomRight);
0700 }
0701 
0702 ModelMoveCommand::ModelMoveCommand(DynamicTreeModel *model, QObject *parent)
0703     : ModelChangeCommand(model, parent)
0704 {
0705 }
0706 
0707 bool ModelMoveCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow)
0708 {
0709     return m_model->beginMoveRows(srcParent, srcStart, srcEnd, destParent, destRow);
0710 }
0711 
0712 void ModelMoveCommand::doCommand()
0713 {
0714     QModelIndex srcParent = findIndex(m_rowNumbers);
0715     QModelIndex destParent = findIndex(m_destRowNumbers);
0716 
0717     if (!emitPreSignal(srcParent, m_startRow, m_endRow, destParent, m_destRow)) {
0718         return;
0719     }
0720 
0721     for (int column = 0; column < m_numCols; ++column) {
0722         const QList<qint64> l = m_model->m_childItems.value(srcParent.internalId())[column].mid(m_startRow, m_endRow - m_startRow + 1);
0723 
0724         for (int i = m_startRow; i <= m_endRow; i++) {
0725             m_model->m_childItems[srcParent.internalId()][column].removeAt(m_startRow);
0726         }
0727         int d;
0728         if (m_destRow < m_startRow) {
0729             d = m_destRow;
0730         } else {
0731             if (srcParent == destParent) {
0732                 d = m_destRow - (m_endRow - m_startRow + 1);
0733             } else {
0734                 d = m_destRow - (m_endRow - m_startRow);
0735             }
0736         }
0737 
0738         for (const qint64 id : l) {
0739             if (!m_model->m_childItems.contains(destParent.internalId())) {
0740                 m_model->m_childItems[destParent.internalId()].append(QList<qint64>());
0741             }
0742 
0743             m_model->m_childItems[destParent.internalId()][column].insert(d++, id);
0744         }
0745     }
0746 
0747     emitPostSignal();
0748 }
0749 
0750 void ModelMoveCommand::emitPostSignal()
0751 {
0752     m_model->endMoveRows();
0753 }
0754 
0755 ModelMoveLayoutChangeCommand::ModelMoveLayoutChangeCommand(DynamicTreeModel *model, QObject *parent)
0756     : ModelMoveCommand(model, parent)
0757 {
0758 }
0759 
0760 ModelMoveLayoutChangeCommand::~ModelMoveLayoutChangeCommand()
0761 {
0762 }
0763 
0764 bool ModelMoveLayoutChangeCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow)
0765 {
0766     m_model->layoutAboutToBeChanged();
0767 
0768     const int column = 0;
0769 
0770     for (int row = srcStart; row <= srcEnd; ++row) {
0771         m_beforeMoveList << m_model->index(row, column, srcParent);
0772     }
0773 
0774     if (srcParent != destParent) {
0775         for (int row = srcEnd + 1; row < m_model->rowCount(srcParent); ++row) {
0776             m_beforeMoveList << m_model->index(row, column, srcParent);
0777         }
0778         for (int row = destRow; row < m_model->rowCount(destParent); ++row) {
0779             m_beforeMoveList << m_model->index(row, column, destParent);
0780         }
0781     } else {
0782         if (destRow < srcStart) {
0783             for (int row = destRow; row < srcStart; ++row) {
0784                 m_beforeMoveList << m_model->index(row, column, srcParent);
0785             }
0786         } else {
0787             for (int row = srcStart + (srcEnd - srcStart + 1); row < destRow; ++row) {
0788                 m_beforeMoveList << m_model->index(row, column, srcParent);
0789             }
0790         }
0791     }
0792     // We assume that the move was legal here.
0793     return true;
0794 }
0795 
0796 void ModelMoveLayoutChangeCommand::emitPostSignal()
0797 {
0798     int srcStart = m_startRow;
0799     int srcEnd = m_endRow;
0800     int destRow = m_destRow;
0801 
0802     // Moving indexes may affect the m_rowNumbers and m_destRowNumbers.
0803     // Instead of adjusting them programmatically, the test writer must specify them if they change.
0804 
0805     const QList<int> sourceRowNumbers = m_endOfMoveSourceAncestors.isEmpty() ? m_rowNumbers : m_endOfMoveSourceAncestors;
0806     QModelIndex srcParent = findIndex(sourceRowNumbers);
0807 
0808     const QList<int> destRowNumbers = m_endOfMoveDestAncestors.isEmpty() ? m_destRowNumbers : m_endOfMoveDestAncestors;
0809     QModelIndex destParent = findIndex(destRowNumbers);
0810 
0811     const int column = 0;
0812 
0813     QModelIndexList afterMoveList;
0814 
0815     if (srcParent != destParent) {
0816         for (int row = destRow; row <= (destRow + (srcEnd - srcStart)); ++row) {
0817             afterMoveList << m_model->index(row, column, destParent);
0818         }
0819         for (int row = srcStart; row < m_model->rowCount(srcParent); ++row) {
0820             afterMoveList << m_model->index(row, column, srcParent);
0821         }
0822         for (int row = destRow + (srcEnd - srcStart + 1); row < m_model->rowCount(destParent); ++row) {
0823             afterMoveList << m_model->index(row, column, destParent);
0824         }
0825     } else {
0826         if (destRow < srcStart) {
0827             for (int row = srcStart; row <= srcEnd; ++row) {
0828                 afterMoveList << m_model->index(destRow + (srcStart - row), column, destParent);
0829             }
0830         } else {
0831             for (int row = srcStart; row <= srcEnd; ++row) {
0832                 afterMoveList << m_model->index(destRow + (srcStart - row - 1), column, destParent);
0833             }
0834         }
0835         if (destRow < srcStart) {
0836             for (int row = destRow + 1; row <= srcStart; ++row) {
0837                 afterMoveList << m_model->index(row, column, srcParent);
0838             }
0839         } else {
0840             for (int row = srcStart + (srcEnd - srcStart + 1); row < (srcStart + (destRow - srcEnd)); ++row) {
0841                 afterMoveList << m_model->index(row - (srcEnd - srcStart + 1), column, srcParent);
0842             }
0843         }
0844     }
0845 
0846     m_model->changePersistentIndexList(m_beforeMoveList, afterMoveList);
0847     m_beforeMoveList.clear();
0848     m_model->layoutChanged();
0849 }
0850 
0851 ModelResetCommand::ModelResetCommand(DynamicTreeModel *model, QObject *parent)
0852     : ModelChangeCommand(model, parent)
0853 {
0854 }
0855 
0856 ModelResetCommand::~ModelResetCommand()
0857 {
0858 }
0859 
0860 void ModelResetCommand::setInitialTree(const QString &treeString)
0861 {
0862     m_treeString = treeString;
0863 }
0864 
0865 void ModelResetCommand::doCommand()
0866 {
0867     m_model->beginResetModel();
0868     bool blocked = m_model->blockSignals(true);
0869     m_model->clear();
0870     if (!m_treeString.isEmpty()) {
0871         ModelInsertCommand ins(m_model);
0872         ins.setStartRow(0);
0873         ins.interpret(m_treeString);
0874         ins.doCommand();
0875     }
0876     m_model->blockSignals(blocked);
0877     m_model->endResetModel();
0878 }
0879 
0880 ModelLayoutChangeCommand::ModelLayoutChangeCommand(DynamicTreeModel *model, QObject *parent)
0881     : ModelChangeCommand(model, parent)
0882 {
0883 }
0884 
0885 ModelLayoutChangeCommand::~ModelLayoutChangeCommand()
0886 {
0887 }
0888 
0889 void ModelLayoutChangeCommand::setInitialTree(const QString &treeString)
0890 {
0891     m_treeString = treeString;
0892 }
0893 
0894 void ModelLayoutChangeCommand::setPersistentChanges(const QList<ModelLayoutChangeCommand::PersistentChange> &changes)
0895 {
0896     m_changes = changes;
0897 }
0898 
0899 void ModelLayoutChangeCommand::doCommand()
0900 {
0901     m_model->layoutAboutToBeChanged();
0902     QModelIndexList oldList;
0903 
0904     for (const PersistentChange &change : std::as_const(m_changes)) {
0905         const IndexFinder oldFinder(m_model, change.oldPath);
0906         oldList << oldFinder.getIndex();
0907     }
0908 
0909     bool blocked = m_model->blockSignals(true);
0910     m_model->clear();
0911     if (!m_treeString.isEmpty()) {
0912         ModelInsertCommand *ins = new ModelInsertCommand(m_model);
0913         ins->setStartRow(0);
0914         ins->interpret(m_treeString);
0915         ins->doCommand();
0916     }
0917 
0918     QModelIndexList newList;
0919     for (const PersistentChange &change : std::as_const(m_changes)) {
0920         const IndexFinder newFinder(m_model, change.newPath);
0921         newList << newFinder.getIndex();
0922     }
0923     m_model->changePersistentIndexList(oldList, newList);
0924 
0925     m_model->blockSignals(blocked);
0926     m_model->layoutChanged();
0927 }
0928 
0929 #include "moc_dynamictreemodel.cpp"