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"