diff --git a/src/ui/FortFirewall.pro b/src/ui/FortFirewall.pro index 7ca5c2ef..8573c5f2 100644 --- a/src/ui/FortFirewall.pro +++ b/src/ui/FortFirewall.pro @@ -18,6 +18,7 @@ SOURCES += \ control/controlworker.cpp \ driver/drivermanager.cpp \ driver/driverworker.cpp \ + form/controls/checkspincombo.cpp \ form/controls/controlutil.cpp \ form/controls/tabbar.cpp \ form/graph/axistickerspeed.cpp \ @@ -108,6 +109,7 @@ HEADERS += \ control/controlworker.h \ driver/drivermanager.h \ driver/driverworker.h \ + form/controls/checkspincombo.h \ form/controls/controlutil.h \ form/controls/tabbar.h \ form/graph/axistickerspeed.h \ diff --git a/src/ui/form/controls/checkspincombo.cpp b/src/ui/form/controls/checkspincombo.cpp new file mode 100644 index 00000000..cdf795b3 --- /dev/null +++ b/src/ui/form/controls/checkspincombo.cpp @@ -0,0 +1,85 @@ +#include "checkspincombo.h" + +#include +#include +#include +#include + +CheckSpinCombo::CheckSpinCombo(QWidget *parent) : + QWidget(parent) +{ + setupUi(); +} + +void CheckSpinCombo::setValues(const ValuesList &v) +{ + if (m_values != v) { + m_values = v; + emit valuesChanged(); + } +} + +void CheckSpinCombo::setNames(const QStringList &v) +{ + if (m_names != v) { + m_names = v; + emit namesChanged(); + } +} + +void CheckSpinCombo::setupUi() +{ + auto layout = new QHBoxLayout(); + + m_checkBox = new QCheckBox(); + + setupSpin(); + setupCombo(); + + layout->addWidget(m_checkBox, 1); + layout->addWidget(m_spinBox); + layout->addWidget(m_comboBox); + + this->setLayout(layout); +} + +void CheckSpinCombo::setupSpin() +{ + m_spinBox = new QSpinBox(); + m_spinBox->setMinimumWidth(80); + m_spinBox->setRange(0, 9999); + + connect(m_spinBox, QOverload::of(&QSpinBox::valueChanged), + this, &CheckSpinCombo::updateComboBoxIndex); +} + +void CheckSpinCombo::setupCombo() +{ + m_comboBox = new QComboBox(); + + connect(m_comboBox, QOverload::of(&QComboBox::activated), + this, &CheckSpinCombo::updateSpinBoxValue); + + connect(this, &CheckSpinCombo::namesChanged, [&] { + m_comboBox->clear(); + m_comboBox->addItems(names()); + + updateComboBoxIndex(m_spinBox->value()); + }); +} + +void CheckSpinCombo::updateSpinBoxValue(int index) +{ + m_spinBox->setValue(values().at(index)); +} + +void CheckSpinCombo::updateComboBoxIndex(int value) +{ + m_comboBox->setCurrentIndex(getIndexByValue(value)); +} + +int CheckSpinCombo::getIndexByValue(int value) const +{ + const int index = values().indexOf(value); + return (index <= 0) ? 0 : index; +} diff --git a/src/ui/form/controls/checkspincombo.h b/src/ui/form/controls/checkspincombo.h new file mode 100644 index 00000000..6f05b57c --- /dev/null +++ b/src/ui/form/controls/checkspincombo.h @@ -0,0 +1,51 @@ +#ifndef CHECKSPINCOMBO_H +#define CHECKSPINCOMBO_H + +#include + +QT_FORWARD_DECLARE_CLASS(QCheckBox) +QT_FORWARD_DECLARE_CLASS(QComboBox) +QT_FORWARD_DECLARE_CLASS(QSpinBox) + +using ValuesList = QVector; + +class CheckSpinCombo : public QWidget +{ + Q_OBJECT + +public: + explicit CheckSpinCombo(QWidget *parent = nullptr); + + const ValuesList &values() const { return m_values; } + void setValues(const ValuesList &v); + + QStringList names() const { return m_names; } + void setNames(const QStringList &v); + + QCheckBox *checkBox() const { return m_checkBox; } + QSpinBox *spinBox() const { return m_spinBox; } + QComboBox *comboBox() const { return m_comboBox; } + +signals: + void valuesChanged(); + void namesChanged(); + +private: + void setupUi(); + void setupSpin(); + void setupCombo(); + + void updateSpinBoxValue(int index); + void updateComboBoxIndex(int value); + int getIndexByValue(int value) const; + +private: + ValuesList m_values; + QStringList m_names; + + QCheckBox *m_checkBox = nullptr; + QSpinBox *m_spinBox = nullptr; + QComboBox *m_comboBox = nullptr; +}; + +#endif // CHECKSPINCOMBO_H diff --git a/src/ui/form/opt/pages/addressespage.cpp b/src/ui/form/opt/pages/addressespage.cpp index c02f13fb..8a8de142 100644 --- a/src/ui/form/opt/pages/addressespage.cpp +++ b/src/ui/form/opt/pages/addressespage.cpp @@ -165,20 +165,30 @@ void AddressesPage::retranslateAddressesPlaceholderText() m_excludeAddresses->editIpText()->setPlaceholderText(placeholderText); } +void AddressesPage::refreshGroup() +{ + m_includeAddresses->cbUseAll()->setChecked(addressGroup()->includeAll()); + m_includeAddresses->editIpText()->setPlainText(addressGroup()->includeText()); + + m_excludeAddresses->cbUseAll()->setChecked(addressGroup()->excludeAll()); + m_excludeAddresses->editIpText()->setPlainText(addressGroup()->excludeText()); +} + void AddressesPage::setupAddressGroup() { const auto refreshAddressGroup = [&] { const int tabIndex = m_tabBar->currentIndex(); - m_addressGroup = conf()->addressGroupsList().at(tabIndex); + m_addressGroup = addressGroupByIndex(tabIndex); - m_includeAddresses->cbUseAll()->setChecked(addressGroup()->includeAll()); - m_includeAddresses->editIpText()->setPlainText(addressGroup()->includeText()); - - m_excludeAddresses->cbUseAll()->setChecked(addressGroup()->excludeAll()); - m_excludeAddresses->editIpText()->setPlainText(addressGroup()->excludeText()); + refreshGroup(); }; refreshAddressGroup(); connect(m_tabBar, &QTabBar::currentChanged, this, refreshAddressGroup); } + +AddressGroup *AddressesPage::addressGroupByIndex(int index) const +{ + return conf()->addressGroupsList().at(index); +} diff --git a/src/ui/form/opt/pages/addressespage.h b/src/ui/form/opt/pages/addressespage.h index 86b40e62..2bfbd2da 100644 --- a/src/ui/form/opt/pages/addressespage.h +++ b/src/ui/form/opt/pages/addressespage.h @@ -14,8 +14,6 @@ public: explicit AddressesPage(OptionsController *ctrl = nullptr, QWidget *parent = nullptr); - AddressGroup *addressGroup() const { return m_addressGroup; } - protected slots: void onRetranslateUi() override; @@ -26,8 +24,12 @@ private: void setupExcludeAddresses(); void setupAddressesUseAllEnabled(); void retranslateAddressesPlaceholderText(); + void refreshGroup(); void setupAddressGroup(); + AddressGroup *addressGroup() const { return m_addressGroup; } + AddressGroup *addressGroupByIndex(int index) const; + private: AddressGroup *m_addressGroup = nullptr; diff --git a/src/ui/form/opt/pages/applicationspage.cpp b/src/ui/form/opt/pages/applicationspage.cpp index 473e85eb..674d5047 100644 --- a/src/ui/form/opt/pages/applicationspage.cpp +++ b/src/ui/form/opt/pages/applicationspage.cpp @@ -3,15 +3,30 @@ #include #include #include +#include #include +#include #include +#include #include "../../../conf/appgroup.h" #include "../../../conf/firewallconf.h" +#include "../../../util/net/netutil.h" +#include "../../controls/checkspincombo.h" #include "../../controls/controlutil.h" #include "../../controls/tabbar.h" #include "../optionscontroller.h" +namespace { + +const ValuesList speedLimitValues = { + 10, 0, 20, 30, 50, 75, 100, 150, 200, 300, 500, 900, + 1024, qRound(1.5 * 1024), 2 * 1024, 3 * 1024, 5 * 1024, qRound(7.5 * 1024), + 10 * 1024, 15 * 1024, 20 * 1024, 30 * 1024, 50 * 1024 +}; + +} + ApplicationsPage::ApplicationsPage(OptionsController *ctrl, QWidget *parent) : BasePage(ctrl, parent) @@ -27,6 +42,12 @@ void ApplicationsPage::onRetranslateUi() m_cbBlockAll->setText(tr("Block All")); m_cbAllowAll->setText(tr("Allow All")); + + m_btGroupOptions->setText(tr("Options")); + m_cscLimitIn->checkBox()->setText(tr("Download speed limit, KiB/s:")); + m_cscLimitOut->checkBox()->setText(tr("Upload speed limit, KiB/s:")); + retranslateGroupLimits(); + m_cbFragmentPacket->setText(tr("Fragment first TCP packet")); } void ApplicationsPage::setupUi() @@ -41,6 +62,12 @@ void ApplicationsPage::setupUi() setupTabBar(); layout->addWidget(m_tabBar); + // App Group + auto groupHeader = setupGroupHeader(); + layout->addLayout(groupHeader); + + setupAppGroup(); + layout->addStretch(); this->setLayout(layout); @@ -107,7 +134,7 @@ void ApplicationsPage::setupRenameGroup() const int tabIndex = m_tabBar->currentIndex(); m_tabBar->setTabText(tabIndex, text); - appGroup(tabIndex)->setName(text); + appGroup()->setName(text); resetGroupName(); ctrl()->setConfEdited(true); @@ -159,7 +186,7 @@ void ApplicationsPage::setupTabBar() m_tabBar->removeTab(index); } else { // Reset alone tab to default one - m_tabBar->setTabText(0, appGroup(0)->name()); + m_tabBar->setTabText(0, appGroup()->name()); } ctrl()->setConfEdited(true); @@ -177,14 +204,162 @@ int ApplicationsPage::addTab(const QString &text) return tabIndex; } +QLayout *ApplicationsPage::setupGroupHeader() +{ + auto layout = new QHBoxLayout(); + + setupGroupOptions(); + + layout->addWidget(m_btGroupOptions); + layout->addStretch(); + + return layout; +} + +void ApplicationsPage::setupGroupOptions() +{ + m_btGroupOptions = new QPushButton(); + m_btGroupOptions->setIcon(QIcon(":/images/application_key.png")); + + setupGroupLimitIn(); + setupGroupLimitOut(); + setupGroupFragmentPacket(); + + // Menu + auto menu = new QMenu(m_btGroupOptions); + + auto waLimitIn = new QWidgetAction(this); + waLimitIn->setDefaultWidget(m_cscLimitIn); + menu->addAction(waLimitIn); + + auto waLimitOut = new QWidgetAction(this); + waLimitOut->setDefaultWidget(m_cscLimitOut); + menu->addAction(waLimitOut); + + menu->addSeparator(); + + auto waFragmentPacket = new QWidgetAction(this); + waFragmentPacket->setDefaultWidget(m_cbFragmentPacket); + menu->addAction(waFragmentPacket); + + m_btGroupOptions->setMenu(menu); +} + +void ApplicationsPage::setupGroupLimitIn() +{ + m_cscLimitIn = new CheckSpinCombo(); + m_cscLimitIn->spinBox()->setRange(0, 99999); + m_cscLimitIn->setValues(speedLimitValues); + + connect(m_cscLimitIn->checkBox(), &QCheckBox::toggled, [&](bool checked) { + if (appGroup()->limitInEnabled() == checked) + return; + + appGroup()->setLimitInEnabled(checked); + + ctrl()->setConfEdited(true); + }); + connect(m_cscLimitIn->spinBox(), QOverload::of(&QSpinBox::valueChanged), [&](int value) { + const auto speedLimit = quint32(value); + + if (appGroup()->speedLimitIn() == speedLimit) + return; + + appGroup()->setSpeedLimitIn(speedLimit); + + ctrl()->setConfEdited(true); + }); +} + +void ApplicationsPage::setupGroupLimitOut() +{ + m_cscLimitOut = new CheckSpinCombo(); + m_cscLimitOut->spinBox()->setRange(0, 99999); + m_cscLimitOut->setValues(speedLimitValues); + + connect(m_cscLimitOut->checkBox(), &QCheckBox::toggled, [&](bool checked) { + if (appGroup()->limitOutEnabled() == checked) + return; + + appGroup()->setLimitOutEnabled(checked); + + ctrl()->setConfEdited(true); + }); + connect(m_cscLimitOut->spinBox(), QOverload::of(&QSpinBox::valueChanged), [&](int value) { + const auto speedLimit = quint32(value); + + if (appGroup()->speedLimitOut() == speedLimit) + return; + + appGroup()->setSpeedLimitOut(speedLimit); + + ctrl()->setConfEdited(true); + }); +} + +void ApplicationsPage::setupGroupFragmentPacket() +{ + m_cbFragmentPacket = ControlUtil::createCheckBox(false, [&](bool checked) { + if (appGroup()->fragmentPacket() == checked) + return; + + appGroup()->setFragmentPacket(checked); + + ctrl()->setConfEdited(true); + }); +} + +void ApplicationsPage::retranslateGroupLimits() +{ + QStringList list; + + list.append(tr("Custom")); + list.append(tr("Disabled")); + + int index = 0; + for (const int v : speedLimitValues) { + if (++index > 2) { + list.append(formatSpeed(v)); + } + } + + m_cscLimitIn->setNames(list); + m_cscLimitOut->setNames(list); +} + +void ApplicationsPage::refreshGroup() +{ + m_cscLimitIn->checkBox()->setChecked(appGroup()->limitInEnabled()); + m_cscLimitIn->spinBox()->setValue(int(appGroup()->speedLimitIn())); + + m_cscLimitOut->checkBox()->setChecked(appGroup()->limitOutEnabled()); + m_cscLimitOut->spinBox()->setValue(int(appGroup()->speedLimitOut())); + + m_cbFragmentPacket->setChecked(appGroup()->fragmentPacket()); +} + +void ApplicationsPage::setupAppGroup() +{ + const auto refreshAppGroup = [&] { + const int tabIndex = m_tabBar->currentIndex(); + m_appGroup = appGroupByIndex(tabIndex); + + refreshGroup(); + }; + + refreshAppGroup(); + + connect(m_tabBar, &QTabBar::currentChanged, this, refreshAppGroup); +} + int ApplicationsPage::appGroupsCount() const { return conf()->appGroupsList().size(); } -AppGroup *ApplicationsPage::appGroup(int tabIndex) const +AppGroup *ApplicationsPage::appGroupByIndex(int index) const { - return conf()->appGroupsList().at(tabIndex); + return conf()->appGroupsList().at(index); } void ApplicationsPage::resetGroupName() @@ -192,3 +367,8 @@ void ApplicationsPage::resetGroupName() m_editGroupName->setText(QString()); m_editGroupName->setFocus(); } + +QString ApplicationsPage::formatSpeed(int kbytes) +{ + return NetUtil::formatSpeed(quint32(kbytes * 1024)); +} diff --git a/src/ui/form/opt/pages/applicationspage.h b/src/ui/form/opt/pages/applicationspage.h index 7c01c3ec..216d26cb 100644 --- a/src/ui/form/opt/pages/applicationspage.h +++ b/src/ui/form/opt/pages/applicationspage.h @@ -5,6 +5,7 @@ QT_FORWARD_DECLARE_CLASS(AppGroup) QT_FORWARD_DECLARE_CLASS(TabBar) +QT_FORWARD_DECLARE_CLASS(CheckSpinCombo) class ApplicationsPage : public BasePage { @@ -25,18 +26,35 @@ private: void setupBlockAllowAll(); void setupTabBar(); int addTab(const QString &text); + QLayout *setupGroupHeader(); + void setupGroupOptions(); + void setupGroupLimitIn(); + void setupGroupLimitOut(); + void setupGroupFragmentPacket(); + void retranslateGroupLimits(); + void refreshGroup(); + void setupAppGroup(); int appGroupsCount() const; - AppGroup *appGroup(int tabIndex) const; + AppGroup *appGroup() const { return m_appGroup; } + AppGroup *appGroupByIndex(int index) const; void resetGroupName(); + static QString formatSpeed(int kbytes); + private: + AppGroup *m_appGroup = nullptr; + QLineEdit *m_editGroupName = nullptr; QPushButton *m_btAddGroup = nullptr; QPushButton *m_btRenameGroup = nullptr; QCheckBox *m_cbBlockAll = nullptr; QCheckBox *m_cbAllowAll = nullptr; TabBar *m_tabBar = nullptr; + QPushButton *m_btGroupOptions = nullptr; + CheckSpinCombo *m_cscLimitIn = nullptr; + CheckSpinCombo *m_cscLimitOut = nullptr; + QCheckBox *m_cbFragmentPacket = nullptr; }; #endif // APPLICATIONSPAGE_H diff --git a/src/ui/form/opt/pages/apps/appscolumn.cpp b/src/ui/form/opt/pages/apps/appscolumn.cpp index dd022b32..37f4ca5d 100644 --- a/src/ui/form/opt/pages/apps/appscolumn.cpp +++ b/src/ui/form/opt/pages/apps/appscolumn.cpp @@ -1,6 +1,6 @@ #include "appscolumn.h" -AppsColumn::AppsColumn(QObject *parent) : - QObject(parent) +AppsColumn::AppsColumn(QWidget *parent) : + QWidget(parent) { } diff --git a/src/ui/form/opt/pages/apps/appscolumn.h b/src/ui/form/opt/pages/apps/appscolumn.h index 354d7b16..81705a89 100644 --- a/src/ui/form/opt/pages/apps/appscolumn.h +++ b/src/ui/form/opt/pages/apps/appscolumn.h @@ -1,14 +1,14 @@ #ifndef APPSCOLUMN_H #define APPSCOLUMN_H -#include +#include -class AppsColumn : public QObject +class AppsColumn : public QWidget { Q_OBJECT public: - explicit AppsColumn(QObject *parent = nullptr); + explicit AppsColumn(QWidget *parent = nullptr); };