From afac18e0041c571901eeecffdfaf003742d5fecd Mon Sep 17 00:00:00 2001 From: Nodir Temirkhodjaev Date: Wed, 8 Jan 2020 15:33:34 +0500 Subject: [PATCH] UI: ProgramsWindow: Add sorting. --- src/ui/3rdparty/sqlite/sqlitedb.cpp | 34 ++++------- src/ui/3rdparty/sqlite/sqlitestmt.cpp | 9 +++ src/ui/3rdparty/sqlite/sqlitestmt.h | 1 + src/ui/conf/confmanager.cpp | 31 +++------- src/ui/conf/confmanager.h | 7 ++- src/ui/form/prog/programswindow.cpp | 18 +++++- src/ui/fortsettings.h | 9 +++ src/ui/log/model/applistmodel.cpp | 88 +++++++++++++++++++++++---- src/ui/log/model/applistmodel.h | 13 +++- 9 files changed, 145 insertions(+), 65 deletions(-) diff --git a/src/ui/3rdparty/sqlite/sqlitedb.cpp b/src/ui/3rdparty/sqlite/sqlitedb.cpp index 4960848a7..0de861da0 100644 --- a/src/ui/3rdparty/sqlite/sqlitedb.cpp +++ b/src/ui/3rdparty/sqlite/sqlitedb.cpp @@ -115,28 +115,18 @@ QVariant SqliteDb::executeEx(const char *sql, QVariantList list; SqliteStmt stmt; - bool success = true; - - if (stmt.prepare(db(), sql)) { - // Bind variables - if (!vars.isEmpty()) { - int index = 0; - for (const QVariant &v : vars) { - success = stmt.bindVar(++index, v); - if (!success) break; - } - } - - if (success) { - const auto stepRes = stmt.step(); - success = (stepRes != SqliteStmt::StepError); - - // Get result - if (stepRes == SqliteStmt::StepRow) { - for (int i = 0; i < resultCount; ++i) { - const QVariant v = stmt.columnVar(i); - list.append(v); - } + bool success = false; + + if (stmt.prepare(db(), sql) + && stmt.bindVars(vars)) { + const auto stepRes = stmt.step(); + success = (stepRes != SqliteStmt::StepError); + + // Get result + if (stepRes == SqliteStmt::StepRow) { + for (int i = 0; i < resultCount; ++i) { + const QVariant v = stmt.columnVar(i); + list.append(v); } } } diff --git a/src/ui/3rdparty/sqlite/sqlitestmt.cpp b/src/ui/3rdparty/sqlite/sqlitestmt.cpp index ab65fd0c7..3c6ab9b8f 100644 --- a/src/ui/3rdparty/sqlite/sqlitestmt.cpp +++ b/src/ui/3rdparty/sqlite/sqlitestmt.cpp @@ -136,6 +136,15 @@ bool SqliteStmt::bindVar(int index, const QVariant &v) } } +bool SqliteStmt::bindVars(const QVariantList &vars, int index) +{ + for (const QVariant &v : vars) { + if (!bindVar(index++, v)) + return false; + } + return true; +} + bool SqliteStmt::clearBindings() { m_bindObjects.clear(); diff --git a/src/ui/3rdparty/sqlite/sqlitestmt.h b/src/ui/3rdparty/sqlite/sqlitestmt.h index c3f73ebc4..31770e3e4 100644 --- a/src/ui/3rdparty/sqlite/sqlitestmt.h +++ b/src/ui/3rdparty/sqlite/sqlitestmt.h @@ -39,6 +39,7 @@ class SqliteStmt bool bindDateTime(int index, const QDateTime &dateTime); bool bindBlob(int index, const QByteArray &data); bool bindVar(int index, const QVariant &v); + bool bindVars(const QVariantList &vars, int index = 1); bool clearBindings(); bool reset(); diff --git a/src/ui/conf/confmanager.cpp b/src/ui/conf/confmanager.cpp index dd47bfaab..91cc98809 100644 --- a/src/ui/conf/confmanager.cpp +++ b/src/ui/conf/confmanager.cpp @@ -122,22 +122,6 @@ const char * const sqlUpdateTask = " WHERE task_id = ?1;" ; -const char * const sqlSelectAppCount = - "SELECT count(*) FROM app;" - ; - -const char * const sqlSelectAppByIndex = - "SELECT t.app_id," - " g.order_index as group_index," - " t.path, t.use_group_perm, t.blocked," - " (alert.app_id IS NOT NULL) as alerted," - " t.end_time" - " FROM app t" - " JOIN app_group g ON g.app_group_id = t.app_group_id" - " LEFT JOIN app_alert alert ON alert.app_id = t.app_id" - " LIMIT 1 OFFSET ?1;" - ; - const char * const sqlSelectApps = "SELECT g.order_index as group_index," " t.path, t.use_group_perm, t.blocked," @@ -325,10 +309,10 @@ bool ConfManager::saveTasks(const QList &taskInfos) return ok; } -int ConfManager::appCount() +int ConfManager::appCount(const QString &sql) { SqliteStmt stmt; - if (!stmt.prepare(m_sqliteDb->db(), sqlSelectAppCount) + if (!stmt.prepare(m_sqliteDb->db(), sql.toLatin1()) || stmt.step() != SqliteStmt::StepRow) return 0; @@ -336,15 +320,15 @@ int ConfManager::appCount() } bool ConfManager::getAppByIndex(bool &useGroupPerm, bool &blocked, bool &alerted, - qint64 &appId, int &groupIndex, - QString &appPath, QDateTime &endTime, int row) + qint64 &appId, int &groupIndex, QString &appPath, + QDateTime &endTime, QDateTime &creatTime, + const QString &sql, const QVariantList &vars) { SqliteStmt stmt; - if (!stmt.prepare(m_sqliteDb->db(), sqlSelectAppByIndex)) + if (!stmt.prepare(m_sqliteDb->db(), sql.toLatin1()) + || !stmt.bindVars(vars)) return false; - stmt.bindInt(1, row); - if (stmt.step() != SqliteStmt::StepRow) return false; @@ -355,6 +339,7 @@ bool ConfManager::getAppByIndex(bool &useGroupPerm, bool &blocked, bool &alerted blocked = stmt.columnBool(4); alerted = stmt.columnBool(5); endTime = stmt.columnDateTime(6); + creatTime = stmt.columnDateTime(7); return true; } diff --git a/src/ui/conf/confmanager.h b/src/ui/conf/confmanager.h index 5fc39fedf..1d81ce4f3 100644 --- a/src/ui/conf/confmanager.h +++ b/src/ui/conf/confmanager.h @@ -44,10 +44,11 @@ class ConfManager : public QObject bool loadTasks(const QList &taskInfos); bool saveTasks(const QList &taskInfos); - int appCount(); + int appCount(const QString &sql); bool getAppByIndex(bool &useGroupPerm, bool &blocked, bool &alerted, - qint64 &appId, int &groupIndex, - QString &appPath, QDateTime &endTime, int row); + qint64 &appId, int &groupIndex, QString &appPath, + QDateTime &endTime, QDateTime &creatTime, + const QString &sql, const QVariantList &vars); qint64 appGroupIdByIndex(int index = 0); QStringList appGroupNames(); diff --git a/src/ui/form/prog/programswindow.cpp b/src/ui/form/prog/programswindow.cpp index 7bceddf70..1c734f69c 100644 --- a/src/ui/form/prog/programswindow.cpp +++ b/src/ui/form/prog/programswindow.cpp @@ -31,6 +31,8 @@ namespace { +#define APPS_HEADER_VERSION 1 + const ValuesList appBlockInHourValues = { 3, 1, 6, 12, 24, 24 * 7, 24 * 30 }; @@ -81,10 +83,14 @@ void ProgramsWindow::onSaveWindowState() { auto header = m_appListView->horizontalHeader(); settings()->setProgAppsHeader(header->saveState()); + settings()->setProgAppsHeaderVersion(APPS_HEADER_VERSION); } void ProgramsWindow::onRestoreWindowState() { + if (settings()->progAppsHeaderVersion() != APPS_HEADER_VERSION) + return; + auto header = m_appListView->horizontalHeader(); header->restoreState(settings()->progAppsHeader()); } @@ -224,7 +230,6 @@ void ProgramsWindow::setupAppEditForm() auto pathLayout = new QHBoxLayout(); m_editPath = new QLineEdit(); - m_editPath->setClearButtonEnabled(true); m_btSelectFile = ControlUtil::createLinkButton(":/images/folder_explore.png"); @@ -366,6 +371,7 @@ void ProgramsWindow::setupTableApps() m_appListView->setSelectionMode(QAbstractItemView::SingleSelection); m_appListView->setSelectionBehavior(QAbstractItemView::SelectItems); + m_appListView->setSortingEnabled(true); m_appListView->setModel(appListModel()); connect(m_appListView, &TableView::doubleClicked, m_btEditApp, &QPushButton::click); @@ -376,14 +382,19 @@ void ProgramsWindow::setupTableAppsHeader() auto header = m_appListView->horizontalHeader(); header->setSectionResizeMode(0, QHeaderView::Interactive); - header->setSectionResizeMode(1, QHeaderView::Stretch); + header->setSectionResizeMode(1, QHeaderView::Interactive); header->setSectionResizeMode(2, QHeaderView::Fixed); header->setSectionResizeMode(3, QHeaderView::Interactive); header->setSectionResizeMode(4, QHeaderView::Stretch); + header->setSectionResizeMode(5, QHeaderView::Stretch); header->resizeSection(0, 500); header->resizeSection(2, 20); header->resizeSection(3, 80); + + header->setSectionsClickable(true); + header->setSortIndicatorShown(true); + header->setSortIndicator(5, Qt::DescendingOrder); } void ProgramsWindow::setupAppInfoRow() @@ -472,6 +483,7 @@ void ProgramsWindow::updateAppEditForm(bool editCurrentApp) m_editPath->setText(appRow.appPath); m_editPath->setReadOnly(editCurrentApp); + m_editPath->setClearButtonEnabled(!editCurrentApp); m_btSelectFile->setEnabled(!editCurrentApp); m_comboAppGroup->setCurrentIndex(appRow.groupIndex); m_cbUseGroupPerm->setChecked(appRow.useGroupPerm); @@ -498,7 +510,7 @@ void ProgramsWindow::deleteCurrentApp() const int appIndex = appListCurrentIndex(); if (appIndex >= 0) { const auto appRow = appListModel()->appRow(appIndex); - appListModel()->deleteApp(appRow.appId, appRow.appPath); + appListModel()->deleteApp(appRow.appId, appRow.appPath, appIndex); } } diff --git a/src/ui/fortsettings.h b/src/ui/fortsettings.h index 3edfd1fc0..865128a36 100644 --- a/src/ui/fortsettings.h +++ b/src/ui/fortsettings.h @@ -81,6 +81,15 @@ class FortSettings : public QObject bool progWindowMaximized() const { return iniBool("progWindow/maximized"); } void setProgWindowMaximized(bool on) { setIniValue("progWindow/maximized", on); } + bool progAppsSortDesc() const { return iniBool("progWindow/appsSortDesc"); } + void setProgSortDesc(bool v) { setIniValue("progWindow/appsSortDesc", v); } + + int progAppsSortColumn() const { return iniInt("progWindow/appsSortColumn"); } + void setProgSortColumn(int v) { setIniValue("progWindow/appsSortColumn", v); } + + int progAppsHeaderVersion() const { return iniInt("progWindow/appsHeaderVersion"); } + void setProgAppsHeaderVersion(int v) { setIniValue("progWindow/appsHeaderVersion", v); } + QByteArray progAppsHeader() const { return iniByteArray("progWindow/appsHeader"); } void setProgAppsHeader(const QByteArray &v) { setIniValue("progWindow/appsHeader", v); } diff --git a/src/ui/log/model/applistmodel.cpp b/src/ui/log/model/applistmodel.cpp index dddcf2eba..028e3c107 100644 --- a/src/ui/log/model/applistmodel.cpp +++ b/src/ui/log/model/applistmodel.cpp @@ -57,7 +57,7 @@ int AppListModel::rowCount(const QModelIndex &parent) const Q_UNUSED(parent) if (m_appCount < 0) { - m_appCount = confManager()->appCount(); + m_appCount = confManager()->appCount(sqlCount()); } return m_appCount; @@ -65,20 +65,25 @@ int AppListModel::rowCount(const QModelIndex &parent) const int AppListModel::columnCount(const QModelIndex &parent) const { - return parent.isValid() ? 0 : 5; + return parent.isValid() ? 0 : 6; } QVariant AppListModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation == Qt::Horizontal - && role == Qt::DisplayRole) { - switch (section) { - case 0: return tr("Program"); - case 1: return tr("Group"); - case 2: return tr("Gr."); - case 3: return tr("State"); - case 4: return tr("End Time"); + if (orientation == Qt::Horizontal) { + switch (role) { + case Qt::DisplayRole: { + switch (section) { + case 0: return tr("Program"); + case 1: return tr("Group"); + case 2: return tr("Gr."); + case 3: return tr("State"); + case 4: return tr("End Time"); + case 5: return tr("Creation Time"); + } + break; + } } } return QVariant(); @@ -111,6 +116,7 @@ QVariant AppListModel::data(const QModelIndex &index, int role) const case 3: return appStateToString(m_rowCache.state); case 4: return m_rowCache.endTime.isValid() ? m_rowCache.endTime : QVariant(); + case 5: return m_rowCache.creatTime; } break; @@ -174,6 +180,16 @@ QVariant AppListModel::data(const QModelIndex &index, int role) const return QVariant(); } +void AppListModel::sort(int column, Qt::SortOrder order) +{ + if (m_sortColumn != column || m_sortOrder != order) { + m_sortColumn = column; + m_sortOrder = order; + + reset(); + } +} + QString AppListModel::appPathByRow(int row) const { updateRowCache(row); @@ -216,11 +232,13 @@ bool AppListModel::updateApp(qint64 appId, const QString &appPath, return false; } -void AppListModel::deleteApp(qint64 appId, const QString &appPath) +void AppListModel::deleteApp(qint64 appId, const QString &appPath, int row) { if (confManager()->updateDriverDeleteApp(appPath) && confManager()->deleteApp(appId)) { - reset(); + beginRemoveRows(QModelIndex(), row, row); + invalidateRowCache(); + endRemoveRows(); } } @@ -252,12 +270,56 @@ void AppListModel::updateRowCache(int row) const if (m_confManager->getAppByIndex(m_rowCache.useGroupPerm, blocked, alerted, m_rowCache.appId, m_rowCache.groupIndex, - m_rowCache.appPath, m_rowCache.endTime, row)) { + m_rowCache.appPath, m_rowCache.endTime, + m_rowCache.creatTime, sql(), {row})) { m_rowCache.state = alerted ? AppAlert : (blocked ? AppBlock : AppAllow); m_rowCache.row = row; } } +QString AppListModel::sqlCount() const +{ + return "SELECT count(*) FROM (" + sqlBase() + ");"; +} + +QString AppListModel::sql() const +{ + return sqlBase() + sqlOrder() + " LIMIT 1 OFFSET ?1;" + ; +} + +QString AppListModel::sqlBase() const +{ + return + "SELECT t.app_id," + " g.order_index as group_index," + " t.path, t.use_group_perm, t.blocked," + " (alert.app_id IS NOT NULL) as alerted," + " t.end_time, t.creat_time" + " FROM app t" + " JOIN app_group g ON g.app_group_id = t.app_group_id" + " LEFT JOIN app_alert alert ON alert.app_id = t.app_id" + ; +} + +QString AppListModel::sqlOrder() const +{ + QString columnsStr = "1"; + switch (m_sortColumn) { + case 0: columnsStr = "3"; break; // Program + case 1: columnsStr = "2"; break; // Group + case 2: columnsStr = "4"; break; // Gr. + case 3: columnsStr = "5, 6"; break; // State + case 4: columnsStr = "7"; break; // End Time + case 5: columnsStr = "1"; break; // Creation Time + } + + const QString orderStr = (m_sortOrder == Qt::AscendingOrder) + ? "ASC" : "DESC"; + + return QString(" ORDER BY %1 %2").arg(columnsStr, orderStr); +} + void AppListModel::updateAppGroupNames() { m_appGroupNames = confManager()->appGroupNames(); diff --git a/src/ui/log/model/applistmodel.h b/src/ui/log/model/applistmodel.h index 485daab80..39ecb8fbb 100644 --- a/src/ui/log/model/applistmodel.h +++ b/src/ui/log/model/applistmodel.h @@ -32,6 +32,7 @@ struct AppRow { QString appPath; QDateTime endTime; + QDateTime creatTime; }; class AppListModel : public TableItemModel @@ -60,6 +61,8 @@ class AppListModel : public TableItemModel int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; + QString appPathByRow(int row) const; AppRow appRow(int row) const; @@ -69,7 +72,7 @@ class AppListModel : public TableItemModel bool updateApp(qint64 appId, const QString &appPath, int groupIndex, bool useGroupPerm, bool blocked, const QDateTime &endTime = QDateTime()); - void deleteApp(qint64 appId, const QString &appPath); + void deleteApp(qint64 appId, const QString &appPath, int row); QStringList appGroupNames() const { return m_appGroupNames; } QString appGroupNameByIndex(int groupIndex) const; @@ -82,11 +85,19 @@ public slots: void invalidateRowCache(); void updateRowCache(int row) const; + QString sqlCount() const; + QString sql() const; + QString sqlBase() const; + QString sqlOrder() const; + void updateAppGroupNames(); QString appStateToString(AppState state) const; private: + int m_sortColumn = 5; + Qt::SortOrder m_sortOrder = Qt::DescendingOrder; + mutable int m_appCount = -1; QStringList m_appGroupNames;