From c1421f00b1c582f9e67d3ca4f463ec8267b32060 Mon Sep 17 00:00:00 2001 From: Nodir Temirkhodjaev Date: Sat, 9 Mar 2024 12:49:23 +0300 Subject: [PATCH] UI: Rules: Rule id per type --- src/ui/conf/confrulemanager.cpp | 18 +++++++++------- src/ui/conf/confzonemanager.cpp | 6 ++++-- src/ui/conf/migrations/1.sql | 2 +- src/ui/conf/rule.cpp | 23 +++++++++++++++++++++ src/ui/conf/rule.h | 25 +++++++++++++++++++--- src/ui/form/rule/ruleeditdialog.cpp | 32 ++++++++++++++++++++++++++--- src/ui/form/rule/ruleeditdialog.h | 4 ++++ src/ui/model/rulelistmodel.cpp | 8 +++++--- src/ui/rpc/confrulemanagerrpc.cpp | 17 +++++++-------- src/ui/util/dbutil.cpp | 14 ++++++------- src/ui/util/dbutil.h | 3 ++- 11 files changed, 117 insertions(+), 35 deletions(-) diff --git a/src/ui/conf/confrulemanager.cpp b/src/ui/conf/confrulemanager.cpp index 23f63cd7..388b348a 100644 --- a/src/ui/conf/confrulemanager.cpp +++ b/src/ui/conf/confrulemanager.cpp @@ -18,17 +18,19 @@ namespace { const QLoggingCategory LC("confRule"); -const char *const sqlInsertRule = "INSERT INTO rule(rule_id, enabled, blocked, exclusive, name," - " notes, rule_text, accept_zones, reject_zones, mod_time)" - " VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10);"; +const char *const sqlInsertRule = "INSERT INTO rule(rule_id, enabled, blocked, exclusive," + " name, notes, rule_text, rule_type," + " accept_zones, reject_zones, mod_time)" + " VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11);"; const char *const sqlUpdateRule = "UPDATE rule" " SET enabled = ?2, blocked = ?3, exclusive = ?4," - " name = ?5, notes = ?6, rule_text = ?7," - " accept_zones = ?8, reject_zones = ?9, mod_time = ?10" + " name = ?5, notes = ?6, rule_text = ?7, rule_type = ?8," + " accept_zones = ?9, reject_zones = ?10, mod_time = ?11" " WHERE rule_id = ?1;"; -const char *const sqlSelectRuleIds = "SELECT rule_id FROM rule ORDER BY rule_id;"; +const char *const sqlSelectRuleIds = "SELECT rule_id FROM rule" + " WHERE rule_id >= ?1 ORDER BY rule_id;"; const char *const sqlDeleteRule = "DELETE FROM rule WHERE rule_id = ?1;"; @@ -83,7 +85,8 @@ bool ConfRuleManager::addOrUpdateRule(Rule &rule) const bool isNew = (rule.ruleId == 0); if (isNew) { - rule.ruleId = DbUtil::getFreeId(sqliteDb(), sqlSelectRuleIds, ConfUtil::ruleMaxCount(), ok); + const auto range = Rule::getRuleIdRangeByType(rule.ruleType); + rule.ruleId = DbUtil::getFreeId(sqliteDb(), sqlSelectRuleIds, range.minId, range.maxId, ok); } else { updateDriverRuleFlag(rule.ruleId, rule.enabled); } @@ -96,6 +99,7 @@ bool ConfRuleManager::addOrUpdateRule(Rule &rule) rule.ruleName, rule.notes, rule.ruleText, + rule.ruleType, rule.acceptZones, rule.rejectZones, DateUtil::now(), diff --git a/src/ui/conf/confzonemanager.cpp b/src/ui/conf/confzonemanager.cpp index 63afa25b..75fecd16 100644 --- a/src/ui/conf/confzonemanager.cpp +++ b/src/ui/conf/confzonemanager.cpp @@ -27,7 +27,8 @@ const char *const sqlUpdateZone = "UPDATE zone" " form_data = ?7, text_inline = ?8" " WHERE zone_id = ?1;"; -const char *const sqlSelectZoneIds = "SELECT zone_id FROM zone ORDER BY zone_id;"; +const char *const sqlSelectZoneIds = "SELECT zone_id FROM zone" + " WHERE zone_id >= ?1 ORDER BY zone_id;"; const char *const sqlDeleteZone = "DELETE FROM zone WHERE zone_id = ?1;"; @@ -99,7 +100,8 @@ bool ConfZoneManager::addOrUpdateZone(Zone &zone) const bool isNew = (zone.zoneId == 0); if (isNew) { - zone.zoneId = DbUtil::getFreeId(sqliteDb(), sqlSelectZoneIds, ConfUtil::zoneMaxCount(), ok); + zone.zoneId = DbUtil::getFreeId(sqliteDb(), sqlSelectZoneIds, /*minId=*/1, + /*maxId=*/ConfUtil::zoneMaxCount() - 1, ok); } else { updateDriverZoneFlag(zone.zoneId, zone.enabled); } diff --git a/src/ui/conf/migrations/1.sql b/src/ui/conf/migrations/1.sql index 814cc441..bfa3bdb5 100644 --- a/src/ui/conf/migrations/1.sql +++ b/src/ui/conf/migrations/1.sql @@ -92,7 +92,7 @@ CREATE TABLE rule( name TEXT NOT NULL, notes TEXT, rule_text TEXT NOT NULL, - rule_type INTEGER NOT NULL, -- app rules (1..64), global before/after apps (65..96, 97..128), preset rules (129..255) + rule_type INTEGER NOT NULL, -- app rules, global before/after apps, preset rules accept_zones INTEGER NOT NULL DEFAULT 0, -- zone indexes bit mask reject_zones INTEGER NOT NULL DEFAULT 0, -- zone indexes bit mask mod_time INTEGER NOT NULL diff --git a/src/ui/conf/rule.cpp b/src/ui/conf/rule.cpp index a6829276..a33a8171 100644 --- a/src/ui/conf/rule.cpp +++ b/src/ui/conf/rule.cpp @@ -11,3 +11,26 @@ bool Rule::isOptionsEqual(const Rule &o) const && acceptZones == o.acceptZones && rejectZones == o.rejectZones && ruleName == o.ruleName && notes == o.notes && ruleText == o.ruleText; } + +Rule::RuleType Rule::getRuleTypeById(int ruleId) +{ + for (int i = 0; i < RuleTypeCount; ++i) { + const auto ruleType = RuleType(i); + const auto range = getRuleIdRangeByType(ruleType); + + if (ruleId >= range.minId && ruleId <= range.maxId) + return ruleType; + } + + Q_UNREACHABLE(); + return AppRule; +} + +RuleIdRange Rule::getRuleIdRangeByType(RuleType ruleType) +{ + static const RuleIdRange ruleIdRanges[] = { { 1, 64 }, { 65, 96 }, { 97, 128 }, { 129, 255 } }; + + Q_ASSERT(ruleType >= 0 && ruleType < RuleTypeCount); + + return ruleIdRanges[ruleType]; +} diff --git a/src/ui/conf/rule.h b/src/ui/conf/rule.h index 4bff091a..990d0260 100644 --- a/src/ui/conf/rule.h +++ b/src/ui/conf/rule.h @@ -4,16 +4,35 @@ #include #include +struct RuleIdRange +{ + int minId; + int maxId; +}; + class Rule { public: + enum RuleType : qint8 { + AppRule = 0, // 1..64 + GlobalBeforeAppsRule, // 65..96 + GlobalAfterAppsRule, // 97..128 + PresetRule, // 129..255 + RuleTypeCount + }; + bool isNameEqual(const Rule &o) const; bool isOptionsEqual(const Rule &o) const; + static RuleType getRuleTypeById(int ruleId); + static RuleIdRange getRuleIdRangeByType(RuleType ruleType); + public: - bool enabled = true; - bool blocked = false; - bool exclusive = false; + bool enabled : 1 = true; + bool blocked : 1 = false; + bool exclusive : 1 = false; + + RuleType ruleType = AppRule; int ruleId = 0; diff --git a/src/ui/form/rule/ruleeditdialog.cpp b/src/ui/form/rule/ruleeditdialog.cpp index a205deb4..a7061745 100644 --- a/src/ui/form/rule/ruleeditdialog.cpp +++ b/src/ui/form/rule/ruleeditdialog.cpp @@ -37,11 +37,15 @@ void RuleEditDialog::initialize(const RuleRow &ruleRow) retranslateUi(); - m_editName->setText(m_ruleRow.ruleName); + m_editName->setText(ruleRow.ruleName); m_editName->setClearButtonEnabled(true); - m_editNotes->setText(m_ruleRow.notes); m_labelEditNotes->setPixmap(IconCache::file(":/icons/script.png")); + m_editNotes->setText(ruleRow.notes); + + m_labelRuleType->setText(tr("Type:")); + m_comboRuleType->setCurrentIndex(ruleRow.ruleType); + m_comboRuleType->setEnabled(isEmpty()); m_cbEnabled->setChecked(ruleRow.enabled); @@ -75,6 +79,9 @@ void RuleEditDialog::retranslateUi() m_labelEditName->setText(tr("Name:")); m_editNotes->setPlaceholderText(tr("Notes")); + + retranslateComboRuleType(); + m_cbEnabled->setText(tr("Enabled")); m_rbAllow->setText(tr("Allow")); @@ -89,6 +96,15 @@ void RuleEditDialog::retranslateUi() this->setWindowTitle(tr("Edit Rule")); } +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); +} + void RuleEditDialog::setupUi() { // Main Layout @@ -105,7 +121,7 @@ void RuleEditDialog::setupUi() this->setSizeGripEnabled(true); // Size - this->setMinimumSize(500, 300); + this->setMinimumWidth(400); } QLayout *RuleEditDialog::setupMainLayout() @@ -143,6 +159,7 @@ QLayout *RuleEditDialog::setupMainLayout() QLayout *RuleEditDialog::setupFormLayout() { auto layout = new QFormLayout(); + layout->setHorizontalSpacing(10); // Name m_editName = new QLineEdit(); @@ -160,6 +177,13 @@ QLayout *RuleEditDialog::setupFormLayout() m_labelEditNotes->setScaledContents(true); m_labelEditNotes->setFixedSize(32, 32); + // Rule Type + m_comboRuleType = ControlUtil::createComboBox(); + m_comboRuleType->setMinimumWidth(100); + + layout->addRow("Type:", m_comboRuleType); + m_labelRuleType = ControlUtil::formRowLabel(layout, m_comboRuleType); + // Enabled m_cbEnabled = new QCheckBox(); @@ -268,6 +292,8 @@ bool RuleEditDialog::validateFields() const void RuleEditDialog::fillRule(Rule &rule) const { + rule.ruleType = Rule::RuleType(m_comboRuleType->currentIndex()); + rule.enabled = m_cbEnabled->isChecked(); rule.blocked = !m_rbAllow->isChecked(); rule.exclusive = m_cbExclusive->isChecked(); diff --git a/src/ui/form/rule/ruleeditdialog.h b/src/ui/form/rule/ruleeditdialog.h index 3b0906a0..c251f636 100644 --- a/src/ui/form/rule/ruleeditdialog.h +++ b/src/ui/form/rule/ruleeditdialog.h @@ -6,6 +6,7 @@ #include QT_FORWARD_DECLARE_CLASS(QCheckBox) +QT_FORWARD_DECLARE_CLASS(QComboBox) QT_FORWARD_DECLARE_CLASS(QLabel) QT_FORWARD_DECLARE_CLASS(QLineEdit) QT_FORWARD_DECLARE_CLASS(QPushButton) @@ -37,6 +38,7 @@ private: void setupController(); void retranslateUi(); + void retranslateComboRuleType(); void setupUi(); QLayout *setupMainLayout(); @@ -59,6 +61,8 @@ private: QLineEdit *m_editName = nullptr; QLabel *m_labelEditNotes = nullptr; PlainTextEdit *m_editNotes = nullptr; + QLabel *m_labelRuleType = nullptr; + QComboBox *m_comboRuleType = nullptr; QCheckBox *m_cbEnabled = nullptr; QRadioButton *m_rbAllow = nullptr; QRadioButton *m_rbBlock = nullptr; diff --git a/src/ui/model/rulelistmodel.cpp b/src/ui/model/rulelistmodel.cpp index 9bd4436f..46afdf26 100644 --- a/src/ui/model/rulelistmodel.cpp +++ b/src/ui/model/rulelistmodel.cpp @@ -208,9 +208,10 @@ bool RuleListModel::updateRuleRow( ruleRow.ruleName = stmt.columnText(4); ruleRow.notes = stmt.columnText(5); ruleRow.ruleText = stmt.columnText(6); - ruleRow.acceptZones = stmt.columnUInt(7); - ruleRow.rejectZones = stmt.columnUInt(8); - ruleRow.modTime = stmt.columnDateTime(9); + ruleRow.ruleType = Rule::RuleType(stmt.columnInt(7)); + ruleRow.acceptZones = stmt.columnUInt(8); + ruleRow.rejectZones = stmt.columnUInt(9); + ruleRow.modTime = stmt.columnDateTime(10); return true; } @@ -225,6 +226,7 @@ QString RuleListModel::sqlBase() const " name," " notes," " rule_text," + " rule_type," " accept_zones," " reject_zones," " mod_time" diff --git a/src/ui/rpc/confrulemanagerrpc.cpp b/src/ui/rpc/confrulemanagerrpc.cpp index 9e1b741e..15b93d0b 100644 --- a/src/ui/rpc/confrulemanagerrpc.cpp +++ b/src/ui/rpc/confrulemanagerrpc.cpp @@ -94,8 +94,8 @@ bool ConfRuleManagerRpc::updateRuleEnabled(int ruleId, bool enabled) QVariantList ConfRuleManagerRpc::ruleToVarList(const Rule &rule) { - return { rule.enabled, rule.blocked, rule.exclusive, rule.ruleId, rule.acceptZones, - rule.rejectZones, rule.ruleName, rule.notes, rule.ruleText }; + return { rule.enabled, rule.blocked, rule.exclusive, rule.ruleType, rule.ruleId, + rule.acceptZones, rule.rejectZones, rule.ruleName, rule.notes, rule.ruleText }; } Rule ConfRuleManagerRpc::varListToRule(const QVariantList &v) @@ -104,12 +104,13 @@ Rule ConfRuleManagerRpc::varListToRule(const QVariantList &v) rule.enabled = v.value(0).toBool(); rule.blocked = v.value(1).toBool(); rule.exclusive = v.value(2).toBool(); - rule.ruleId = v.value(3).toInt(); - rule.acceptZones = v.value(4).toUInt(); - rule.rejectZones = v.value(5).toUInt(); - rule.ruleName = v.value(6).toString(); - rule.notes = v.value(7).toString(); - rule.ruleText = v.value(8).toString(); + rule.ruleType = Rule::RuleType(v.value(3).toInt()); + rule.ruleId = v.value(4).toInt(); + rule.acceptZones = v.value(5).toUInt(); + rule.rejectZones = v.value(6).toUInt(); + rule.ruleName = v.value(7).toString(); + rule.notes = v.value(8).toString(); + rule.ruleText = v.value(9).toString(); return rule; } diff --git a/src/ui/util/dbutil.cpp b/src/ui/util/dbutil.cpp index 9760ff6e..28c3eb31 100644 --- a/src/ui/util/dbutil.cpp +++ b/src/ui/util/dbutil.cpp @@ -3,22 +3,22 @@ #include #include -int DbUtil::getFreeId(SqliteDb *sqliteDb, const char *sqlSelectIds, int maxCount, bool &ok) +int DbUtil::getFreeId(SqliteDb *sqliteDb, const char *sqlSelectIds, int minId, int maxId, bool &ok) { - int freeId = 1; - SqliteStmt stmt; if (stmt.prepare(sqliteDb->db(), sqlSelectIds)) { + stmt.bindInt(1, minId); + while (stmt.step() == SqliteStmt::StepRow) { const int id = stmt.columnInt(0); - if (freeId < id) + if (minId < id || id >= maxId) break; - freeId = id + 1; + minId = id + 1; } } - ok = (freeId < maxCount); + ok = (minId <= maxId); - return freeId; + return minId; } diff --git a/src/ui/util/dbutil.h b/src/ui/util/dbutil.h index 88584a18..2f8dc37d 100644 --- a/src/ui/util/dbutil.h +++ b/src/ui/util/dbutil.h @@ -8,7 +8,8 @@ class SqliteDb; class DbUtil { public: - static int getFreeId(SqliteDb *sqliteDb, const char *sqlSelectIds, int maxCount, bool &ok); + static int getFreeId( + SqliteDb *sqliteDb, const char *sqlSelectIds, int minId, int maxId, bool &ok); }; #endif // DBUTIL_H