diff --git a/CMakeLists.txt b/CMakeLists.txt index d5492fe..ec6dc0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ add_library(${TARGET_APP} STATIC model/commands/insertrowscommand.h model/commands/insertrowscommand.cpp model/commands/removerowscommand.h model/commands/removerowscommand.cpp model/commands/edititemcommand.h model/commands/edititemcommand.cpp + data/filehandler.h data/filehandler.cpp ) include_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/data/filehandler.cpp b/data/filehandler.cpp new file mode 100644 index 0000000..5cd9bba --- /dev/null +++ b/data/filehandler.cpp @@ -0,0 +1,53 @@ +#include "filehandler.h" + +#include +#include +#include +#include + +bool FileHandler::saveToFile(const QJsonDocument& doc, const QString& fileName) { + qDebug() << "saving file..."; + QString path = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).at(0); + qDebug() << path; + + QDir dir; + if (!dir.exists(path)) { + dir.mkpath(path); + } + // qDebug() << path + fileName; + const QString filePath = path + '/' + fileName; + QFile file(filePath); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qWarning() << "can't open file"; + return false; + } + QTextStream out(&file); + out.setEncoding(QStringConverter::Utf8); + out << doc.toJson(QJsonDocument::Indented); + return true; +} + +QByteArray FileHandler::loadJSONDataFromFile(const QString fileName) { + QByteArray jsonData; + QFile file; + QString path = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).at(0); + file.setFileName(path + "/" + fileName); + if (file.exists()) { + qDebug() << "File found, reading content..."; + const bool successfulOpened = file.open(QIODevice::ReadOnly | QIODevice::Text); + if (successfulOpened) { + // TODO learn and decide on the differences between "readAll" and using + // streams + jsonData = file.readAll(); + file.close(); + } else { + qWarning() << "File could not be opened!"; + } + } else { + qInfo() << "File not found. Returning empty result..."; + } + return jsonData; +} + +FileHandler::FileHandler() {} diff --git a/data/filehandler.h b/data/filehandler.h new file mode 100644 index 0000000..aecf072 --- /dev/null +++ b/data/filehandler.h @@ -0,0 +1,17 @@ +#ifndef FILEHANDLER_H +#define FILEHANDLER_H + +class QJsonDocument; +class QString; +class QByteArray; + +class FileHandler { + public: + static bool saveToFile(const QJsonDocument& doc, const QString& fileName); + static QByteArray loadJSONDataFromFile(const QString fileName); + + private: + explicit FileHandler(); +}; + +#endif // FILEHANDLER_H diff --git a/formats/jsonparser.cpp b/formats/jsonparser.cpp index 5ef95cb..10ba77a 100644 --- a/formats/jsonparser.cpp +++ b/formats/jsonparser.cpp @@ -51,7 +51,7 @@ QJsonArray JsonParser::extractItemArray(const QByteArray& jsonData, const QStrin } else { QJsonObject rootObject = doc.object(); - itemArray = rootObject.value(QString("items")).toArray(); + itemArray = rootObject.value(objectName).toArray(); } return itemArray; diff --git a/genericcore.cpp b/genericcore.cpp index 4cf0500..3893b9c 100644 --- a/genericcore.cpp +++ b/genericcore.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -10,6 +11,7 @@ #include "../../ApplicationConfig.h" #include "CoreConfig.h" #include "constants.h" +#include "data/filehandler.h" #include "model/tablemodel.h" #include @@ -19,6 +21,15 @@ using namespace std; GenericCore::GenericCore() { qDebug() << "Creating core..."; + QCoreApplication::setOrganizationName("Working-Copy Collective"); + QCoreApplication::setOrganizationDomain("working-copy.org"); + +#ifdef QT_DEBUG + QCoreApplication::setApplicationName(QString(APPLICATION_NAME) + "-dev"); +#else + QCoreApplication::setApplicationName(QString(APPLICATION_NAME)); +#endif + // TODO let the model own its undo stack (& use TableModel::getUndoStack() if necessary) m_modelUndoStack = new QUndoStack(this); @@ -79,9 +90,51 @@ QUndoStack* GenericCore::getModelUndoStack() const { return m_modelUndoStack; } std::shared_ptr GenericCore::getModel() const { return m_mainModel; } +/** + * Save items to default file (in standard location). + * @brief GenericCore::saveItems Saves item fo file. + */ +void GenericCore::saveItems() { + qDebug() << "saving items..."; + + const QJsonDocument doc = m_mainModel->getAllItemsAsJsonDoc(); + const bool successfulSave = FileHandler::saveToFile(doc, "items.json"); + if (successfulSave) { + // QStringList completedTaskStrings = m_model->completedTasks(); + // appendCompletedTasksToFile(completedTaskStrings, "completed.txt"); + m_modelUndoStack->setClean(); + emit displayStatusMessage(QString("Items saved.")); + } else { + emit displayStatusMessage(QString("Error: Items couldn't be saved.")); + } +} + void GenericCore::setupModels() { m_mainModel = make_shared(m_modelUndoStack, this); // TODO add QAbstractItemModelTester + initModelData(); +} + +/** + * Initializing model with data. Tries to read items from default file. Generating example items as + * fallback. + * @brief GenericCore::initModelData + */ +void GenericCore::initModelData() { + qInfo() << "Trying to read model data from file..."; + const QByteArray jsonDoc = FileHandler::loadJSONDataFromFile("items.json"); + // qDebug() << "jsonDoc:" << jsonDoc; + // TODO decide on lack of file(s) (config, data) if example items should be generated + // (see welcome wizard) + if (jsonDoc.isEmpty()) { + qDebug() << "No item content in file. Generating example items..."; + const QByteArray exampleItems = m_mainModel->generateExampleItems(); + m_mainModel->insertItems(0, exampleItems); + } else { + qDebug() << "Item in file found."; + m_mainModel->insertItems(0, jsonDoc); + } + m_modelUndoStack->clear(); } QString GenericCore::getMaintenanceToolFilePath() const { diff --git a/genericcore.h b/genericcore.h index 20ac805..671ded1 100644 --- a/genericcore.h +++ b/genericcore.h @@ -25,6 +25,8 @@ class GenericCore : public QObject { QUndoStack* getModelUndoStack() const; std::shared_ptr getModel() const; + void saveItems(); + signals: void displayStatusMessage(QString message); @@ -33,6 +35,7 @@ class GenericCore : public QObject { std::shared_ptr m_mainModel; void setupModels(); + void initModelData(); QString getMaintenanceToolFilePath() const; }; diff --git a/model/modelitem.cpp b/model/modelitem.cpp index a168b7f..5df55b6 100644 --- a/model/modelitem.cpp +++ b/model/modelitem.cpp @@ -1,5 +1,10 @@ #include "modelitem.h" +#include "tablemodel.h" + +#include +#include + ModelItem::ModelItem(const QHash values) : m_values(values) {} @@ -38,3 +43,27 @@ bool ModelItem::setItemData(const QMap& changedValues) { return valueChanged; } + +QJsonObject ModelItem::toJsonObject() const { + QJsonObject itemObject; + // itemObject.insert("uuid", m_uuid.toString()); + // itemObject.insert("entryDateUTC", m_entryDateUTC.toString(Qt::ISODate)); + itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::NameRole), + data(TableModel::NameRole).toString()); + itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::DescriptionRole), + data(TableModel::DescriptionRole).toString()); + itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::InfoRole), + data(TableModel::InfoRole).toString()); + itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::AmountRole), + data(TableModel::AmountRole).toInt()); + itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::FactorRole), + data(TableModel::FactorRole).toReal()); + + // if (m_modifiedDateUTC.isValid()) { + // itemObject.insert("modifiedDateUTC", m_modifiedDateUTC.toString(Qt::ISODate)); + // } + // if (m_endDateUTC.isValid()) { + // itemObject.insert("endDateUTC", m_endDateUTC.toString(Qt::ISODate)); + // } + return itemObject; +} diff --git a/model/modelitem.h b/model/modelitem.h index 6f9c366..2f3e037 100644 --- a/model/modelitem.h +++ b/model/modelitem.h @@ -12,6 +12,9 @@ class ModelItem { // TODO change return value to list of changed roles bool setItemData(const QMap& changedValues); + // QString toString() const; + QJsonObject toJsonObject() const; + private: QHash m_values; }; diff --git a/model/tablemodel.cpp b/model/tablemodel.cpp index 675309a..ce706fa 100644 --- a/model/tablemodel.cpp +++ b/model/tablemodel.cpp @@ -6,6 +6,10 @@ #include "commands/removerowscommand.h" #include "modelitem.h" +#include +#include +#include + QHash TableModel::ROLE_NAMES = {{NameRole, "Name"}, {DescriptionRole, "Description"}, {InfoRole, "Info"}, @@ -13,21 +17,35 @@ QHash TableModel::ROLE_NAMES = {{NameRole, "Name"}, {FactorRole, "Factor"}}; QList TableModel::intColumns = {"Amount", "Factor"}; +QByteArray TableModel::generateExampleItems() { + QJsonDocument doc = QJsonDocument(); + QJsonObject rootObject; + QJsonArray array; + + for (int row = 0; row < 5; ++row) { + QJsonObject itemObject; + // itemObject.insert("uuid", m_uuid.toString()); + // itemObject.insert("entryDateUTC", m_entryDateUTC.toString(Qt::ISODate)); + itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::NameRole), + QString("Item %1").arg(row)); + itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::DescriptionRole), + QString("This is item %1").arg(row)); + itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::InfoRole), + QString("Info of item %1").arg(row)); + itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::AmountRole), row); + itemObject.insert(TableModel::ROLE_NAMES.value(TableModel::FactorRole), row * 1.1); + + array.append(itemObject); + } + rootObject.insert("items", array); + + doc.setObject(rootObject); + return doc.toJson(); +} + 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); - values[DescriptionRole] = QString("This is item %1").arg(row); - values[InfoRole] = QString("Info of item %1").arg(row); - values[AmountRole] = row; - values[FactorRole] = row * 1.1; - - shared_ptr item = make_unique(values); - m_items.append(std::move(item)); - } -} + , m_undoStack(undoStack) {} Qt::ItemFlags TableModel::flags(const QModelIndex& index) const { return Qt::ItemIsEditable | QAbstractTableModel::flags(index); @@ -111,6 +129,21 @@ bool TableModel::setItemData(const QModelIndex& index, const QMap return false; } +QJsonDocument TableModel::getAllItemsAsJsonDoc() const { + QJsonDocument doc = QJsonDocument(); + QJsonObject rootObject; + QJsonArray array; + + foreach (shared_ptr item, m_items) { + QJsonObject itemObject = item->toJsonObject(); + array.append(itemObject); + } + rootObject.insert("items", array); + + doc.setObject(rootObject); + return doc; +} + bool TableModel::removeRows(int firstRow, int nRows, const QModelIndex& parentIndex) { if (parentIndex != QModelIndex()) { qWarning() << "Removing of child rows is not supported yet!"; @@ -143,7 +176,7 @@ void TableModel::insertItems(int startPosition, startPosition = m_items.size(); } - QList> valueList = JsonParser::toItemValuesList(jsonDoc); + QList> valueList = JsonParser::toItemValuesList(jsonDoc, "items"); InsertRowsCommand* insertCommand = new InsertRowsCommand(this, startPosition, valueList); m_undoStack->push(insertCommand); diff --git a/model/tablemodel.h b/model/tablemodel.h index 069179e..2bb758a 100644 --- a/model/tablemodel.h +++ b/model/tablemodel.h @@ -19,6 +19,7 @@ class TableModel : public QAbstractTableModel { enum UserRoles { NameRole = Qt::UserRole + 1, DescriptionRole, InfoRole, AmountRole, FactorRole }; static QHash ROLE_NAMES; static QList intColumns; + static QByteArray generateExampleItems(); explicit TableModel(QUndoStack* undoStack, QObject* parent = nullptr); @@ -34,15 +35,20 @@ class TableModel : public QAbstractTableModel { bool setData(const QModelIndex& index, const QVariant& value, int role) override; bool setItemData(const QModelIndex& index, const QMap& roles) override; + QJsonDocument getAllItemsAsJsonDoc() const; + public slots: // bool insertRows(int position, int rows, const QModelIndex& parentIndex = QModelIndex()) // override; bool removeRows(int firstRow, int nRows, const QModelIndex& parentIndex = QModelIndex()) override; void appendItems(const QByteArray& jsonDoc); - void insertItems(int startPosition, const QByteArray& jsonDoc, const QModelIndex& parentIndex); + void insertItems(int startPosition, + const QByteArray& jsonDoc, + const QModelIndex& parentIndex = QModelIndex()); private: /// *** members *** + // TODO shared_ptr -> unique_ptr QList> m_items; QUndoStack* m_undoStack;