Warning, file /utilities/toad/src/tasksmodel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 // SPDX-FileCopyrightText: 2022 Felipe Kinoshita <kinofhek@gmail.com>
0002 // SPDX-License-Identifier: LGPL-2.1-or-later
0003 
0004 #include "tasksmodel.h"
0005 
0006 #include <QStandardPaths>
0007 #include <QFile>
0008 #include <QDir>
0009 
0010 #include <QJsonArray>
0011 #include <QJsonDocument>
0012 #include <QJsonObject>
0013 
0014 TasksModel::TasksModel(QObject *parent)
0015     : QAbstractListModel(parent)
0016 {
0017     loadTasks();
0018 }
0019 
0020 QHash<int, QByteArray> TasksModel::roleNames() const
0021 {
0022     return {
0023         {Roles::TitleRole, QByteArrayLiteral("title")},
0024         {Roles::CheckedRole, QByteArrayLiteral("checked")}
0025     };
0026 }
0027 
0028 QVariant TasksModel::data(const QModelIndex &index, int role) const
0029 {
0030     if (!index.isValid() || index.row() < 0 || index.row() >= m_tasks.count()) {
0031         return {};
0032     }
0033 
0034     auto task = m_tasks.at(index.row());
0035 
0036     switch (role) {
0037     case Roles::TitleRole:
0038         return task.title();
0039     case Roles::CheckedRole:
0040         return task.checked();
0041     }
0042 
0043     return {};
0044 }
0045 
0046 int TasksModel::rowCount(const QModelIndex &parent) const
0047 {
0048     Q_UNUSED(parent)
0049     return m_tasks.size();
0050 }
0051 
0052 bool TasksModel::setData(const QModelIndex &index, const QVariant &value, int role)
0053 {
0054     if (index.row() < 0 || index.row() >= m_tasks.count()) {
0055         return false;
0056     }
0057 
0058     auto& task = m_tasks[index.row()];
0059 
0060     switch (role) {
0061         case Roles::CheckedRole:
0062             task.setChecked(value.toBool());
0063 
0064             if (task.checked()) {
0065                 m_completedTasks++;
0066                 Q_EMIT completedTasksChanged();
0067             }
0068 
0069             if (!task.checked()) {
0070                 if (m_completedTasks > 0) {
0071                     m_completedTasks--;
0072                     Q_EMIT completedTasksChanged();
0073                 }
0074             }
0075 
0076             break;
0077         case Roles::TitleRole:
0078             task.setTitle(value.toString());
0079             break;
0080     }
0081 
0082     emit dataChanged(index, index, { role });
0083 
0084     saveTasks();
0085 
0086     return true;
0087 }
0088 
0089 void TasksModel::add(const QString &title)
0090 {
0091     Task t(title, false);
0092 
0093     beginInsertRows(QModelIndex(), m_tasks.count(), m_tasks.count());
0094     m_tasks.append(t);
0095     endInsertRows();
0096 
0097     saveTasks();
0098 }
0099 
0100 void TasksModel::remove(const int &index)
0101 {
0102     if (index < 0 || index > m_tasks.count()) {
0103         return;
0104     }
0105 
0106     if (m_tasks[index].checked()) {
0107         if (m_completedTasks > 0) {
0108             m_completedTasks--;
0109             Q_EMIT completedTasksChanged();
0110         }
0111     }
0112 
0113     beginRemoveRows(QModelIndex(), index, index);
0114     m_tasks.removeAt(index);
0115     endRemoveRows();
0116 
0117     saveTasks();
0118 }
0119 
0120 void TasksModel::clear()
0121 {
0122     beginResetModel();
0123     m_tasks.clear();
0124     endResetModel();
0125 
0126     saveTasks();
0127 }
0128 
0129 bool TasksModel::saveTasks() const
0130 {
0131     const QString outputDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
0132 
0133     QFile outputFile(outputDir + QStringLiteral("/tasks.json"));
0134     if (!QDir(outputDir).mkpath(QStringLiteral("."))) {
0135         qDebug() << "Destdir doesn't exist and I can't create it: " << outputDir;
0136         return false;
0137     }
0138     if (!outputFile.open(QIODevice::WriteOnly)) {
0139         qDebug() << "Failed to write tabs to disk";
0140     }
0141 
0142     QJsonArray tasksArray;
0143     std::transform(m_tasks.cbegin(), m_tasks.cend(), std::back_inserter(tasksArray), [](const Task &task) {
0144         return task.toJson();
0145     });
0146 
0147     qDebug() << "Wrote to file" << outputFile.fileName() << "(" << tasksArray.count() << "tasks" << ")";
0148 
0149     const QJsonDocument document({
0150         {QLatin1String("tasks"), tasksArray},
0151         {QLatin1String("completedTasks"), m_completedTasks}
0152     });
0153 
0154     outputFile.write(document.toJson());
0155 
0156     return true;
0157 }
0158 
0159 bool TasksModel::loadTasks()
0160 {
0161     beginResetModel();
0162 
0163     const QString input = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/tasks.json");
0164 
0165     QFile inputFile(input);
0166     if (!inputFile.exists()) {
0167         return false;
0168     }
0169 
0170     if (!inputFile.open(QIODevice::ReadOnly)) {
0171         qDebug() << "Failed to load tabs from disk";
0172     }
0173 
0174     const auto tasksStorage = QJsonDocument::fromJson(inputFile.readAll()).object();
0175     m_tasks.clear();
0176 
0177     m_completedTasks = tasksStorage.value(QLatin1String("completedTasks")).toInt();
0178 
0179     const auto tasks = tasksStorage.value(QLatin1String("tasks")).toArray();
0180 
0181     std::transform(tasks.cbegin(), tasks.cend(), std::back_inserter(m_tasks), [](const QJsonValue &task) {
0182         return Task::fromJson(task.toObject());
0183     });
0184 
0185     qDebug() << "loaded from file:" << m_tasks.count() << input;
0186 
0187     endResetModel();
0188 
0189     return true;
0190 }