diff --git a/src/ui/3rdparty/sqlite/sqlitedb.cpp b/src/ui/3rdparty/sqlite/sqlitedb.cpp index cb6b61ee..80a8b52d 100644 --- a/src/ui/3rdparty/sqlite/sqlitedb.cpp +++ b/src/ui/3rdparty/sqlite/sqlitedb.cpp @@ -123,14 +123,15 @@ bool SqliteDb::executeStr(const QString &sql) return execute(sqlUtf8.data()); } -QVariant SqliteDb::executeEx(const char *sql, const QVariantList &vars, int resultCount, bool *ok) +QVariant SqliteDb::executeEx(const char *sql, const QVariantList &vars, const QVariantHash &varsMap, + int resultCount, bool *ok) { QVariantList list; SqliteStmt stmt; bool success = false; - if (prepare(stmt, sql, vars)) { + if (prepare(stmt, sql, vars, varsMap)) { const auto stepRes = stmt.step(); success = (stepRes != SqliteStmt::StepError); @@ -151,25 +152,47 @@ QVariant SqliteDb::executeEx(const char *sql, const QVariantList &vars, int resu return (listSize == 0) ? QVariant() : (listSize == 1 ? list.at(0) : list); } -bool SqliteDb::executeExOk(const char *sql, const QVariantList &vars) +QVariant SqliteDb::executeEx(const char *sql, const QVariantList &vars, int resultCount, bool *ok) +{ + return executeEx(sql, vars, {}, resultCount, ok); +} + +bool SqliteDb::executeExOk(const char *sql, const QVariantList &vars, const QVariantHash &varsMap) { bool ok = false; - executeEx(sql, vars, 0, &ok); + executeEx(sql, vars, varsMap, 0, &ok); return ok; } -bool SqliteDb::prepare(SqliteStmt &stmt, const char *sql, const QVariantList &vars) +bool SqliteDb::executeExOk(const char *sql, const QVariantList &vars) +{ + return executeExOk(sql, vars, {}); +} + +bool SqliteDb::prepare( + SqliteStmt &stmt, const char *sql, const QVariantList &vars, const QVariantHash &varsMap) { if (!stmt.prepare(db(), sql)) return false; - return stmt.bindVars(vars); + return stmt.bindVars(vars) && stmt.bindVarsMap(varsMap); +} + +bool SqliteDb::prepare(SqliteStmt &stmt, const char *sql, const QVariantList &vars) +{ + return prepare(stmt, sql, vars, {}); +} + +bool SqliteDb::prepare( + SqliteStmt &stmt, const QString &sql, const QVariantList &vars, const QVariantHash &varsMap) +{ + const auto sqlUtf8 = sql.toUtf8(); + return prepare(stmt, sqlUtf8.constData(), vars, varsMap); } bool SqliteDb::prepare(SqliteStmt &stmt, const QString &sql, const QVariantList &vars) { - const auto sqlUtf8 = sql.toUtf8(); - return prepare(stmt, sqlUtf8.constData(), vars); + return prepare(stmt, sql, vars, {}); } bool SqliteDb::done(SqliteStmt *stmt) diff --git a/src/ui/3rdparty/sqlite/sqlitedb.h b/src/ui/3rdparty/sqlite/sqlitedb.h index 8f35085e..2f9537b3 100644 --- a/src/ui/3rdparty/sqlite/sqlitedb.h +++ b/src/ui/3rdparty/sqlite/sqlitedb.h @@ -79,13 +79,22 @@ public: bool execute(const char *sql); bool executeStr(const QString &sql); + QVariant executeEx(const char *sql, const QVariantList &vars, const QVariantHash &varsMap, + int resultCount = 1, bool *ok = nullptr); QVariant executeEx(const char *sql, const QVariantList &vars = {}, int resultCount = 1, bool *ok = nullptr); + bool executeExOk(const char *sql, const QVariantList &vars, const QVariantHash &varsMap); bool executeExOk(const char *sql, const QVariantList &vars = {}); + bool prepare(SqliteStmt &stmt, const char *sql, const QVariantList &vars, + const QVariantHash &varsMap); bool prepare(SqliteStmt &stmt, const char *sql, const QVariantList &vars = {}); + + bool prepare(SqliteStmt &stmt, const QString &sql, const QVariantList &vars, + const QVariantHash &varsMap); bool prepare(SqliteStmt &stmt, const QString &sql, const QVariantList &vars = {}); + bool done(SqliteStmt *stmt); qint64 lastInsertRowid() const; diff --git a/src/ui/3rdparty/sqlite/sqlitestmt.cpp b/src/ui/3rdparty/sqlite/sqlitestmt.cpp index 437afcf9..893b438a 100644 --- a/src/ui/3rdparty/sqlite/sqlitestmt.cpp +++ b/src/ui/3rdparty/sqlite/sqlitestmt.cpp @@ -164,8 +164,11 @@ bool SqliteStmt::bindVars(const QVariantList &vars, int index) return true; } -bool SqliteStmt::bindVarsMap(const QVariantMap &varsMap) +bool SqliteStmt::bindVarsMap(const QVariantHash &varsMap) { + if (varsMap.isEmpty()) + return true; + auto it = varsMap.constBegin(); for (; it != varsMap.constEnd(); ++it) { const QString k = it.key(); diff --git a/src/ui/3rdparty/sqlite/sqlitestmt.h b/src/ui/3rdparty/sqlite/sqlitestmt.h index 6aeb4c27..f41e31bd 100644 --- a/src/ui/3rdparty/sqlite/sqlitestmt.h +++ b/src/ui/3rdparty/sqlite/sqlitestmt.h @@ -46,7 +46,7 @@ public: bool bindVarBlob(int index, const QVariant &v); bool bindVar(int index, const QVariant &v); bool bindVars(const QVariantList &vars, int index = 1); - bool bindVarsMap(const QVariantMap &varsMap); + bool bindVarsMap(const QVariantHash &varsMap); bool clearBindings(); bool reset(); diff --git a/src/ui/FortFirewallUI.pro b/src/ui/FortFirewallUI.pro index d602e539..cba66bdd 100644 --- a/src/ui/FortFirewallUI.pro +++ b/src/ui/FortFirewallUI.pro @@ -58,6 +58,7 @@ SOURCES += \ form/controls/tableview.cpp \ form/controls/textarea2splitter.cpp \ form/controls/textarea2splitterhandle.cpp \ + form/controls/treeview.cpp \ form/controls/zonesselector.cpp \ form/dialog/dialogutil.cpp \ form/dialog/passworddialog.cpp \ @@ -268,6 +269,7 @@ HEADERS += \ form/controls/tableview.h \ form/controls/textarea2splitter.h \ form/controls/textarea2splitterhandle.h \ + form/controls/treeview.h \ form/controls/zonesselector.h \ form/dialog/dialogutil.h \ form/dialog/passworddialog.h \ diff --git a/src/ui/form/controls/controlutil.cpp b/src/ui/form/controls/controlutil.cpp index ae3a8f90..9e55216f 100644 --- a/src/ui/form/controls/controlutil.cpp +++ b/src/ui/form/controls/controlutil.cpp @@ -265,6 +265,26 @@ QLayout *ControlUtil::createRowLayout(QWidget *w1, QWidget *w2, int stretch1) return layout; } +void ControlUtil::clearLayout(QLayout *layout) +{ + if (!layout) + return; + + int i = layout->count(); + while (--i >= 0) { + auto item = layout->takeAt(i); + + auto w = item->widget(); + if (w) { + w->deleteLater(); + } else { + clearLayout(item->layout()); + } + + delete item; + } +} + QLayout *ControlUtil::createScrollLayout(QLayout *content, bool isBgTransparent) { auto scrollAreaContent = new QWidget(); diff --git a/src/ui/form/controls/controlutil.h b/src/ui/form/controls/controlutil.h index 518f8a73..be50f202 100644 --- a/src/ui/form/controls/controlutil.h +++ b/src/ui/form/controls/controlutil.h @@ -90,6 +90,8 @@ public: static QLayout *createRowLayout(QWidget *w1, QWidget *w2, int stretch1 = 1); + static void clearLayout(QLayout *layout); + static QLayout *createScrollLayout(QLayout *content, bool isBgTransparent = true); static QWidget *wrapToScrollArea(QWidget *content, bool isBgTransparent = true); diff --git a/src/ui/form/controls/treeview.cpp b/src/ui/form/controls/treeview.cpp new file mode 100644 index 00000000..952d5ce1 --- /dev/null +++ b/src/ui/form/controls/treeview.cpp @@ -0,0 +1,50 @@ +#include "treeview.h" + +#include +#include + +TreeView::TreeView(QWidget *parent) : QTreeView(parent) +{ + setTabKeyNavigation(false); + setUniformRowHeights(true); + setAnimated(true); +} + +void TreeView::setModel(QAbstractItemModel *model) +{ + QTreeView::setModel(model); + + connect(model, &QAbstractItemModel::modelReset, this, + [&] { emit currentIndexChanged(currentIndex()); }); +} + +int TreeView::currentRow() const +{ + return currentIndex().row(); +} + +void TreeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + QTreeView::selectionChanged(selected, deselected); + + if (selected.isEmpty()) + return; + + if (!currentIndex().isValid()) { + setCurrentIndex(selected.indexes().first()); + } +} + +void TreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + QTreeView::currentChanged(current, previous); + + emit currentIndexChanged(current); +} + +void TreeView::contextMenuEvent(QContextMenuEvent *event) +{ + if (m_menu) { + m_menu->popup(event->globalPos()); + } +} diff --git a/src/ui/form/controls/treeview.h b/src/ui/form/controls/treeview.h new file mode 100644 index 00000000..b99b1863 --- /dev/null +++ b/src/ui/form/controls/treeview.h @@ -0,0 +1,34 @@ +#ifndef TREEVIEW_H +#define TREEVIEW_H + +#include + +class TreeView : public QTreeView +{ + Q_OBJECT + +public: + explicit TreeView(QWidget *parent = nullptr); + + QMenu *menu() const { return m_menu; } + void setMenu(QMenu *menu) { m_menu = menu; } + + void setModel(QAbstractItemModel *model) override; + + int currentRow() const; + +signals: + void currentIndexChanged(const QModelIndex &index); + +protected: + void selectionChanged( + const QItemSelection &selected, const QItemSelection &deselected) override; + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous) override; + + void contextMenuEvent(QContextMenuEvent *event) override; + +private: + QMenu *m_menu = nullptr; +}; + +#endif // TREEVIEW_H diff --git a/src/ui/form/controls/zonesselector.cpp b/src/ui/form/controls/zonesselector.cpp index 11aa6576..2056dc3c 100644 --- a/src/ui/form/controls/zonesselector.cpp +++ b/src/ui/form/controls/zonesselector.cpp @@ -107,6 +107,11 @@ void ZonesSelector::setupZones() connect(m_menuZones, &QMenu::aboutToShow, this, &ZonesSelector::updateZonesMenu); + setupZonesChanged(); +} + +void ZonesSelector::setupZonesChanged() +{ auto confZoneManager = IoC(); connect(confZoneManager, &ConfZoneManager::zoneRemoved, this, [&](int zoneId) { @@ -114,14 +119,17 @@ void ZonesSelector::setupZones() retranslateZonesText(); }); - auto zoneListModel = IoC(); - - connect(zoneListModel, &ZoneListModel::modelChanged, this, [&] { + const auto refreshZonesMenu = [&] { clearZonesMenu(); updateZonesMenuEnabled(); - }); + }; - updateZonesMenuEnabled(); + refreshZonesMenu(); + + auto zoneListModel = IoC(); + + connect(zoneListModel, &ZoneListModel::modelReset, this, refreshZonesMenu); + connect(zoneListModel, &ZoneListModel::dataChanged, this, refreshZonesMenu); } void ZonesSelector::resetZonesMenu() @@ -134,12 +142,7 @@ void ZonesSelector::clearZonesMenu() { m_menuZones->close(); - int i = m_menuLayout->count(); - while (--i >= 0) { - auto item = m_menuLayout->takeAt(i); - item->widget()->deleteLater(); - delete item; - } + ControlUtil::clearLayout(m_menuLayout); } void ZonesSelector::createZonesMenu() diff --git a/src/ui/form/controls/zonesselector.h b/src/ui/form/controls/zonesselector.h index 1c9bbd54..87aed448 100644 --- a/src/ui/form/controls/zonesselector.h +++ b/src/ui/form/controls/zonesselector.h @@ -38,6 +38,7 @@ private: void setupUi(); void setupZones(); + void setupZonesChanged(); void resetZonesMenu(); void clearZonesMenu(); diff --git a/src/ui/form/rule/ruleeditdialog.cpp b/src/ui/form/rule/ruleeditdialog.cpp index 81f7ed79..6fd9ccfe 100644 --- a/src/ui/form/rule/ruleeditdialog.cpp +++ b/src/ui/form/rule/ruleeditdialog.cpp @@ -14,6 +14,7 @@ #include
#include #include +#include #include #include @@ -100,11 +101,7 @@ void RuleEditDialog::retranslateUi() void RuleEditDialog::retranslateComboRuleType() { - const QStringList list = { tr("Application Rules"), - tr("Global Rules, applied before App Rules"), tr("Global Rules, applied after App Rules"), - tr("Preset Rules") }; - - ControlUtil::setComboBoxTexts(m_comboRuleType, list); + ControlUtil::setComboBoxTexts(m_comboRuleType, RuleListModel::ruleTypeNames()); } void RuleEditDialog::retranslateRulePlaceholderText() diff --git a/src/ui/form/rule/ruleswindow.cpp b/src/ui/form/rule/ruleswindow.cpp index 6f3db5cb..a789bc16 100644 --- a/src/ui/form/rule/ruleswindow.cpp +++ b/src/ui/form/rule/ruleswindow.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -25,7 +25,7 @@ namespace { -constexpr int RULES_HEADER_VERSION = 1; +constexpr int RULES_HEADER_VERSION = 2; } @@ -74,7 +74,7 @@ void RulesWindow::saveWindowState(bool /*wasVisible*/) iniUser()->setRuleWindowGeometry(m_stateWatcher->geometry()); iniUser()->setRuleWindowMaximized(m_stateWatcher->maximized()); - auto header = m_ruleListView->horizontalHeader(); + auto header = m_ruleListView->header(); iniUser()->setRulesHeader(header->saveState()); iniUser()->setRulesHeaderVersion(RULES_HEADER_VERSION); @@ -87,7 +87,7 @@ void RulesWindow::restoreWindowState() iniUser()->ruleWindowMaximized()); if (iniUser()->rulesHeaderVersion() == RULES_HEADER_VERSION) { - auto header = m_ruleListView->horizontalHeader(); + auto header = m_ruleListView->header(); header->restoreState(iniUser()->rulesHeader()); } } @@ -124,14 +124,14 @@ void RulesWindow::setupUi() // Header auto header = setupHeader(); - // Table - setupTableRules(); - setupTableRulesHeader(); + // Tree + setupTreeRules(); + setupTreeRulesHeader(); - // Actions on zones table's current changed - setupTableRulesChanged(); + // Actions on rules tree's current changed + setupTreeRulesChanged(); - // Actions on zone list model's changed + // Actions on rule list model's changed setupRuleListModelChanged(); auto layout = new QVBoxLayout(); @@ -199,48 +199,52 @@ void RulesWindow::setupEditSearch() connect(this, &RulesWindow::aboutToShow, m_editSearch, qOverload<>(&QWidget::setFocus)); } -void RulesWindow::setupTableRules() +void RulesWindow::setupTreeRules() { - m_ruleListView = new TableView(); + m_ruleListView = new TreeView(); m_ruleListView->setAlternatingRowColors(true); m_ruleListView->setSelectionMode(QAbstractItemView::SingleSelection); m_ruleListView->setSelectionBehavior(QAbstractItemView::SelectItems); m_ruleListView->setModel(ruleListModel()); + const auto appRulesIndex = ruleListModel()->index(0, 0); + m_ruleListView->expand(appRulesIndex); + m_ruleListView->setMenu(m_btEdit->menu()); - connect(m_ruleListView, &TableView::activated, m_actEditRule, &QAction::trigger); + connect(m_ruleListView, &TreeView::activated, m_actEditRule, &QAction::trigger); } -void RulesWindow::setupTableRulesHeader() +void RulesWindow::setupTreeRulesHeader() { - auto header = m_ruleListView->horizontalHeader(); + auto header = m_ruleListView->header(); header->setSectionResizeMode(0, QHeaderView::Interactive); header->setSectionResizeMode(1, QHeaderView::Stretch); + header->setStretchLastSection(true); - header->resizeSection(0, 350); + header->resizeSection(0, 360); } -void RulesWindow::setupTableRulesChanged() +void RulesWindow::setupTreeRulesChanged() { - const auto refreshTableRulesChanged = [&] { + const auto refreshTreeRulesChanged = [&] { const int ruleIndex = ruleListCurrentIndex(); const bool ruleSelected = (ruleIndex >= 0); m_actEditRule->setEnabled(ruleSelected); m_actRemoveRule->setEnabled(ruleSelected); }; - refreshTableRulesChanged(); + refreshTreeRulesChanged(); - connect(m_ruleListView, &TableView::currentIndexChanged, this, refreshTableRulesChanged); + connect(m_ruleListView, &TreeView::currentIndexChanged, this, refreshTreeRulesChanged); } void RulesWindow::setupRuleListModelChanged() { const auto refreshAddRule = [&] { - m_actAddRule->setEnabled(ruleListModel()->rowCount() < ConfUtil::zoneMaxCount()); + m_actAddRule->setEnabled(ruleListModel()->rowCount() < ConfUtil::ruleMaxCount()); }; refreshAddRule(); diff --git a/src/ui/form/rule/ruleswindow.h b/src/ui/form/rule/ruleswindow.h index 8baf0f94..26f0608b 100644 --- a/src/ui/form/rule/ruleswindow.h +++ b/src/ui/form/rule/ruleswindow.h @@ -15,7 +15,7 @@ class IniUser; class RuleEditDialog; class RuleListModel; class RulesController; -class TableView; +class TreeView; class WidgetWindowStateWatcher; class WindowManager; @@ -50,9 +50,9 @@ private: void setupUi(); QLayout *setupHeader(); void setupEditSearch(); - void setupTableRules(); - void setupTableRulesHeader(); - void setupTableRulesChanged(); + void setupTreeRules(); + void setupTreeRulesHeader(); + void setupTreeRulesChanged(); void setupRuleListModelChanged(); void addNewRule(); @@ -75,7 +75,7 @@ private: QAction *m_actRemoveRule = nullptr; QLineEdit *m_editSearch = nullptr; QPushButton *m_btMenu = nullptr; - TableView *m_ruleListView = nullptr; + TreeView *m_ruleListView = nullptr; RuleEditDialog *m_formRuleEdit = nullptr; }; diff --git a/src/ui/model/applistmodel.cpp b/src/ui/model/applistmodel.cpp index b8c5668b..ef2ec3b3 100644 --- a/src/ui/model/applistmodel.cpp +++ b/src/ui/model/applistmodel.cpp @@ -394,10 +394,10 @@ QIcon AppListModel::appIcon(const AppRow &appRow) const return appInfoCache()->appIcon(appRow.appPath); } -bool AppListModel::updateAppRow(const QString &sql, const QVariantList &vars, AppRow &appRow) const +bool AppListModel::updateAppRow(const QString &sql, const QVariantHash &vars, AppRow &appRow) const { SqliteStmt stmt; - if (!(sqliteDb()->prepare(stmt, sql, vars) && stmt.step() == SqliteStmt::StepRow)) { + if (!(sqliteDb()->prepare(stmt, sql, {}, vars) && stmt.step() == SqliteStmt::StepRow)) { appRow.invalidate(); return false; } @@ -438,7 +438,7 @@ const AppRow &AppListModel::appRowAt(int row) const AppRow AppListModel::appRowById(qint64 appId) const { AppRow appRow; - updateAppRow(sqlBase() + " WHERE t.app_id = ?1;", { appId }, appRow); + updateAppRow(sqlBase() + " WHERE t.app_id = :id;", { { "id", appId } }, appRow); return appRow; } @@ -455,12 +455,8 @@ AppRow AppListModel::appRowByPath(const QString &appPath) const return appRow; } -bool AppListModel::updateTableRow(int row) const +bool AppListModel::updateTableRow(const QVariantHash &vars, int /*row*/) const { - QVariantList vars; - fillSqlVars(vars); - vars.append(row); // must be a last one for :OFFSET - return updateAppRow(sql(), vars, m_appRow); } diff --git a/src/ui/model/applistmodel.h b/src/ui/model/applistmodel.h index 03173835..3e917f47 100644 --- a/src/ui/model/applistmodel.h +++ b/src/ui/model/applistmodel.h @@ -42,7 +42,7 @@ public: AppRow appRowByPath(const QString &appPath) const; protected: - bool updateTableRow(int row) const override; + bool updateTableRow(const QVariantHash &vars, int row) const override; TableRow &tableRow() const override { return m_appRow; } QString sqlBase() const override; @@ -57,7 +57,7 @@ private: QIcon appIcon(const AppRow &appRow) const; - bool updateAppRow(const QString &sql, const QVariantList &vars, AppRow &appRow) const; + bool updateAppRow(const QString &sql, const QVariantHash &vars, AppRow &appRow) const; private: mutable AppRow m_appRow; diff --git a/src/ui/model/connblocklistmodel.cpp b/src/ui/model/connblocklistmodel.cpp index 1598d850..785f0ba8 100644 --- a/src/ui/model/connblocklistmodel.cpp +++ b/src/ui/model/connblocklistmodel.cpp @@ -265,7 +265,7 @@ void ConnBlockListModel::updateConnIdRange() updateConnRows(oldIdMin, oldIdMax, idMin, idMax); } -bool ConnBlockListModel::updateTableRow(int row) const +bool ConnBlockListModel::updateTableRow(const QVariantHash & /*vars*/, int row) const { const qint64 connId = connIdMin() + row; diff --git a/src/ui/model/connblocklistmodel.h b/src/ui/model/connblocklistmodel.h index e1e16fb7..2fee7ee5 100644 --- a/src/ui/model/connblocklistmodel.h +++ b/src/ui/model/connblocklistmodel.h @@ -70,9 +70,11 @@ protected slots: void updateConnIdRange(); protected: - bool updateTableRow(int row) const override; + bool updateTableRow(const QVariantHash &vars, int row) const override; TableRow &tableRow() const override { return m_connRow; } + void fillQueryVarsForRow(QVariantHash & /*vars*/, int /*row*/) const override { } + int doSqlCount() const override; QString sqlBase() const override; QString sqlWhere() const override; diff --git a/src/ui/model/rulelistmodel.cpp b/src/ui/model/rulelistmodel.cpp index b4ebf85a..296041f8 100644 --- a/src/ui/model/rulelistmodel.cpp +++ b/src/ui/model/rulelistmodel.cpp @@ -54,9 +54,6 @@ SqliteDb *RuleListModel::sqliteDb() const void RuleListModel::initialize() { - setSortColumn(1); - setSortOrder(Qt::AscendingOrder); - auto confRuleManager = IoC(); connect(confRuleManager, &ConfRuleManager::ruleAdded, this, &TableItemModel::reset); @@ -64,9 +61,51 @@ void RuleListModel::initialize() connect(confRuleManager, &ConfRuleManager::ruleUpdated, this, &TableItemModel::refresh); } -int RuleListModel::columnCount(const QModelIndex &parent) const +QModelIndex RuleListModel::index(int row, int column, const QModelIndex &parent) const { - return parent.isValid() ? 0 : 2; + int id = row; + if (parent.isValid()) { + id = parent.internalId(); + if (id < 0) + return QModelIndex(); + + id = -(id + 1); + } + + return createIndex(row, column, id); +} + +QModelIndex RuleListModel::parent(const QModelIndex &child) const +{ + if (!child.isValid()) + return QModelIndex(); + + const int id = child.internalId(); + if (id >= 0) + return QModelIndex(); + + const int row = -(id + 1); + + return createIndex(row, 0, row); +} + +int RuleListModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) + return Rule::RuleTypeCount; + + const int id = parent.internalId(); + if (id < 0) + return 0; + + setSqlRuleType(id); + + return FtsTableSqlModel::rowCount(parent); +} + +int RuleListModel::columnCount(const QModelIndex & /*parent*/) const +{ + return 2; } QVariant RuleListModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -82,6 +121,13 @@ QVariant RuleListModel::data(const QModelIndex &index, int role) const if (!index.isValid()) return QVariant(); + const QModelIndex parent = index.parent(); + if (!parent.isValid()) { + return rootData(index, role); + } + + setSqlRuleType(parent.row()); + switch (role) { // Label case Qt::DisplayRole: @@ -95,6 +141,29 @@ QVariant RuleListModel::data(const QModelIndex &index, int role) const // Enabled case Qt::CheckStateRole: return dataCheckState(index); + + // Row Height + case Qt::SizeHintRole: + return QSize(100, 24); + } + + return QVariant(); +} + +QVariant RuleListModel::rootData(const QModelIndex &index, int role) const +{ + if (index.column() > 0) + return QVariant(); + + switch (role) { + // Label + case Qt::DisplayRole: + case Qt::ToolTipRole: + return ruleTypeNames().value(index.row()); + + // Row Height + case Qt::SizeHintRole: + return QSize(100, 24); } return QVariant(); @@ -155,13 +224,14 @@ QVariant RuleListModel::dataCheckState(const QModelIndex &index) const return QVariant(); } -bool RuleListModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool RuleListModel::setData(const QModelIndex &index, const QVariant & /*value*/, int role) { - Q_UNUSED(value); - if (!index.isValid()) return false; + const QModelIndex parent = index.parent(); + setSqlRuleType(parent.row()); + switch (role) { case Qt::CheckStateRole: const auto ruleRow = ruleRowAt(index.row()); @@ -171,9 +241,23 @@ bool RuleListModel::setData(const QModelIndex &index, const QVariant &value, int return false; } +Qt::ItemFlags RuleListModel::flagHasChildren(const QModelIndex &index) const +{ + const QModelIndex parent = index.parent(); + + return parent.isValid() ? Qt::ItemNeverHasChildren : Qt::NoItemFlags; +} + Qt::ItemFlags RuleListModel::flagIsUserCheckable(const QModelIndex &index) const { - return index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags; + const QModelIndex parent = index.parent(); + + return parent.isValid() && index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags; +} + +void RuleListModel::fillQueryVars(QVariantHash &vars) const +{ + vars.insert(":type", sqlRuleType()); } const RuleRow &RuleListModel::ruleRowAt(int row) const @@ -183,20 +267,16 @@ const RuleRow &RuleListModel::ruleRowAt(int row) const return m_ruleRow; } -bool RuleListModel::updateTableRow(int row) const +bool RuleListModel::updateTableRow(const QVariantHash &vars, int /*row*/) const { - QVariantList vars; - fillSqlVars(vars); - vars.append(row); // must be a last one for :OFFSET - return updateRuleRow(sql(), vars, m_ruleRow); } bool RuleListModel::updateRuleRow( - const QString &sql, const QVariantList &vars, RuleRow &ruleRow) const + const QString &sql, const QVariantHash &vars, RuleRow &ruleRow) const { SqliteStmt stmt; - if (!(sqliteDb()->prepare(stmt, sql, vars) && stmt.step() == SqliteStmt::StepRow)) { + if (!(sqliteDb()->prepare(stmt, sql, {}, vars) && stmt.step() == SqliteStmt::StepRow)) { ruleRow.invalidate(); return false; } @@ -230,15 +310,36 @@ QString RuleListModel::sqlBase() const " accept_zones," " reject_zones," " mod_time" - " FROM rule t"; + " FROM rule t" + " WHERE rule_type = :type"; } QString RuleListModel::sqlWhereFts() const { - return " WHERE t.rule_id IN ( SELECT rowid FROM rule_fts(:match) )"; + return " AND t.rule_id IN ( SELECT rowid FROM rule_fts(:match) )"; } QString RuleListModel::sqlOrderColumn() const { return "rule_type, lower(name)"; } + +void RuleListModel::setSqlRuleType(qint8 v) const +{ + if (m_sqlRuleType == v) + return; + + m_sqlRuleType = v; + + invalidateRowCache(); +} + +QStringList RuleListModel::ruleTypeNames() +{ + return { + RuleListModel::tr("Application Rules"), + RuleListModel::tr("Global Rules, applied before App Rules"), + RuleListModel::tr("Global Rules, applied after App Rules"), + RuleListModel::tr("Preset Rules"), + }; +} diff --git a/src/ui/model/rulelistmodel.h b/src/ui/model/rulelistmodel.h index 3a3f312d..c61d465a 100644 --- a/src/ui/model/rulelistmodel.h +++ b/src/ui/model/rulelistmodel.h @@ -29,6 +29,11 @@ public: void initialize(); + QModelIndex index( + int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &child) const override; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData( @@ -38,25 +43,37 @@ public: const RuleRow &ruleRowAt(int row) const; + static QStringList ruleTypeNames(); + protected: + Qt::ItemFlags flagHasChildren(const QModelIndex &index) const override; Qt::ItemFlags flagIsUserCheckable(const QModelIndex &index) const override; - bool updateTableRow(int row) const override; + void fillQueryVars(QVariantHash &vars) const override; + + bool updateTableRow(const QVariantHash &vars, int row) const override; TableRow &tableRow() const override { return m_ruleRow; } - bool updateRuleRow(const QString &sql, const QVariantList &vars, RuleRow &ruleRow) const; + bool updateRuleRow(const QString &sql, const QVariantHash &vars, RuleRow &ruleRow) const; QString sqlBase() const override; QString sqlWhereFts() const override; QString sqlOrderColumn() const override; + qint8 sqlRuleType() const { return m_sqlRuleType; } + void setSqlRuleType(qint8 v) const; + private: + QVariant rootData(const QModelIndex &index, int role) const; + QVariant headerDataDisplay(int section) const; QVariant dataDisplay(const QModelIndex &index, int role) const; QVariant dataDecoration(const QModelIndex &index) const; QVariant dataCheckState(const QModelIndex &index) const; private: + mutable qint8 m_sqlRuleType = -1; + mutable RuleRow m_ruleRow; }; diff --git a/src/ui/model/servicelistmodel.cpp b/src/ui/model/servicelistmodel.cpp index f82efb9d..5171815a 100644 --- a/src/ui/model/servicelistmodel.cpp +++ b/src/ui/model/servicelistmodel.cpp @@ -114,7 +114,7 @@ QVariant ServiceListModel::dataDecoration(const QModelIndex &index) const return QVariant(); } -bool ServiceListModel::updateTableRow(int /*row*/) const +bool ServiceListModel::updateTableRow(const QVariantHash & /*vars*/, int /*row*/) const { return true; } diff --git a/src/ui/model/servicelistmodel.h b/src/ui/model/servicelistmodel.h index ca6b91b9..b741f724 100644 --- a/src/ui/model/servicelistmodel.h +++ b/src/ui/model/servicelistmodel.h @@ -33,9 +33,11 @@ public: const ServiceInfo &serviceInfoAt(int index) const; protected: - bool updateTableRow(int row) const override; + bool updateTableRow(const QVariantHash &vars, int row) const override; TableRow &tableRow() const override { return m_serviceRow; } + void fillQueryVarsForRow(QVariantHash & /*vars*/, int /*row*/) const override { } + private: QVariant dataDisplay(const QModelIndex &index) const; QVariant dataDisplayProcessId(const ServiceInfo &info) const; diff --git a/src/ui/model/traflistmodel.cpp b/src/ui/model/traflistmodel.cpp index c71cd5dd..3bf68ef0 100644 --- a/src/ui/model/traflistmodel.cpp +++ b/src/ui/model/traflistmodel.cpp @@ -177,7 +177,7 @@ void TrafListModel::reset() } } -bool TrafListModel::updateTableRow(int row) const +bool TrafListModel::updateTableRow(const QVariantHash & /*vars*/, int row) const { m_trafRow.trafTime = getTrafTime(row); diff --git a/src/ui/model/traflistmodel.h b/src/ui/model/traflistmodel.h index 9fa28eca..1ed64de1 100644 --- a/src/ui/model/traflistmodel.h +++ b/src/ui/model/traflistmodel.h @@ -54,9 +54,11 @@ public slots: void reset(); protected: - bool updateTableRow(int row) const override; + bool updateTableRow(const QVariantHash &vars, int row) const override; TableRow &tableRow() const override { return m_trafRow; } + void fillQueryVarsForRow(QVariantHash & /*vars*/, int /*row*/) const override { } + QString formatTrafUnit(qint64 bytes) const; QString formatTrafTime(qint32 trafTime) const; diff --git a/src/ui/model/zonelistmodel.cpp b/src/ui/model/zonelistmodel.cpp index 83ec6fe4..6c2df7c6 100644 --- a/src/ui/model/zonelistmodel.cpp +++ b/src/ui/model/zonelistmodel.cpp @@ -171,26 +171,32 @@ QVariant ZoneListModel::zoneSourceByCode(const QString &sourceCode) const return m_zoneSourcesMap.value(sourceCode); } -bool ZoneListModel::updateTableRow(int row) const +bool ZoneListModel::updateTableRow(const QVariantHash &vars, int /*row*/) const +{ + return updateZoneRow(sql(), vars, m_zoneRow); +} + +bool ZoneListModel::updateZoneRow( + const QString &sql, const QVariantHash &vars, ZoneRow &zoneRow) const { SqliteStmt stmt; - if (!(sqliteDb()->prepare(stmt, sql(), { row }) && stmt.step() == SqliteStmt::StepRow)) + if (!(sqliteDb()->prepare(stmt, sql, {}, vars) && stmt.step() == SqliteStmt::StepRow)) return false; - m_zoneRow.zoneId = stmt.columnInt(0); - m_zoneRow.enabled = stmt.columnBool(1); - m_zoneRow.customUrl = stmt.columnBool(2); - m_zoneRow.zoneName = stmt.columnText(3); - m_zoneRow.sourceCode = stmt.columnText(4); - m_zoneRow.url = stmt.columnText(5); - m_zoneRow.formData = stmt.columnText(6); - m_zoneRow.addressCount = stmt.columnInt(7); - m_zoneRow.textInline = stmt.columnText(8); - m_zoneRow.textChecksum = stmt.columnText(9); - m_zoneRow.binChecksum = stmt.columnText(10); - m_zoneRow.sourceModTime = stmt.columnDateTime(11); - m_zoneRow.lastRun = stmt.columnDateTime(12); - m_zoneRow.lastSuccess = stmt.columnDateTime(13); + zoneRow.zoneId = stmt.columnInt(0); + zoneRow.enabled = stmt.columnBool(1); + zoneRow.customUrl = stmt.columnBool(2); + zoneRow.zoneName = stmt.columnText(3); + zoneRow.sourceCode = stmt.columnText(4); + zoneRow.url = stmt.columnText(5); + zoneRow.formData = stmt.columnText(6); + zoneRow.addressCount = stmt.columnInt(7); + zoneRow.textInline = stmt.columnText(8); + zoneRow.textChecksum = stmt.columnText(9); + zoneRow.binChecksum = stmt.columnText(10); + zoneRow.sourceModTime = stmt.columnDateTime(11); + zoneRow.lastRun = stmt.columnDateTime(12); + zoneRow.lastSuccess = stmt.columnDateTime(13); return true; } diff --git a/src/ui/model/zonelistmodel.h b/src/ui/model/zonelistmodel.h index aafd8a45..999b5d88 100644 --- a/src/ui/model/zonelistmodel.h +++ b/src/ui/model/zonelistmodel.h @@ -47,9 +47,11 @@ public: protected: Qt::ItemFlags flagIsUserCheckable(const QModelIndex &index) const override; - bool updateTableRow(int row) const override; + bool updateTableRow(const QVariantHash &vars, int row) const override; TableRow &tableRow() const override { return m_zoneRow; } + bool updateZoneRow(const QString &sql, const QVariantHash &vars, ZoneRow &zoneRow) const; + QString sqlBase() const override; private: diff --git a/src/ui/task/tasklistmodel.h b/src/ui/task/tasklistmodel.h index 3c378504..5fb6d87e 100644 --- a/src/ui/task/tasklistmodel.h +++ b/src/ui/task/tasklistmodel.h @@ -48,9 +48,11 @@ signals: protected: Qt::ItemFlags flagIsUserCheckable(const QModelIndex &index) const override; - bool updateTableRow(int /*row*/) const override { return true; } + bool updateTableRow(const QVariantHash & /*vars*/, int /*row*/) const override { return true; } TableRow &tableRow() const override { return m_taskRow; } + void fillQueryVarsForRow(QVariantHash & /*vars*/, int /*row*/) const override { } + private: QVariant dataDisplay(const QModelIndex &index) const; QVariant dataCheckState(const QModelIndex &index) const; diff --git a/src/ui/util/model/ftstablesqlmodel.cpp b/src/ui/util/model/ftstablesqlmodel.cpp index e44452d7..923c6f03 100644 --- a/src/ui/util/model/ftstablesqlmodel.cpp +++ b/src/ui/util/model/ftstablesqlmodel.cpp @@ -30,10 +30,10 @@ void FtsTableSqlModel::setFtsFilter(const QString &filter) resetLater(); } -void FtsTableSqlModel::fillSqlVars(QVariantList &vars) const +void FtsTableSqlModel::fillQueryVars(QVariantHash &vars) const { if (!ftsFilterMatch().isEmpty()) { - vars.append(ftsFilterMatch()); + vars.insert(":match", ftsFilterMatch()); } } diff --git a/src/ui/util/model/ftstablesqlmodel.h b/src/ui/util/model/ftstablesqlmodel.h index 653a5f80..ba7c7f73 100644 --- a/src/ui/util/model/ftstablesqlmodel.h +++ b/src/ui/util/model/ftstablesqlmodel.h @@ -15,7 +15,7 @@ public: void setFtsFilter(const QString &filter); protected: - void fillSqlVars(QVariantList &vars) const override; + void fillQueryVars(QVariantHash &vars) const override; QString sqlWhere() const override; virtual QString sqlWhereFts() const = 0; diff --git a/src/ui/util/model/tableitemmodel.cpp b/src/ui/util/model/tableitemmodel.cpp index 98601d8a..667a485f 100644 --- a/src/ui/util/model/tableitemmodel.cpp +++ b/src/ui/util/model/tableitemmodel.cpp @@ -9,23 +9,14 @@ QModelIndex TableItemModel::index(int row, int column, const QModelIndex &parent return hasIndex(row, column, parent) ? createIndex(row, column) : QModelIndex(); } -QModelIndex TableItemModel::parent(const QModelIndex &child) const +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; + return rowCount(parent) > 0; } Qt::ItemFlags TableItemModel::flags(const QModelIndex &index) const @@ -33,7 +24,7 @@ Qt::ItemFlags TableItemModel::flags(const QModelIndex &index) const if (!index.isValid()) return Qt::NoItemFlags; - return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | flagHasChildren(index) | flagIsUserCheckable(index); } @@ -48,6 +39,11 @@ void TableItemModel::resetLater() m_resetTimer->startTrigger(); } +Qt::ItemFlags TableItemModel::flagHasChildren(const QModelIndex & /*index*/) const +{ + return Qt::ItemNeverHasChildren; +} + Qt::ItemFlags TableItemModel::flagIsUserCheckable(const QModelIndex & /*index*/) const { return Qt::NoItemFlags; @@ -70,7 +66,7 @@ void TableItemModel::refresh() emit dataChanged(firstCell, lastCell); } -void TableItemModel::invalidateRowCache() +void TableItemModel::invalidateRowCache() const { tableRow().invalidate(); } @@ -80,9 +76,18 @@ void TableItemModel::updateRowCache(int row) const if (tableRow().isValid(row)) return; - if (row >= 0 && !updateTableRow(row)) { - row = -1; + if (row >= 0) { + QVariantHash vars; + fillQueryVarsForRow(vars, row); + + if (!updateTableRow(vars, row)) { + row = -1; + } } tableRow().row = row; } + +void TableItemModel::fillQueryVars(QVariantHash & /*vars*/) const { } + +void TableItemModel::fillQueryVarsForRow(QVariantHash & /*vars*/, int /*row*/) const { } diff --git a/src/ui/util/model/tableitemmodel.h b/src/ui/util/model/tableitemmodel.h index e75dd075..8cf4266b 100644 --- a/src/ui/util/model/tableitemmodel.h +++ b/src/ui/util/model/tableitemmodel.h @@ -25,7 +25,6 @@ public: 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; @@ -36,12 +35,16 @@ public slots: void refresh(); protected: + virtual Qt::ItemFlags flagHasChildren(const QModelIndex &index) const; virtual Qt::ItemFlags flagIsUserCheckable(const QModelIndex &index) const; - virtual void invalidateRowCache(); + virtual void invalidateRowCache() const; void updateRowCache(int row) const; - virtual bool updateTableRow(int row) const = 0; + virtual void fillQueryVars(QVariantHash &vars) const; + virtual void fillQueryVarsForRow(QVariantHash &vars, int row) const; + + virtual bool updateTableRow(const QVariantHash &vars, int row) const = 0; virtual TableRow &tableRow() const = 0; private: diff --git a/src/ui/util/model/tablesqlmodel.cpp b/src/ui/util/model/tablesqlmodel.cpp index a336e1a5..ce386262 100644 --- a/src/ui/util/model/tablesqlmodel.cpp +++ b/src/ui/util/model/tablesqlmodel.cpp @@ -5,15 +5,13 @@ TableSqlModel::TableSqlModel(QObject *parent) : TableItemModel(parent) { } -int TableSqlModel::rowCount(const QModelIndex &parent) const +int TableSqlModel::rowCount(const QModelIndex & /*parent*/) const { - Q_UNUSED(parent); - - if (m_rowCount < 0) { - m_rowCount = doSqlCount(); + if (m_sqlRowCount < 0) { + m_sqlRowCount = doSqlCount(); } - return m_rowCount; + return m_sqlRowCount; } void TableSqlModel::sort(int column, Qt::SortOrder order) @@ -26,26 +24,26 @@ void TableSqlModel::sort(int column, Qt::SortOrder order) } } -void TableSqlModel::invalidateRowCache() +void TableSqlModel::invalidateRowCache() const { - m_rowCount = -1; + setSqlRowCount(-1); TableItemModel::invalidateRowCache(); - emit modelChanged(); } -void TableSqlModel::fillSqlVars(QVariantList &vars) const +void TableSqlModel::fillQueryVarsForRow(QVariantHash &vars, int row) const { - Q_UNUSED(vars); + fillQueryVars(vars); + vars.insert(":offset", row); } int TableSqlModel::doSqlCount() const { - QVariantList vars; - fillSqlVars(vars); + QVariantHash vars; + fillQueryVars(vars); const auto sqlUtf8 = sqlCount().toUtf8(); - return sqliteDb()->executeEx(sqlUtf8, vars).toInt(); + return sqliteDb()->executeEx(sqlUtf8, {}, vars).toInt(); } QString TableSqlModel::sqlCount() const @@ -83,5 +81,5 @@ QString TableSqlModel::sqlOrderColumn() const QString TableSqlModel::sqlLimitOffset() const { - return " LIMIT 1 OFFSET :row"; + return " LIMIT 1 OFFSET :offset"; } diff --git a/src/ui/util/model/tablesqlmodel.h b/src/ui/util/model/tablesqlmodel.h index c49241dd..fd03536c 100644 --- a/src/ui/util/model/tablesqlmodel.h +++ b/src/ui/util/model/tablesqlmodel.h @@ -18,13 +18,10 @@ public: void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; -signals: - void modelChanged(); - protected: - void invalidateRowCache() override; + void invalidateRowCache() const override; - virtual void fillSqlVars(QVariantList &vars) const; + void fillQueryVarsForRow(QVariantHash &vars, int row) const override; virtual int doSqlCount() const; virtual QString sqlCount() const; @@ -43,11 +40,14 @@ protected: Qt::SortOrder sortOrder() const { return m_sortOrder; } void setSortOrder(Qt::SortOrder v) { m_sortOrder = v; } + int sqlRowCount() const { return m_sqlRowCount; } + void setSqlRowCount(int v) const { m_sqlRowCount = v; } + private: int m_sortColumn = -1; Qt::SortOrder m_sortOrder = Qt::AscendingOrder; - mutable int m_rowCount = -1; + mutable int m_sqlRowCount = -1; }; #endif // TABLESQLMODEL_H