UI: Rules: Tree view by types

This commit is contained in:
Nodir Temirkhodjaev 2024-03-10 21:16:03 +03:00
parent 6df010c54a
commit 129c9095fa
33 changed files with 432 additions and 148 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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();

View File

@ -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();

View File

@ -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 \

View File

@ -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();

View File

@ -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);

View File

@ -0,0 +1,50 @@
#include "treeview.h"
#include <QContextMenuEvent>
#include <QMenu>
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 &current, const QModelIndex &previous)
{
QTreeView::currentChanged(current, previous);
emit currentIndexChanged(current);
}
void TreeView::contextMenuEvent(QContextMenuEvent *event)
{
if (m_menu) {
m_menu->popup(event->globalPos());
}
}

View File

@ -0,0 +1,34 @@
#ifndef TREEVIEW_H
#define TREEVIEW_H
#include <QTreeView>
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 &current, const QModelIndex &previous) override;
void contextMenuEvent(QContextMenuEvent *event) override;
private:
QMenu *m_menu = nullptr;
};
#endif // TREEVIEW_H

View File

@ -107,6 +107,11 @@ void ZonesSelector::setupZones()
connect(m_menuZones, &QMenu::aboutToShow, this, &ZonesSelector::updateZonesMenu);
setupZonesChanged();
}
void ZonesSelector::setupZonesChanged()
{
auto confZoneManager = IoC<ConfZoneManager>();
connect(confZoneManager, &ConfZoneManager::zoneRemoved, this, [&](int zoneId) {
@ -114,14 +119,17 @@ void ZonesSelector::setupZones()
retranslateZonesText();
});
auto zoneListModel = IoC<ZoneListModel>();
connect(zoneListModel, &ZoneListModel::modelChanged, this, [&] {
const auto refreshZonesMenu = [&] {
clearZonesMenu();
updateZonesMenuEnabled();
});
};
updateZonesMenuEnabled();
refreshZonesMenu();
auto zoneListModel = IoC<ZoneListModel>();
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()

View File

@ -38,6 +38,7 @@ private:
void setupUi();
void setupZones();
void setupZonesChanged();
void resetZonesMenu();
void clearZonesMenu();

View File

@ -14,6 +14,7 @@
#include <form/controls/plaintextedit.h>
#include <form/controls/zonesselector.h>
#include <manager/windowmanager.h>
#include <model/rulelistmodel.h>
#include <util/iconcache.h>
#include <util/net/netutil.h>
@ -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()

View File

@ -10,7 +10,7 @@
#include <conf/confmanager.h>
#include <conf/firewallconf.h>
#include <form/controls/controlutil.h>
#include <form/controls/tableview.h>
#include <form/controls/treeview.h>
#include <form/dialog/dialogutil.h>
#include <manager/windowmanager.h>
#include <model/rulelistmodel.h>
@ -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();

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -54,9 +54,6 @@ SqliteDb *RuleListModel::sqliteDb() const
void RuleListModel::initialize()
{
setSortColumn(1);
setSortOrder(Qt::AscendingOrder);
auto confRuleManager = IoC<ConfRuleManager>();
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"),
};
}

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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:

View File

@ -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;

View File

@ -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());
}
}

View File

@ -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;

View File

@ -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 { }

View File

@ -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:

View File

@ -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";
}

View File

@ -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