diff --git a/src/ui/FortFirewall.pro b/src/ui/FortFirewall.pro index 8738e2a5..97cfbe0c 100644 --- a/src/ui/FortFirewall.pro +++ b/src/ui/FortFirewall.pro @@ -27,6 +27,7 @@ SOURCES += \ form/controls/listview.cpp \ form/controls/spincombo.cpp \ form/controls/tabbar.cpp \ + form/controls/tableview.cpp \ form/controls/textarea2splitter.cpp \ form/controls/textarea2splitterhandle.cpp \ form/graph/axistickerspeed.cpp \ @@ -56,7 +57,6 @@ SOURCES += \ log/model/appblockedmodel.cpp \ log/model/appstatmodel.cpp \ log/model/iplistmodel.cpp \ - log/model/stringlistmodel.cpp \ log/model/traflistmodel.cpp \ main.cpp \ mainwindow.cpp \ @@ -67,6 +67,7 @@ SOURCES += \ task/taskinfo.cpp \ task/taskinfotasix.cpp \ task/taskinfoupdatechecker.cpp \ + task/tasklistmodel.cpp \ task/taskmanager.cpp \ task/tasktasix.cpp \ task/taskupdatechecker.cpp \ @@ -87,6 +88,8 @@ SOURCES += \ util/guiutil.cpp \ util/hotkeymanager.cpp \ util/logger.cpp \ + util/model/stringlistmodel.cpp \ + util/model/tableitemmodel.cpp \ util/nativeeventfilter.cpp \ util/net/hostinfo.cpp \ util/net/hostinfocache.cpp \ @@ -124,6 +127,7 @@ HEADERS += \ form/controls/listview.h \ form/controls/spincombo.h \ form/controls/tabbar.h \ + form/controls/tableview.h \ form/controls/textarea2splitter.h \ form/controls/textarea2splitterhandle.h \ form/graph/axistickerspeed.h \ @@ -153,7 +157,6 @@ HEADERS += \ log/model/appblockedmodel.h \ log/model/appstatmodel.h \ log/model/iplistmodel.h \ - log/model/stringlistmodel.h \ log/model/traflistmodel.h \ mainwindow.h \ stat/quotamanager.h \ @@ -163,6 +166,7 @@ HEADERS += \ task/taskinfo.h \ task/taskinfotasix.h \ task/taskinfoupdatechecker.h \ + task/tasklistmodel.h \ task/taskmanager.h \ task/tasktasix.h \ task/taskupdatechecker.h \ @@ -183,6 +187,8 @@ HEADERS += \ util/guiutil.h \ util/hotkeymanager.h \ util/logger.h \ + util/model/stringlistmodel.h \ + util/model/tableitemmodel.h \ util/nativeeventfilter.h \ util/net/hostinfo.h \ util/net/hostinfocache.h \ diff --git a/src/ui/form/controls/tableview.cpp b/src/ui/form/controls/tableview.cpp new file mode 100644 index 00000000..583893ab --- /dev/null +++ b/src/ui/form/controls/tableview.cpp @@ -0,0 +1,14 @@ +#include "tableview.h" + +TableView::TableView(QWidget *parent) : + QTableView(parent) +{ +} + +void TableView::currentChanged(const QModelIndex ¤t, + const QModelIndex &previous) +{ + QTableView::currentChanged(current, previous); + + emit currentIndexChanged(current); +} diff --git a/src/ui/form/controls/tableview.h b/src/ui/form/controls/tableview.h new file mode 100644 index 00000000..c92d9aa8 --- /dev/null +++ b/src/ui/form/controls/tableview.h @@ -0,0 +1,21 @@ +#ifndef TABLEVIEW_H +#define TABLEVIEW_H + +#include + +class TableView : public QTableView +{ + Q_OBJECT + +public: + explicit TableView(QWidget *parent = nullptr); + +signals: + void currentIndexChanged(const QModelIndex &index); + +protected: + void currentChanged(const QModelIndex ¤t, + const QModelIndex &previous) override; +}; + +#endif // TABLEVIEW_H diff --git a/src/ui/form/opt/pages/schedulepage.cpp b/src/ui/form/opt/pages/schedulepage.cpp index 90cf70f9..697fc030 100644 --- a/src/ui/form/opt/pages/schedulepage.cpp +++ b/src/ui/form/opt/pages/schedulepage.cpp @@ -1,7 +1,219 @@ #include "schedulepage.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../task/taskinfo.h" +#include "../../../task/tasklistmodel.h" +#include "../../../task/taskmanager.h" +#include "../../controls/checkspincombo.h" +#include "../../controls/controlutil.h" +#include "../../controls/tableview.h" +#include "../optionscontroller.h" + +namespace { + +const ValuesList taskIntervalHourValues = { + 3, 1, 6, 12, 24, 24 * 7, 24 * 30 +}; + +} + SchedulePage::SchedulePage(OptionsController *ctrl, QWidget *parent) : - BasePage(ctrl, parent) + BasePage(ctrl, parent), + m_taskListModel(new TaskListModel(taskManager(), this)) { + setupTaskListModel(); + + setupUi(); +} + +void SchedulePage::setScheduleEdited(bool v) +{ + if (m_scheduleEdited != v) { + m_scheduleEdited = v; + + if (scheduleEdited()) { + ctrl()->setOthersEdited(true); + } + } +} + +void SchedulePage::onEditResetted() +{ + setScheduleEdited(false); + + m_taskListModel->resetEdited(); +} + +void SchedulePage::onSaved() +{ + if (!scheduleEdited()) return; + + m_taskListModel->saveChanges(); +} + +void SchedulePage::onRetranslateUi() +{ + m_taskListModel->refresh(); + + m_btTaskRun->setText(tr("Run")); + m_btTaskAbort->setText(tr("Abort")); + + retranslateTaskDetails(); +} + +void SchedulePage::setupTaskListModel() +{ + connect(m_taskListModel, &TaskListModel::dataChanged, [&] { + setScheduleEdited(true); + }); +} + +void SchedulePage::retranslateTaskDetails() +{ + const QStringList list = { + tr("Custom"), tr("Hourly"), tr("Each 6 hours"), + tr("Each 12 hours"), tr("Daily"), tr("Weekly"), tr("Monthly") + }; + + m_lscTaskInterval->setNames(list); + m_lscTaskInterval->spinBox()->setSuffix(tr(" hours")); +} + +void SchedulePage::setupUi() +{ + auto layout = new QVBoxLayout(); + + // Tasks Table + setupTableTasks(); + setupTableTasksHeader(); + layout->addWidget(m_tableTasks, 1); + + // Task Details + setupTaskDetails(); + layout->addWidget(m_taskDetailsRow); + + // Actions on tasks table's current changed + setupTableTasksChanged(); + + this->setLayout(layout); +} + +void SchedulePage::setupTableTasks() +{ + m_tableTasks = new TableView(); + m_tableTasks->setSelectionMode(QAbstractItemView::SingleSelection); + m_tableTasks->setSelectionBehavior(QAbstractItemView::SelectRows); + + m_tableTasks->setModel(taskListModel()); +} + +void SchedulePage::setupTableTasksHeader() +{ + auto header = m_tableTasks->horizontalHeader(); + + header->setSectionResizeMode(0, QHeaderView::Fixed); + header->setSectionResizeMode(1, QHeaderView::Stretch); + header->setSectionResizeMode(2, QHeaderView::Stretch); + header->setSectionResizeMode(3, QHeaderView::Stretch); + + const auto refreshTableTasksHeader = [&] { + auto hh = m_tableTasks->horizontalHeader(); + hh->resizeSection(0, qRound(hh->width() * 0.45)); + }; + + refreshTableTasksHeader(); + + connect(header, &QHeaderView::geometriesChanged, this, refreshTableTasksHeader); +} + +void SchedulePage::setupTaskDetails() +{ + m_taskDetailsRow = new QWidget(); + + auto layout = new QHBoxLayout(); + layout->setMargin(0); + m_taskDetailsRow->setLayout(layout); + + setupTaskInterval(); + + m_btTaskRun = ControlUtil::createButton(":/images/run.png", [&] { + currentTaskInfo()->run(); + }); + m_btTaskAbort = ControlUtil::createButton(":/images/cancel.png", [&] { + currentTaskInfo()->abort(); + }); + + layout->addWidget(m_lscTaskInterval, 1); + layout->addWidget(m_btTaskRun); + layout->addWidget(m_btTaskAbort); +} + +void SchedulePage::setupTaskInterval() +{ + m_lscTaskInterval = new CheckSpinCombo(); + m_lscTaskInterval->checkBox()->setFont(ControlUtil::createFont(QFont::DemiBold)); + m_lscTaskInterval->spinBox()->setRange(1, 24 * 30 * 12); // ~Year + m_lscTaskInterval->setValues(taskIntervalHourValues); + + connect(m_lscTaskInterval->checkBox(), &QCheckBox::toggled, [&](bool checked) { + const int taskIndex = currentTaskIndex(); + const auto index = taskListModel()->index(taskIndex, 0); + + taskListModel()->setData(index, checked, TaskListModel::RoleEnabled); + }); + connect(m_lscTaskInterval->spinBox(), QOverload::of(&QSpinBox::valueChanged), [&](int value) { + const int taskIndex = currentTaskIndex(); + const auto index = taskListModel()->index(taskIndex, 1); + + taskListModel()->setData(index, value, TaskListModel::RoleIntervalHours); + }); +} + +void SchedulePage::setupTableTasksChanged() +{ + const auto refreshTableTasksChanged = [&] { + const int taskIndex = currentTaskIndex(); + const bool taskSelected = taskIndex >= 0; + + setCurrentTaskInfo(taskSelected + ? taskListModel()->taskInfoAt(taskIndex) + : nullptr); + m_taskDetailsRow->setVisible(taskSelected); + + if (taskSelected) { + const auto index = taskListModel()->index(taskIndex, 0); + + m_lscTaskInterval->checkBox()->setChecked( + taskListModel()->data(index, TaskListModel::RoleEnabled).toBool()); + m_lscTaskInterval->checkBox()->setText( + taskListModel()->data(index).toString()); + m_lscTaskInterval->spinBox()->setValue( + taskListModel()->data(index, TaskListModel::RoleIntervalHours).toInt()); + + const bool running = currentTaskInfo()->running(); + m_btTaskRun->setEnabled(!running); + m_btTaskAbort->setEnabled(running); + } + }; + + refreshTableTasksChanged(); + + connect(m_tableTasks, &TableView::currentIndexChanged, this, refreshTableTasksChanged); + connect(taskListModel(), &TaskListModel::dataChanged, this, refreshTableTasksChanged); + connect(taskManager(), &TaskManager::taskStarted, this, refreshTableTasksChanged); + connect(taskManager(), &TaskManager::taskFinished, this, refreshTableTasksChanged); +} + +int SchedulePage::currentTaskIndex() const +{ + return m_tableTasks->currentIndex().row(); } diff --git a/src/ui/form/opt/pages/schedulepage.h b/src/ui/form/opt/pages/schedulepage.h index e94b74e6..cf71ee2a 100644 --- a/src/ui/form/opt/pages/schedulepage.h +++ b/src/ui/form/opt/pages/schedulepage.h @@ -3,6 +3,13 @@ #include "basepage.h" +QT_FORWARD_DECLARE_CLASS(QTableView) + +QT_FORWARD_DECLARE_CLASS(CheckSpinCombo) +QT_FORWARD_DECLARE_CLASS(TableView) +QT_FORWARD_DECLARE_CLASS(TaskInfo) +QT_FORWARD_DECLARE_CLASS(TaskListModel) + class SchedulePage : public BasePage { Q_OBJECT @@ -11,8 +18,45 @@ public: explicit SchedulePage(OptionsController *ctrl = nullptr, QWidget *parent = nullptr); -signals: + TaskListModel *taskListModel() const { return m_taskListModel; } +protected slots: + void onEditResetted() override; + void onSaved() override; + + void onRetranslateUi() override; + +private: + bool scheduleEdited() const { return m_scheduleEdited; } + void setScheduleEdited(bool v); + + void retranslateTaskDetails(); + + void setupTaskListModel(); + + void setupUi(); + void setupTableTasks(); + void setupTableTasksHeader(); + void setupTaskDetails(); + void setupTaskInterval(); + void setupTableTasksChanged(); + + int currentTaskIndex() const; + + TaskInfo *currentTaskInfo() const { return m_taskInfo; } + void setCurrentTaskInfo(TaskInfo *v) { m_taskInfo = v; } + +private: + bool m_scheduleEdited = false; + + TaskListModel *m_taskListModel = nullptr; + TaskInfo *m_taskInfo = nullptr; + + TableView *m_tableTasks = nullptr; + QWidget *m_taskDetailsRow = nullptr; + CheckSpinCombo *m_lscTaskInterval = nullptr; + QPushButton *m_btTaskRun = nullptr; + QPushButton *m_btTaskAbort = nullptr; }; #endif // SCHEDULEPAGE_H diff --git a/src/ui/form/opt/pages/statisticspage.cpp b/src/ui/form/opt/pages/statisticspage.cpp index cd2381bc..dbd7f1bf 100644 --- a/src/ui/form/opt/pages/statisticspage.cpp +++ b/src/ui/form/opt/pages/statisticspage.cpp @@ -734,7 +734,7 @@ void StatisticsPage::setupAppListViewChanged() refreshAppListViewChanged(); - connect(m_appListView, &ListView::currentIndexChanged, this , refreshAppListViewChanged); + connect(m_appListView, &ListView::currentIndexChanged, this, refreshAppListViewChanged); } void StatisticsPage::updatePage() diff --git a/src/ui/fortmanager.cpp b/src/ui/fortmanager.cpp index b50c4d64..a8f28bc4 100644 --- a/src/ui/fortmanager.cpp +++ b/src/ui/fortmanager.cpp @@ -193,7 +193,7 @@ void FortManager::setupLogger() void FortManager::setupTaskManager() { - m_taskManager->loadSettings(settings(), m_confManager); + m_taskManager->loadSettings(); } void FortManager::setupTranslationManager() diff --git a/src/ui/log/model/appblockedmodel.h b/src/ui/log/model/appblockedmodel.h index ab73538d..ff928cd2 100644 --- a/src/ui/log/model/appblockedmodel.h +++ b/src/ui/log/model/appblockedmodel.h @@ -4,7 +4,7 @@ #include #include -#include "stringlistmodel.h" +#include "../../util/model/stringlistmodel.h" QT_FORWARD_DECLARE_CLASS(IpListModel) QT_FORWARD_DECLARE_CLASS(LogEntryBlocked) diff --git a/src/ui/log/model/appstatmodel.h b/src/ui/log/model/appstatmodel.h index fb8bd5d5..609a6150 100644 --- a/src/ui/log/model/appstatmodel.h +++ b/src/ui/log/model/appstatmodel.h @@ -1,7 +1,7 @@ #ifndef APPSTATMODEL_H #define APPSTATMODEL_H -#include "stringlistmodel.h" +#include "../../util/model/stringlistmodel.h" QT_FORWARD_DECLARE_CLASS(AppInfoCache) QT_FORWARD_DECLARE_CLASS(LogEntryProcNew) diff --git a/src/ui/log/model/iplistmodel.h b/src/ui/log/model/iplistmodel.h index 8bc8538b..3a8192ae 100644 --- a/src/ui/log/model/iplistmodel.h +++ b/src/ui/log/model/iplistmodel.h @@ -1,7 +1,7 @@ #ifndef IPLISTMODEL_H #define IPLISTMODEL_H -#include "stringlistmodel.h" +#include "../../util/model/stringlistmodel.h" class IpListModel : public StringListModel { diff --git a/src/ui/log/model/traflistmodel.cpp b/src/ui/log/model/traflistmodel.cpp index 8259a645..9955b5b2 100644 --- a/src/ui/log/model/traflistmodel.cpp +++ b/src/ui/log/model/traflistmodel.cpp @@ -9,14 +9,7 @@ TrafListModel::TrafListModel(StatManager *statManager, QObject *parent) : - QAbstractItemModel(parent), - m_isEmpty(false), - m_unit(UnitAdaptive), - m_type(TrafHourly), - m_appId(0), - m_minTrafTime(0), - m_maxTrafTime(0), - m_trafCount(0), + TableItemModel(parent), m_statManager(statManager) { } @@ -36,28 +29,6 @@ void TrafListModel::setAppId(qint64 appId) m_appId = appId; } -QModelIndex TrafListModel::index(int row, int column, - const QModelIndex &parent) const -{ - return hasIndex(row, column, parent) - ? createIndex(row, column) : QModelIndex(); -} - -QModelIndex TrafListModel::parent(const QModelIndex &child) const -{ - Q_UNUSED(child) - - return {}; -} - -QModelIndex TrafListModel::sibling(int row, int column, - const QModelIndex &index) const -{ - Q_UNUSED(index) - - return this->index(row, column); -} - int TrafListModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) @@ -70,11 +41,6 @@ int TrafListModel::columnCount(const QModelIndex &parent) const return parent.isValid() ? 0 : 4; } -bool TrafListModel::hasChildren(const QModelIndex &parent) const -{ - return !parent.isValid() && rowCount() > 0; -} - QVariant TrafListModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal @@ -113,12 +79,6 @@ QVariant TrafListModel::data(const QModelIndex &index, int role) const return QVariant(); } -Qt::ItemFlags TrafListModel::flags(const QModelIndex &index) const -{ - return QAbstractItemModel::flags(index) - | (index.isValid() ? Qt::ItemNeverHasChildren : Qt::NoItemFlags); -} - void TrafListModel::clear() { m_statManager->clear(); diff --git a/src/ui/log/model/traflistmodel.h b/src/ui/log/model/traflistmodel.h index 79f51c86..c2ab1b82 100644 --- a/src/ui/log/model/traflistmodel.h +++ b/src/ui/log/model/traflistmodel.h @@ -1,7 +1,7 @@ #ifndef TRAFLISTMODEL_H #define TRAFLISTMODEL_H -#include +#include "../util/model/tableitemmodel.h" QT_FORWARD_DECLARE_CLASS(StatManager) @@ -15,7 +15,7 @@ struct TrafficRow { qint64 outBytes = 0; }; -class TrafListModel : public QAbstractItemModel +class TrafListModel : public TableItemModel { Q_OBJECT @@ -50,22 +50,13 @@ public: qint64 appId() const { return m_appId; } void setAppId(qint64 appId); - QModelIndex index(int row, int column, - const QModelIndex &parent = QModelIndex()) const override; - QModelIndex parent(const QModelIndex &child) const override; - - QModelIndex sibling(int row, int column, - const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; - bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - Qt::ItemFlags flags(const QModelIndex &index) const override; - public slots: void clear(); @@ -91,20 +82,20 @@ private: static const char *getSqlSelectTraffic(TrafType type, qint64 appId); private: - bool m_isEmpty; + bool m_isEmpty = false; - TrafUnit m_unit; - TrafType m_type; + TrafUnit m_unit = UnitAdaptive; + TrafType m_type = TrafHourly; - qint64 m_appId; + qint64 m_appId = 0; - qint32 m_minTrafTime; - qint32 m_maxTrafTime; - qint32 m_trafCount; + qint32 m_minTrafTime = 0; + qint32 m_maxTrafTime = 0; + qint32 m_trafCount = 0; + + StatManager *m_statManager = nullptr; mutable TrafficRow m_rowCache; - - StatManager *m_statManager; }; #endif // TRAFLISTMODEL_H diff --git a/src/ui/task/taskinfo.cpp b/src/ui/task/taskinfo.cpp index 8d22f932..7bf7dd00 100644 --- a/src/ui/task/taskinfo.cpp +++ b/src/ui/task/taskinfo.cpp @@ -12,6 +12,7 @@ TaskInfo::TaskInfo(TaskType type, QObject *parent) : QObject(parent), m_enabled(false), + m_running(false), m_aborted(false), m_intervalHours(24), m_type(type), @@ -33,6 +34,14 @@ void TaskInfo::setEnabled(bool enabled) } } +void TaskInfo::setRunning(bool running) +{ + if (m_running != running) { + m_running = running; + emit enabledChanged(); + } +} + void TaskInfo::setIntervalHours(int intervalHours) { if (m_intervalHours != intervalHours) { @@ -159,6 +168,9 @@ void TaskInfo::run() setTaskWorker(taskWorker); + setRunning(true); + emit workStarted(); + taskWorker->run(); } @@ -185,6 +197,7 @@ void TaskInfo::handleFinished(bool success) setLastSuccess(lastRun()); } + setRunning(false); emit workFinished(success); abort(); diff --git a/src/ui/task/taskinfo.h b/src/ui/task/taskinfo.h index ce7a7c7e..7d713a7e 100644 --- a/src/ui/task/taskinfo.h +++ b/src/ui/task/taskinfo.h @@ -37,6 +37,9 @@ public: bool enabled() const { return m_enabled; } void setEnabled(bool enabled); + bool running() const { return m_running; } + void setRunning(bool running); + int intervalHours() const { return m_intervalHours; } void setIntervalHours(int intervalHours); @@ -62,8 +65,6 @@ public: TaskWorker *taskWorker() const { return m_taskWorker; } void setTaskWorker(TaskWorker *taskWorker); - bool running() const { return m_taskWorker != nullptr; } - void rawData(QByteArray &data) const; void setRawData(const QByteArray &data); @@ -72,12 +73,14 @@ public: signals: void enabledChanged(); + void runningChanged(); void intervalHoursChanged(); void typeChanged(); void lastRunChanged(); void lastSuccessChanged(); void taskWorkerChanged(); + void workStarted(); void workFinished(bool success); public slots: @@ -94,6 +97,7 @@ private: private: quint8 m_enabled : 1; + quint8 m_running : 1; quint8 m_aborted : 1; // transient quint16 m_intervalHours; diff --git a/src/ui/task/tasklistmodel.cpp b/src/ui/task/tasklistmodel.cpp new file mode 100644 index 00000000..31c34729 --- /dev/null +++ b/src/ui/task/tasklistmodel.cpp @@ -0,0 +1,245 @@ +#include "tasklistmodel.h" + +#include "taskinfo.h" +#include "taskmanager.h" + +TaskListModel::TaskListModel(TaskManager *taskManager, + QObject *parent) : + TableItemModel(parent), + m_taskManager(taskManager) +{ + setupTaskRows(); + + connect(m_taskManager, &TaskManager::taskFinished, this, &TaskListModel::refresh); +} + +TaskListModel::~TaskListModel() +{ + clearTaskRows(); +} + +const QList &TaskListModel::taskInfosList() const +{ + return taskManager()->taskInfosList(); +} + +int TaskListModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return taskInfosList().size(); +} + +int TaskListModel::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : 4; +} + +QVariant TaskListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal + && role == Qt::DisplayRole) { + switch (section) { + case 0: return tr("Name"); + case 1: return tr("Interval, hours"); + case 2: return tr("Last Run"); + case 3: return tr("Last Success"); + } + } + return QVariant(); +} + +QVariant TaskListModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + switch (role) { + case Qt::DisplayRole: + { + const int row = index.row(); + const int column = index.column(); + + const auto taskInfo = taskInfoAt(row); + + switch (column) { + case 0: return taskInfo->title(); + case 1: return taskIntervalHours(row); + case 2: return formatDateTime(taskInfo->lastRun()); + case 3: return formatDateTime(taskInfo->lastSuccess()); + } + } + break; + + case Qt::CheckStateRole: + case RoleEnabled: + if (index.column() == 0) { + return taskEnabled(index.row()); + } + break; + + case RoleIntervalHours: + return taskIntervalHours(index.row()); + + case RoleRunning: { + const auto taskInfo = taskInfoAt(index.row()); + return taskInfo->running(); + } + } + + return QVariant(); +} + +bool TaskListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + Q_UNUSED(value) + + if (!index.isValid()) + return false; + + switch (role) { + case Qt::CheckStateRole: + setTaskEnabled(index, !taskEnabled(index.row())); + return true; + + case RoleEnabled: + setTaskEnabled(index, value.toBool()); + return true; + + case RoleIntervalHours: + setTaskIntervalHours(index, value.toInt()); + return true; + } + + return false; +} + +Qt::ItemFlags TaskListModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + const int column = index.column(); + + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren + | (column == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags); +} + +void TaskListModel::resetEdited() +{ + beginResetModel(); + setupTaskRows(); + endResetModel(); +} + +void TaskListModel::saveChanges() +{ + const int taskCount = taskInfosList().size(); + for (int i = 0; i < taskCount; ++i) { + const auto taskRow = taskRowAt(i); + if (!taskRow) continue; + + auto taskInfo = taskInfoAt(i); + taskInfo->setEnabled(taskRow->enabled); + taskInfo->setIntervalHours(taskRow->intervalHours); + } + + taskManager()->saveSettings(); +} + +void TaskListModel::setupTaskRows() +{ + clearTaskRows(); + + const int taskCount = taskInfosList().size(); + for (int i = 0; i < taskCount; ++i) { + m_taskRows.append(nullptr); + } +} + +void TaskListModel::clearTaskRows() +{ + qDeleteAll(m_taskRows); + m_taskRows.clear(); +} + +bool TaskListModel::taskEnabled(int index) const +{ + const auto taskRow = taskRowAt(index); + if (taskRow) + return taskRow->enabled; + + const auto taskInfo = taskInfoAt(index); + return taskInfo->enabled(); +} + +void TaskListModel::setTaskEnabled(const QModelIndex &index, bool v) +{ + const int row = index.row(); + + auto taskRow = taskRowAt(row); + if (!taskRow) { + taskRow = addTaskRow(row); + } + + if (taskRow->enabled == v) + return; + + taskRow->enabled = v; + + emit dataChanged(index, index, {Qt::CheckStateRole}); +} + +int TaskListModel::taskIntervalHours(int index) const +{ + const auto taskRow = taskRowAt(index); + if (taskRow) + return taskRow->intervalHours; + + const auto taskInfo = taskInfoAt(index); + return taskInfo->intervalHours(); +} + +void TaskListModel::setTaskIntervalHours(const QModelIndex &index, int v) +{ + const int row = index.row(); + + auto taskRow = taskRowAt(row); + if (!taskRow) { + taskRow = addTaskRow(row); + } + + if (taskRow->intervalHours == v) + return; + + taskRow->intervalHours = v; + + emit dataChanged(index, index, {Qt::DisplayRole}); +} + +TaskRow *TaskListModel::addTaskRow(int index) +{ + auto taskRow = new TaskRow(); + m_taskRows.replace(index, taskRow); + + auto taskInfo = taskInfoAt(index); + taskRow->enabled = taskInfo->enabled(); + taskRow->intervalHours = taskInfo->intervalHours(); + + return taskRow; +} + +TaskInfo *TaskListModel::taskInfoAt(int index) const +{ + return taskInfosList().at(index); +} + +TaskRow *TaskListModel::taskRowAt(int index) const +{ + return m_taskRows.at(index); +} + +QString TaskListModel::formatDateTime(const QDateTime &dateTime) +{ + return dateTime.toString("yyyy-MM-dd HH:mm:ss"); +} diff --git a/src/ui/task/tasklistmodel.h b/src/ui/task/tasklistmodel.h new file mode 100644 index 00000000..6fb4c191 --- /dev/null +++ b/src/ui/task/tasklistmodel.h @@ -0,0 +1,74 @@ +#ifndef TASKLISTMODEL_H +#define TASKLISTMODEL_H + +#include + +#include "../util/model/tableitemmodel.h" + +QT_FORWARD_DECLARE_CLASS(TaskInfo) +QT_FORWARD_DECLARE_CLASS(TaskManager) + +struct TaskRow { + bool enabled = false; + int intervalHours = 0; +}; + +class TaskListModel : public TableItemModel +{ + Q_OBJECT + +public: + enum Roles { + RoleEnabled = Qt::UserRole, + RoleIntervalHours, + RoleRunning + }; + Q_ENUM(Roles) + + explicit TaskListModel(TaskManager *taskManager, + QObject *parent = nullptr); + ~TaskListModel() override; + + TaskManager *taskManager() const { return m_taskManager; } + + const QList &taskInfosList() const; + TaskInfo *taskInfoAt(int index) const; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + +public slots: + void resetEdited(); + + void saveChanges(); + +private: + void setupTaskRows(); + void clearTaskRows(); + + bool taskEnabled(int index) const; + void setTaskEnabled(const QModelIndex &index, bool v); + + int taskIntervalHours(int index) const; + void setTaskIntervalHours(const QModelIndex &index, int v); + + TaskRow *addTaskRow(int index); + + TaskRow *taskRowAt(int index) const; + + static QString formatDateTime(const QDateTime &dateTime); + +private: + TaskManager *m_taskManager = nullptr; + + mutable QList m_taskRows; +}; + +#endif // TASKLISTMODEL_H diff --git a/src/ui/task/taskmanager.cpp b/src/ui/task/taskmanager.cpp index f695f505..f59c4568 100644 --- a/src/ui/task/taskmanager.cpp +++ b/src/ui/task/taskmanager.cpp @@ -6,7 +6,6 @@ #include "../util/dateutil.h" #include "taskinfotasix.h" #include "taskinfoupdatechecker.h" -#include "taskworker.h" TaskManager::TaskManager(FortManager *fortManager, QObject *parent) : @@ -18,8 +17,17 @@ TaskManager::TaskManager(FortManager *fortManager, m_timer.setSingleShot(true); - connect(&m_timer, &QTimer::timeout, - this, &TaskManager::runExpiredTasks); + connect(&m_timer, &QTimer::timeout, this, &TaskManager::runExpiredTasks); +} + +FortSettings *TaskManager::settings() const +{ + return fortManager()->settings(); +} + +ConfManager *TaskManager::confManager() const +{ + return fortManager()->confManager(); } void TaskManager::setupTasks() @@ -32,17 +40,16 @@ void TaskManager::setupTasks() void TaskManager::appendTaskInfo(TaskInfo *taskInfo) { - connect(taskInfo, &TaskInfo::workFinished, - this, &TaskManager::handleTaskFinished); + connect(taskInfo, &TaskInfo::workStarted, this, &TaskManager::handleTaskStarted); + connect(taskInfo, &TaskInfo::workFinished, this, &TaskManager::handleTaskFinished); m_taskInfos.append(taskInfo); } -void TaskManager::loadSettings(const FortSettings *fortSettings, - ConfManager *confManager) +void TaskManager::loadSettings() { - if (!confManager->loadTasks(m_taskInfos)) { - const TasksMap tasksMap = fortSettings->tasks(); + if (!confManager()->loadTasks(m_taskInfos)) { + const TasksMap tasksMap = settings()->tasks(); if (!tasksMap.isEmpty()) { for (TaskInfo *taskInfo : m_taskInfos) { const QByteArray taskData = tasksMap.value(taskInfo->name()); @@ -56,26 +63,33 @@ void TaskManager::loadSettings(const FortSettings *fortSettings, runExpiredTasks(); } -bool TaskManager::saveSettings(FortSettings *fortSettings, - ConfManager *confManager) +bool TaskManager::saveSettings() { runExpiredTasks(); - if (!confManager->saveTasks(m_taskInfos)) + if (!confManager()->saveTasks(m_taskInfos)) return false; - fortSettings->removeTasks(); + settings()->removeTasks(); return true; } +void TaskManager::handleTaskStarted() +{ + auto taskInfo = qobject_cast(sender()); + + emit taskStarted(taskInfo); +} + void TaskManager::handleTaskFinished(bool success) { auto taskInfo = qobject_cast(sender()); taskInfo->processResult(m_fortManager, success); - saveSettings(m_fortManager->settings(), - m_fortManager->confManager()); + saveSettings(); + + emit taskFinished(taskInfo); } void TaskManager::runExpiredTasks() diff --git a/src/ui/task/taskmanager.h b/src/ui/task/taskmanager.h index c15ea91e..fef2feb5 100644 --- a/src/ui/task/taskmanager.h +++ b/src/ui/task/taskmanager.h @@ -21,6 +21,8 @@ public: QObject *parent = nullptr); FortManager *fortManager() const { return m_fortManager; } + FortSettings *settings() const; + ConfManager *confManager() const; TaskInfoUpdateChecker *taskInfoUpdateChecker() const { return m_taskInfoUpdateChecker; } @@ -29,11 +31,15 @@ public: signals: void taskInfosChanged(); + void taskStarted(TaskInfo *taskInfo); + void taskFinished(TaskInfo *taskInfo); + public slots: - void loadSettings(const FortSettings *fortSettings, ConfManager *confManager); - bool saveSettings(FortSettings *fortSettings, ConfManager *confManager); + void loadSettings(); + bool saveSettings(); private slots: + void handleTaskStarted(); void handleTaskFinished(bool success); void runExpiredTasks(); diff --git a/src/ui/log/model/stringlistmodel.cpp b/src/ui/util/model/stringlistmodel.cpp similarity index 89% rename from src/ui/log/model/stringlistmodel.cpp rename to src/ui/util/model/stringlistmodel.cpp index 11ab40bb..9cc37c90 100644 --- a/src/ui/log/model/stringlistmodel.cpp +++ b/src/ui/util/model/stringlistmodel.cpp @@ -62,6 +62,14 @@ void StringListModel::replace(const QString &text, int row) emit dataChanged(modelIndex, modelIndex); } +void StringListModel::refresh() +{ + const auto firstCell = index(0, 0); + const auto lastCell = index(rowCount() - 1, 0); + + emit dataChanged(firstCell, lastCell); +} + void StringListModel::removeRow(int row) { m_list.removeAt(row); diff --git a/src/ui/log/model/stringlistmodel.h b/src/ui/util/model/stringlistmodel.h similarity index 97% rename from src/ui/log/model/stringlistmodel.h rename to src/ui/util/model/stringlistmodel.h index b9bb631f..d5906a74 100644 --- a/src/ui/log/model/stringlistmodel.h +++ b/src/ui/util/model/stringlistmodel.h @@ -30,6 +30,7 @@ public slots: beginResetModel(); endResetModel(); } + void refresh(); protected: void removeRow(int row); diff --git a/src/ui/util/model/tableitemmodel.cpp b/src/ui/util/model/tableitemmodel.cpp new file mode 100644 index 00000000..292f4689 --- /dev/null +++ b/src/ui/util/model/tableitemmodel.cpp @@ -0,0 +1,47 @@ +#include "tableitemmodel.h" + +TableItemModel::TableItemModel(QObject *parent) : + QAbstractItemModel(parent) +{ +} + +QModelIndex TableItemModel::index(int row, int column, const QModelIndex &parent) const +{ + return hasIndex(row, column, parent) + ? createIndex(row, column) : QModelIndex(); +} + +QModelIndex TableItemModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child) + + return {}; +} + +QModelIndex TableItemModel::sibling(int row, int column, const QModelIndex &index) const +{ + Q_UNUSED(index) + + return this->index(row, column); +} + +bool TableItemModel::hasChildren(const QModelIndex &parent) const +{ + return !parent.isValid() && rowCount() > 0; +} + +Qt::ItemFlags TableItemModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; +} + +void TableItemModel::refresh() +{ + const auto firstCell = index(0, 0); + const auto lastCell = index(rowCount() - 1, columnCount(firstCell) - 1); + + emit dataChanged(firstCell, lastCell); +} diff --git a/src/ui/util/model/tableitemmodel.h b/src/ui/util/model/tableitemmodel.h new file mode 100644 index 00000000..5c9bae43 --- /dev/null +++ b/src/ui/util/model/tableitemmodel.h @@ -0,0 +1,31 @@ +#ifndef TABLEITEMMODEL_H +#define TABLEITEMMODEL_H + +#include + +class TableItemModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit TableItemModel(QObject *parent = nullptr); + + QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &child) const override; + + QModelIndex sibling(int row, int column, + const QModelIndex &index) const override; + bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + +public slots: + void reset() { + beginResetModel(); + endResetModel(); + } + void refresh(); +}; + +#endif // TABLEITEMMODEL_H