From 0166a00d9d4bcdb6c1c8387e857ad87b006db916 Mon Sep 17 00:00:00 2001 From: Bent Witthold Date: Thu, 11 Dec 2025 15:46:28 +0100 Subject: [PATCH] Adding new items to the model can now be made undone/redone. --- CMakeLists.txt | 1 + genericcore.cpp | 7 +++-- genericcore.h | 4 +-- model/commands/insertrowscommand.cpp | 33 +++++++++++++++++++++ model/commands/insertrowscommand.h | 28 ++++++++++++++++++ model/tablemodel.cpp | 44 +++++++++++++++++++++------- model/tablemodel.h | 15 ++++++++-- 7 files changed, 114 insertions(+), 18 deletions(-) create mode 100644 model/commands/insertrowscommand.cpp create mode 100644 model/commands/insertrowscommand.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b28f992..92cf602 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ add_library(${TARGET_APP} STATIC model/tablemodel.h model/tablemodel.cpp model/modelitem.h model/modelitem.cpp formats/jsonparser.h formats/jsonparser.cpp + model/commands/insertrowscommand.h model/commands/insertrowscommand.cpp ) include_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/genericcore.cpp b/genericcore.cpp index 5f6c506..4cf0500 100644 --- a/genericcore.cpp +++ b/genericcore.cpp @@ -19,7 +19,8 @@ using namespace std; GenericCore::GenericCore() { qDebug() << "Creating core..."; - m_modelUndoStack = make_shared(this); + // TODO let the model own its undo stack (& use TableModel::getUndoStack() if necessary) + m_modelUndoStack = new QUndoStack(this); setupModels(); } @@ -74,12 +75,12 @@ void GenericCore::triggerApplicationUpdate() { QProcess::startDetached(toolFilePath, args); } -std::shared_ptr GenericCore::getModUndoStack() const { return m_modelUndoStack; } +QUndoStack* GenericCore::getModelUndoStack() const { return m_modelUndoStack; } std::shared_ptr GenericCore::getModel() const { return m_mainModel; } void GenericCore::setupModels() { - m_mainModel = make_shared(this); + m_mainModel = make_shared(m_modelUndoStack, this); // TODO add QAbstractItemModelTester } diff --git a/genericcore.h b/genericcore.h index b8f194d..20ac805 100644 --- a/genericcore.h +++ b/genericcore.h @@ -22,14 +22,14 @@ class GenericCore : public QObject { bool isApplicationUpdateAvailable(); void triggerApplicationUpdate(); - std::shared_ptr getModUndoStack() const; + QUndoStack* getModelUndoStack() const; std::shared_ptr getModel() const; signals: void displayStatusMessage(QString message); private: - std::shared_ptr m_modelUndoStack; + QUndoStack* m_modelUndoStack; std::shared_ptr m_mainModel; void setupModels(); diff --git a/model/commands/insertrowscommand.cpp b/model/commands/insertrowscommand.cpp new file mode 100644 index 0000000..231ce29 --- /dev/null +++ b/model/commands/insertrowscommand.cpp @@ -0,0 +1,33 @@ +#include "insertrowscommand.h" + +#include + +#include "../tablemodel.h" + +InsertRowsCommand::InsertRowsCommand(TableModel* model, + int startRow, + QList > valueList, + QUndoCommand* parent) + : QUndoCommand(parent) + , m_tableModel(model) + , m_startRow(startRow) + , m_valueList(valueList) { + qInfo() << "New InsertCommand..."; + const QString commandText = + QString("inserting %1 item(s) on row %2").arg(valueList.length()).arg(startRow); + setText(commandText); +} + +void InsertRowsCommand::undo() { + qDebug() << "Undoing the InsertCommand..."; + if (m_tableModel) { + m_tableModel->execRemoveItems(m_startRow, m_valueList.length()); + } +} + +void InsertRowsCommand::redo() { + qDebug() << "(Re-)doing the InsertCommand..."; + if (m_tableModel) { + m_tableModel->execInsertItems(m_startRow, m_valueList); + } +} diff --git a/model/commands/insertrowscommand.h b/model/commands/insertrowscommand.h new file mode 100644 index 0000000..0341226 --- /dev/null +++ b/model/commands/insertrowscommand.h @@ -0,0 +1,28 @@ +#ifndef INSERTROWSCOMMAND_H +#define INSERTROWSCOMMAND_H + +#include + +class TableModel; + +class InsertRowsCommand : public QUndoCommand { + public: + // TODO don't use simple pointer to model + /// Using simple pointer to model because there was a crash when closing the application with an + /// unclean undo stack + InsertRowsCommand(TableModel* model, + int startRow, + QList > valueList, + QUndoCommand* parent = nullptr); + + /// QUndoCommand interface + void undo() override; + void redo() override; + + private: + TableModel* m_tableModel; + const int m_startRow; + const QList > m_valueList; +}; + +#endif // INSERTROWSCOMMAND_H diff --git a/model/tablemodel.cpp b/model/tablemodel.cpp index 7a05900..81f15da 100644 --- a/model/tablemodel.cpp +++ b/model/tablemodel.cpp @@ -1,6 +1,7 @@ #include "tablemodel.h" #include "../formats/jsonparser.h" +#include "commands/insertrowscommand.h" #include "modelitem.h" QHash TableModel::ROLE_NAMES = {{NameRole, "Name"}, @@ -9,8 +10,9 @@ QHash TableModel::ROLE_NAMES = {{NameRole, "Name"}, {AmountRole, "Amount"}, {FactorRole, "Factor"}}; -TableModel::TableModel(QObject* parent) - : QAbstractTableModel{parent} { +TableModel::TableModel(QUndoStack* undoStack, QObject* parent) + : QAbstractTableModel{parent} + , m_undoStack(undoStack) { for (int row = 0; row < 5; ++row) { QHash values; values[NameRole] = QString("Item %1").arg(row); @@ -56,6 +58,12 @@ QVariant TableModel::data(const QModelIndex& index, int role) const { case Qt::DisplayRole: case Qt::EditRole: return m_items.at(row)->data(roleForColumn); + case NameRole: + case DescriptionRole: + case InfoRole: + case AmountRole: + case FactorRole: + return m_items.at(row)->data(role); } return QVariant(); @@ -122,14 +130,9 @@ void TableModel::insertItems(int startPosition, } QList> valueList = JsonParser::toItemValuesList(jsonDoc); - const int nRows = valueList.size(); - beginInsertRows(QModelIndex(), startPosition, startPosition + nRows - 1); - for (int row = 0; row < nRows; ++row) { - const int rowPosition = startPosition + row; - shared_ptr item = make_unique(valueList.at(row)); - m_items.insert(rowPosition, std::move(item)); - } - endInsertRows(); + + InsertRowsCommand* insertCommand = new InsertRowsCommand(this, startPosition, valueList); + m_undoStack->push(insertCommand); } int TableModel::getRoleForColumn(const int column) const { @@ -154,3 +157,24 @@ int TableModel::getRoleForColumn(const int column) const { break; } } + +void TableModel::execInsertItems(const int firstRow, const QList> valueList) { + const int nRows = valueList.size(); + qDebug() << "Inserting" << nRows << "items..."; + + const int lastRow = firstRow + nRows - 1; + beginInsertRows(QModelIndex(), firstRow, lastRow); + for (int row = 0; row < nRows; ++row) { + const int rowPosition = firstRow + row; + shared_ptr item = make_unique(valueList.at(row)); + m_items.insert(rowPosition, std::move(item)); + } + endInsertRows(); +} + +void TableModel::execRemoveItems(const int firstRow, const int nRows) { + const int lastRow = firstRow + nRows - 1; + beginRemoveRows(QModelIndex(), firstRow, lastRow); + m_items.remove(firstRow, nRows); + endRemoveRows(); +} diff --git a/model/tablemodel.h b/model/tablemodel.h index 5778f52..d8e0455 100644 --- a/model/tablemodel.h +++ b/model/tablemodel.h @@ -3,6 +3,7 @@ #include +class QUndoStack; class ModelItem; using namespace std; @@ -10,11 +11,13 @@ using namespace std; class TableModel : public QAbstractTableModel { Q_OBJECT + friend class InsertRowsCommand; + public: enum UserRoles { NameRole = Qt::UserRole + 1, DescriptionRole, InfoRole, AmountRole, FactorRole }; static QHash ROLE_NAMES; - explicit TableModel(QObject* parent = nullptr); + explicit TableModel(QUndoStack* undoStack, QObject* parent = nullptr); /// QAbstractItemModel interface Qt::ItemFlags flags(const QModelIndex& index) const override; @@ -36,10 +39,16 @@ class TableModel : public QAbstractTableModel { void insertItems(int startPosition, const QByteArray& jsonDoc, const QModelIndex& parentIndex); private: - /// members + /// *** members *** QList> m_items; + QUndoStack* m_undoStack; - /// functions + /// *** functions *** + /// undo/redo functions + void execInsertItems(const int firstRow, const QList> valueList); + void execRemoveItems(const int firstRow, const int nRows); + + /// misc functions int getRoleForColumn(const int column) const; };