From b110b594422713d40e2ba7df329ca267091e4280 Mon Sep 17 00:00:00 2001 From: Nodir Temirkhodjaev Date: Thu, 13 Feb 2020 14:11:25 +0500 Subject: [PATCH] UI: AddressesPage: Edit Zones. --- src/ui/3rdparty/sqlite/sqlitedb.cpp | 2 +- src/ui/conf/addressgroup.cpp | 41 ++++ src/ui/conf/addressgroup.h | 17 +- src/ui/conf/confmanager.cpp | 179 +++++++++++++----- src/ui/conf/migrations/1.sql | 1 - .../opt/pages/addresses/addressescolumn.cpp | 6 +- src/ui/form/opt/pages/addressespage.cpp | 128 ++++++++++++- src/ui/form/opt/pages/addressespage.h | 9 + src/ui/form/opt/pages/basepage.h | 1 + src/ui/model/zonelistmodel.cpp | 21 +- src/ui/model/zonelistmodel.h | 5 +- src/ui/util/model/tablesqlmodel.cpp | 1 + src/ui/util/model/tablesqlmodel.h | 3 + 13 files changed, 339 insertions(+), 75 deletions(-) diff --git a/src/ui/3rdparty/sqlite/sqlitedb.cpp b/src/ui/3rdparty/sqlite/sqlitedb.cpp index 26ee8ead..1c7a72c0 100644 --- a/src/ui/3rdparty/sqlite/sqlitedb.cpp +++ b/src/ui/3rdparty/sqlite/sqlitedb.cpp @@ -143,7 +143,7 @@ bool SqliteDb::prepare(SqliteStmt &stmt, const char *sql, const QVariantList &vars) { return stmt.prepare(db(), sql) - && stmt.bindVars(vars); + && (vars.isEmpty() || stmt.bindVars(vars)); } qint64 SqliteDb::lastInsertRowid() const diff --git a/src/ui/conf/addressgroup.cpp b/src/ui/conf/addressgroup.cpp index d84b4419..dc38c857 100644 --- a/src/ui/conf/addressgroup.cpp +++ b/src/ui/conf/addressgroup.cpp @@ -48,6 +48,44 @@ void AddressGroup::setExcludeText(const QString &excludeText) } } +void AddressGroup::addIncludeZone(int zoneId, bool sorting) +{ + addZone(m_includeZones, zoneId, sorting); +} + +void AddressGroup::removeIncludeZone(int zoneId) +{ + removeZone(m_includeZones, zoneId); +} + +void AddressGroup::addExcludeZone(int zoneId, bool sorting) +{ + addZone(m_excludeZones, zoneId, sorting); +} + +void AddressGroup::removeExcludeZone(int zoneId) +{ + removeZone(m_excludeZones, zoneId); +} + +void AddressGroup::addZone(QVector &zones, int zoneId, bool sorting) +{ + zones.append(zoneId); + + if (sorting) { + std::sort(zones.begin(), zones.end()); + } + + setEdited(true); +} + +void AddressGroup::removeZone(QVector &zones, int zoneId) +{ + zones.removeOne(zoneId); + + setEdited(true); +} + void AddressGroup::copy(const AddressGroup &o) { m_edited = o.edited(); @@ -59,4 +97,7 @@ void AddressGroup::copy(const AddressGroup &o) m_includeText = o.includeText(); m_excludeText = o.excludeText(); + + m_includeZones = o.includeZones(); + m_excludeZones = o.excludeZones(); } diff --git a/src/ui/conf/addressgroup.h b/src/ui/conf/addressgroup.h index 291dfd54..873d5fe8 100644 --- a/src/ui/conf/addressgroup.h +++ b/src/ui/conf/addressgroup.h @@ -3,6 +3,7 @@ #include #include +#include class AddressGroup : public QObject { @@ -33,6 +34,15 @@ public: QString excludeText() const { return m_excludeText; } void setExcludeText(const QString &excludeText); + const QVector &includeZones() const { return m_includeZones; } + const QVector &excludeZones() const { return m_excludeZones; } + + void addIncludeZone(int zoneId, bool sorting = false); + void removeIncludeZone(int zoneId); + + void addExcludeZone(int zoneId, bool sorting = false); + void removeExcludeZone(int zoneId); + void copy(const AddressGroup &o); signals: @@ -41,7 +51,9 @@ signals: void includeTextChanged(); void excludeTextChanged(); -public slots: +private: + void addZone(QVector &zones, int zoneId, bool sorting); + void removeZone(QVector &zones, int zoneId); private: bool m_edited : 1; @@ -53,6 +65,9 @@ private: QString m_includeText; QString m_excludeText; + + QVector m_includeZones; + QVector m_excludeZones; }; #endif // ADDRESSGROUP_H diff --git a/src/ui/conf/confmanager.cpp b/src/ui/conf/confmanager.cpp index fb912823..6f495861 100644 --- a/src/ui/conf/confmanager.cpp +++ b/src/ui/conf/confmanager.cpp @@ -41,6 +41,13 @@ const char * const sqlSelectAddressGroups = " ORDER BY order_index;" ; +const char * const sqlSelectAddressGroupZones = + "SELECT include, zone_id" + " FROM address_group_zone" + " WHERE addr_group_id = ?1" + " ORDER BY include, zone_id;" + ; + const char * const sqlSelectAppGroups = "SELECT app_group_id, enabled," " fragment_packet, period_enabled," @@ -66,6 +73,16 @@ const char * const sqlUpdateAddressGroup = " WHERE addr_group_id = ?1;" ; +const char * const sqlDeleteAddressGroupZones = + "DELETE FROM address_group_zone" + " WHERE addr_group_id = ?1;" + ; + +const char * const sqlInsertAddressGroupZone = + "INSERT INTO address_group_zone(addr_group_id, zone_id, include)" + " VALUES(?1, ?2, ?3);" + ; + const char * const sqlInsertAppGroup = "INSERT INTO app_group(app_group_id, order_index, enabled," " fragment_packet, period_enabled," @@ -236,6 +253,86 @@ bool migrateFunc(SqliteDb *db, int version, bool isNewDb, void *ctx) return true; } +bool loadAddressGroupZones(SqliteDb *db, AddressGroup *addrGroup) +{ + SqliteStmt stmt; + if (!db->prepare(stmt, sqlSelectAddressGroupZones, {addrGroup->id()})) + return false; + + while (stmt.step() == SqliteStmt::StepRow) { + const bool include = stmt.columnBool(0); + const int zoneId = stmt.columnInt(1); + + if (include) { + addrGroup->addIncludeZone(zoneId); + } else { + addrGroup->addExcludeZone(zoneId); + } + } + + return true; +} + +bool saveAddressGroupZones(SqliteDb *db, AddressGroup *addrGroup) +{ + const auto addrGroupId = addrGroup->id(); + + bool ok; + db->executeEx(sqlDeleteAddressGroupZones, {addrGroupId}, 0, &ok); + if (!ok) return false; + + SqliteStmt stmt; + if (!db->prepare(stmt, sqlInsertAddressGroupZone)) + return false; + + const auto addAddressGroupZone = [&](int zoneId, bool include) -> bool { + return stmt.bindVars({addrGroupId, zoneId, include}) + && stmt.step() == SqliteStmt::StepDone + && stmt.reset(); + }; + + for (const int zoneId : addrGroup->includeZones()) { + if (!addAddressGroupZone(zoneId, true)) + return false; + } + for (const int zoneId : addrGroup->excludeZones()) { + if (!addAddressGroupZone(zoneId, false)) + return false; + } + + return true; +} + +bool loadAddressGroups(SqliteDb *db, const QList &addressGroups, + int &index) +{ + SqliteStmt stmt; + if (!db->prepare(stmt, sqlSelectAddressGroups)) + return false; + + index = 0; + while (stmt.step() == SqliteStmt::StepRow) { + auto addrGroup = addressGroups.at(index); + Q_ASSERT(addrGroup != nullptr); + + addrGroup->setId(stmt.columnInt64(0)); + addrGroup->setIncludeAll(stmt.columnBool(1)); + addrGroup->setExcludeAll(stmt.columnBool(2)); + addrGroup->setIncludeText(stmt.columnText(3)); + addrGroup->setExcludeText(stmt.columnText(4)); + + if (!loadAddressGroupZones(db, addrGroup)) + return false; + + addrGroup->setEdited(false); + + if (++index > 1) + break; + } + + return true; +} + bool saveAddressGroup(SqliteDb *db, AddressGroup *addrGroup, int orderIndex) { const bool rowExists = (addrGroup->id() != 0); @@ -260,11 +357,45 @@ bool saveAddressGroup(SqliteDb *db, AddressGroup *addrGroup, int orderIndex) if (!rowExists) { addrGroup->setId(db->lastInsertRowid()); } + + if (!saveAddressGroupZones(db, addrGroup)) + return false; + addrGroup->setEdited(false); return true; } +bool loadAppGroups(SqliteDb *db, FirewallConf &conf) +{ + SqliteStmt stmt; + if (!db->prepare(stmt, sqlSelectAppGroups)) + return false; + + while (stmt.step() == SqliteStmt::StepRow) { + auto appGroup = new AppGroup(); + + appGroup->setId(stmt.columnInt64(0)); + appGroup->setEnabled(stmt.columnBool(1)); + appGroup->setFragmentPacket(stmt.columnInt(2)); + appGroup->setPeriodEnabled(stmt.columnBool(3)); + appGroup->setLimitInEnabled(stmt.columnBool(4)); + appGroup->setLimitOutEnabled(stmt.columnBool(5)); + appGroup->setSpeedLimitIn(quint32(stmt.columnInt(6))); + appGroup->setSpeedLimitOut(quint32(stmt.columnInt(7))); + appGroup->setName(stmt.columnText(8)); + appGroup->setBlockText(stmt.columnText(9)); + appGroup->setAllowText(stmt.columnText(10)); + appGroup->setPeriodFrom(stmt.columnText(11)); + appGroup->setPeriodTo(stmt.columnText(12)); + appGroup->setEdited(false); + + conf.addAppGroup(appGroup); + } + + return true; +} + bool saveAppGroup(SqliteDb *db, AppGroup *appGroup, int orderIndex) { const bool rowExists = (appGroup->id() != 0); @@ -851,26 +982,11 @@ bool ConfManager::loadFromDb(FirewallConf &conf, bool &isNew) { // Load Address Groups { - SqliteStmt stmt; - if (!stmt.prepare(m_sqliteDb->db(), sqlSelectAddressGroups)) + int count = 0; + if (!loadAddressGroups(m_sqliteDb, conf.addressGroups(), count)) return false; - int index = 0; - while (stmt.step() == SqliteStmt::StepRow) { - auto addrGroup = conf.addressGroups().at(index); - Q_ASSERT(addrGroup != nullptr); - - addrGroup->setId(stmt.columnInt64(0)); - addrGroup->setIncludeAll(stmt.columnBool(1)); - addrGroup->setExcludeAll(stmt.columnBool(2)); - addrGroup->setIncludeText(stmt.columnText(3)); - addrGroup->setExcludeText(stmt.columnText(4)); - - if (++index > 1) - break; - } - - if (index == 0) { + if (count == 0) { isNew = true; return true; } @@ -878,31 +994,8 @@ bool ConfManager::loadFromDb(FirewallConf &conf, bool &isNew) } // Load App Groups - { - SqliteStmt stmt; - if (!stmt.prepare(m_sqliteDb->db(), sqlSelectAppGroups)) - return false; - - while (stmt.step() == SqliteStmt::StepRow) { - auto appGroup = new AppGroup(); - - appGroup->setId(stmt.columnInt64(0)); - appGroup->setEnabled(stmt.columnBool(1)); - appGroup->setFragmentPacket(stmt.columnInt(2)); - appGroup->setPeriodEnabled(stmt.columnBool(3)); - appGroup->setLimitInEnabled(stmt.columnBool(4)); - appGroup->setLimitOutEnabled(stmt.columnBool(5)); - appGroup->setSpeedLimitIn(quint32(stmt.columnInt(6))); - appGroup->setSpeedLimitOut(quint32(stmt.columnInt(7))); - appGroup->setName(stmt.columnText(8)); - appGroup->setBlockText(stmt.columnText(9)); - appGroup->setAllowText(stmt.columnText(10)); - appGroup->setPeriodFrom(stmt.columnText(11)); - appGroup->setPeriodTo(stmt.columnText(12)); - - conf.addAppGroup(appGroup); - } - } + if (!loadAppGroups(m_sqliteDb, conf)) + return false; return true; } diff --git a/src/ui/conf/migrations/1.sql b/src/ui/conf/migrations/1.sql index 392496b7..d42a6b92 100644 --- a/src/ui/conf/migrations/1.sql +++ b/src/ui/conf/migrations/1.sql @@ -26,7 +26,6 @@ CREATE TABLE address_group( CREATE TABLE address_group_zone( addr_group_id INTEGER NOT NULL, zone_id INTEGER NOT NULL, - order_index INTEGER NOT NULL, include BOOLEAN NOT NULL ); diff --git a/src/ui/form/opt/pages/addresses/addressescolumn.cpp b/src/ui/form/opt/pages/addresses/addressescolumn.cpp index d5ab3515..f426ace8 100644 --- a/src/ui/form/opt/pages/addresses/addressescolumn.cpp +++ b/src/ui/form/opt/pages/addresses/addressescolumn.cpp @@ -7,6 +7,7 @@ #include #include "../../../controls/controlutil.h" +#include "../../../controls/widebutton.h" #include "../../../controls/plaintextedit.h" AddressesColumn::AddressesColumn(QWidget *parent) : @@ -17,7 +18,7 @@ AddressesColumn::AddressesColumn(QWidget *parent) : void AddressesColumn::retranslateUi() { - m_btSelectZones->setText(tr("Zones:")); + m_btSelectZones->setText(tr("Zones")); m_btSelectZones->setToolTip(tr("Select Zones")); } @@ -55,9 +56,8 @@ QLayout *AddressesColumn::setupZonesRow() { auto layout = new QHBoxLayout(); layout->setMargin(0); - layout->setSpacing(0); - m_btSelectZones = ControlUtil::createLinkButton(":/images/map_magnify.png"); + m_btSelectZones = new WideButton(QIcon(":/images/map_magnify.png")); layout->addWidget(m_btSelectZones); m_labelZones = ControlUtil::createLabel(); diff --git a/src/ui/form/opt/pages/addressespage.cpp b/src/ui/form/opt/pages/addressespage.cpp index 674f18f7..a4aa8915 100644 --- a/src/ui/form/opt/pages/addressespage.cpp +++ b/src/ui/form/opt/pages/addressespage.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,7 @@ AddressesPage::AddressesPage(OptionsController *ctrl, setupUi(); setupAddressGroup(); + setupZones(); } AddressGroup *AddressesPage::addressGroup() const @@ -210,12 +212,12 @@ void AddressesPage::setupSplitterButtons() void AddressesPage::updateGroup() { m_includeAddresses->cbUseAll()->setChecked(addressGroup()->includeAll()); - m_includeAddresses->labelZones()->setText(zonesText(true)); m_includeAddresses->editIpText()->setText(addressGroup()->includeText()); m_excludeAddresses->cbUseAll()->setChecked(addressGroup()->excludeAll()); - m_excludeAddresses->labelZones()->setText(zonesText(false)); m_excludeAddresses->editIpText()->setText(addressGroup()->excludeText()); + + updateZonesTextAll(); } void AddressesPage::setupAddressGroup() @@ -232,6 +234,119 @@ void AddressesPage::setupAddressGroup() connect(m_tabBar, &QTabBar::currentChanged, this, refreshAddressGroup); } +void AddressesPage::clearZonesMenu() +{ + m_menuZones->close(); + m_menuZones->clear(); +} + +void AddressesPage::createZonesMenu() +{ + const auto onZoneActionTriggered = [&](bool checked) { + auto action = qobject_cast(sender()); + const int zoneId = action->data().toInt(); + + const bool include = m_includeAddresses->btSelectZones()->isDown(); + auto addrGroup = this->addressGroup(); + + if (checked) { + if (include) { + addrGroup->addIncludeZone(zoneId, true); + } else { + addrGroup->addExcludeZone(zoneId, true); + } + } else { + if (include) { + addrGroup->removeIncludeZone(zoneId); + } else { + addrGroup->removeExcludeZone(zoneId); + } + } + + ctrl()->setConfEdited(true); + + updateZonesText(include); + }; + + const int zoneCount = zoneListModel()->rowCount(); + for (int zoneId = 1; zoneId <= zoneCount; ++zoneId) { + const auto zoneName = zoneListModel()->zoneNameById(zoneId); + if (zoneName.isEmpty()) + continue; + + auto action = new QAction(zoneName, m_menuZones); + action->setCheckable(true); + action->setData(zoneId); + + connect(action, &QAction::triggered, this, onZoneActionTriggered); + + m_menuZones->addAction(action); + } +} + +void AddressesPage::updateZonesMenu(bool include) +{ + if (m_menuZones->isEmpty()) { + createZonesMenu(); + } + + const auto actions = m_menuZones->actions(); + if (actions.isEmpty()) + return; + + const auto zoneIds = addressGroupZones(include); + + for (auto action : actions) { + const int zoneId = action->data().toInt(); + const bool checked = zoneIds.contains(zoneId); + + action->setChecked(checked); + } +} + +void AddressesPage::updateZonesText(bool include) +{ + if (include) { + m_includeAddresses->labelZones()->setText(zonesText(true)); + } else { + m_excludeAddresses->labelZones()->setText(zonesText(false)); + } +} + +void AddressesPage::updateZonesTextAll() +{ + updateZonesText(true); + updateZonesText(false); +} + +void AddressesPage::setupZones() +{ + m_menuZones = new QMenu(this); + + const auto refreshZonesMenu = [&] { + const bool include = (sender() == m_includeAddresses->btSelectZones()); + + updateZonesMenu(include); + }; + + connect(m_includeAddresses->btSelectZones(), &QPushButton::pressed, this, refreshZonesMenu); + connect(m_excludeAddresses->btSelectZones(), &QPushButton::pressed, this, refreshZonesMenu); + + m_includeAddresses->btSelectZones()->setMenu(m_menuZones); + m_excludeAddresses->btSelectZones()->setMenu(m_menuZones); + + connect(zoneListModel(), &ZoneListModel::zoneRemoved, this, [&](int zoneId) { + for (auto addrGroup : addressGroups()) { + addrGroup->removeIncludeZone(zoneId); + addrGroup->removeExcludeZone(zoneId); + } + }); + connect(zoneListModel(), &ZoneListModel::modelChanged, this, [&] { + clearZonesMenu(); + updateZonesTextAll(); + }); +} + const QList &AddressesPage::addressGroups() const { return conf()->addressGroups(); @@ -242,10 +357,15 @@ AddressGroup *AddressesPage::addressGroupByIndex(int index) const return addressGroups().at(index); } +const QVector &AddressesPage::addressGroupZones(bool include) const +{ + return include ? addressGroup()->includeZones() + : addressGroup()->excludeZones(); +} + QString AddressesPage::zonesText(bool include) const { - const auto zoneIds = zoneListModel()->addressGroupZones( - addressGroup()->id(), include); + const auto zoneIds = addressGroupZones(include); if (zoneIds.isEmpty()) return QString(); diff --git a/src/ui/form/opt/pages/addressespage.h b/src/ui/form/opt/pages/addressespage.h index 94b71c77..d264fe96 100644 --- a/src/ui/form/opt/pages/addressespage.h +++ b/src/ui/form/opt/pages/addressespage.h @@ -40,10 +40,18 @@ private: void setupSplitterButtons(); void updateGroup(); void setupAddressGroup(); + void clearZonesMenu(); + void createZonesMenu(); + void updateZonesMenu(bool include); + void updateZonesText(bool include); + void updateZonesTextAll(); + void setupZones(); const QList &addressGroups() const; AddressGroup *addressGroupByIndex(int index) const; + const QVector &addressGroupZones(bool include) const; + QString zonesText(bool include) const; static QString localNetworks(); @@ -56,6 +64,7 @@ private: AddressesColumn *m_excludeAddresses = nullptr; TextArea2Splitter *m_splitter = nullptr; QPushButton *m_btAddLocals = nullptr; + QMenu *m_menuZones = nullptr; }; #endif // ADDRESSESPAGE_H diff --git a/src/ui/form/opt/pages/basepage.h b/src/ui/form/opt/pages/basepage.h index 5f2d94ef..8cc30c94 100644 --- a/src/ui/form/opt/pages/basepage.h +++ b/src/ui/form/opt/pages/basepage.h @@ -10,6 +10,7 @@ QT_FORWARD_DECLARE_CLASS(QComboBox) QT_FORWARD_DECLARE_CLASS(QGroupBox) QT_FORWARD_DECLARE_CLASS(QLabel) QT_FORWARD_DECLARE_CLASS(QLineEdit) +QT_FORWARD_DECLARE_CLASS(QMenu) QT_FORWARD_DECLARE_CLASS(QPushButton) QT_FORWARD_DECLARE_CLASS(QTabBar) diff --git a/src/ui/model/zonelistmodel.cpp b/src/ui/model/zonelistmodel.cpp index 281e2bc8..c189dd1c 100644 --- a/src/ui/model/zonelistmodel.cpp +++ b/src/ui/model/zonelistmodel.cpp @@ -192,6 +192,7 @@ void ZoneListModel::deleteZone(int zoneId, int row) beginRemoveRows(QModelIndex(), row, row); if (confManager()->deleteZone(zoneId)) { + emit zoneRemoved(zoneId); invalidateRowCache(); removeRow(row); } @@ -199,26 +200,6 @@ void ZoneListModel::deleteZone(int zoneId, int row) endRemoveRows(); } -QVector ZoneListModel::addressGroupZones(qint64 addrGroupId, bool include) -{ - static const char * const sql = - "SELECT zone_id" - " FROM address_group_zone" - " WHERE addr_group_id = ?1 AND include = ?2" - " ORDER BY order_index;" - ; - - QVector list; - SqliteStmt stmt; - if (sqliteDb()->prepare(stmt, sql, {addrGroupId, include})) { - while (stmt.step() == SqliteStmt::StepRow) { - const int id = stmt.columnInt(0); - list.append(id); - } - } - return list; -} - QString ZoneListModel::zoneNameById(int zoneId) { static const char * const sql = diff --git a/src/ui/model/zonelistmodel.h b/src/ui/model/zonelistmodel.h index 384e1d7c..b3fd25b7 100644 --- a/src/ui/model/zonelistmodel.h +++ b/src/ui/model/zonelistmodel.h @@ -71,8 +71,6 @@ public: const QDateTime &lastSuccess); void deleteZone(int zoneId, int row); - QVector addressGroupZones(qint64 addrGroupId, bool include); - QString zoneNameById(int zoneId); QVariant zoneTypeByCode(const QString &typeCode) const; @@ -80,6 +78,9 @@ public: QVariant zoneSourceByCode(const QString &sourceCode) const; const QVariantList &zoneSources() const { return m_zoneSources; } +signals: + void zoneRemoved(int zoneId); + protected: bool updateTableRow(int row) const override; TableRow &tableRow() const override { return m_zoneRow; } diff --git a/src/ui/util/model/tablesqlmodel.cpp b/src/ui/util/model/tablesqlmodel.cpp index 3d519ee2..39ad708a 100644 --- a/src/ui/util/model/tablesqlmodel.cpp +++ b/src/ui/util/model/tablesqlmodel.cpp @@ -45,6 +45,7 @@ void TableSqlModel::invalidateRowCache() { m_rowCount = -1; tableRow().invalidate(); + emit modelChanged(); } void TableSqlModel::updateRowCache(int row) const diff --git a/src/ui/util/model/tablesqlmodel.h b/src/ui/util/model/tablesqlmodel.h index e3e21cea..e9febd23 100644 --- a/src/ui/util/model/tablesqlmodel.h +++ b/src/ui/util/model/tablesqlmodel.h @@ -25,6 +25,9 @@ public: void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; +signals: + void modelChanged(); + public slots: void reset(); void refresh();