You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
319 lines
11 KiB
319 lines
11 KiB
// networksettingspage.cpp — Network configuration form (список listen-сокетов) |
|
#include "networksettingspage.h" |
|
#include "../transport/config_updater.h" |
|
#include <QVBoxLayout> |
|
#include <QHBoxLayout> |
|
#include <QGridLayout> |
|
#include <QLabel> |
|
#include <QCheckBox> |
|
#include <QLineEdit> |
|
#include <QListWidget> |
|
#include <QPushButton> |
|
#include <QNetworkInterface> |
|
#include <QFile> |
|
#include <QTextStream> |
|
|
|
static const QString AUTO_IP_LABEL = "Auto (0.0.0.0)"; |
|
|
|
NetworkSettingsPage::NetworkSettingsPage(QWidget* parent) |
|
: QWidget(parent) |
|
{ |
|
auto* layout = new QVBoxLayout(this); |
|
layout->setSpacing(12); |
|
layout->setContentsMargins(0, 0, 0, 0); |
|
|
|
auto* title = new QLabel("Listen Sockets", this); |
|
title->setStyleSheet("font-weight: bold; font-size: 13px;"); |
|
layout->addWidget(title); |
|
|
|
m_autoSocketsCheck = new QCheckBox("Auto sockets (UDP+TCP from interfaces)", this); |
|
layout->addWidget(m_autoSocketsCheck); |
|
|
|
m_serverList = new QListWidget(this); |
|
m_serverList->setMinimumHeight(110); |
|
layout->addWidget(m_serverList); |
|
|
|
auto* btnRow = new QHBoxLayout(); |
|
m_addBtn = new QPushButton("Add", this); |
|
m_removeBtn = new QPushButton("Remove", this); |
|
btnRow->addWidget(m_addBtn); |
|
btnRow->addWidget(m_removeBtn); |
|
btnRow->addStretch(); |
|
layout->addLayout(btnRow); |
|
|
|
// --- форма правки выбранного сокета --- |
|
auto* formLayout = new QGridLayout(); |
|
|
|
formLayout->addWidget(new QLabel("Name:", this), 0, 0); |
|
m_nameEdit = new QLineEdit(this); |
|
m_nameEdit->setMinimumWidth(180); |
|
formLayout->addWidget(m_nameEdit, 0, 1); |
|
|
|
formLayout->addWidget(new QLabel("IP Address:", this), 1, 0); |
|
m_ipCombo = new QComboBox(this); |
|
m_ipCombo->setEditable(true); |
|
m_ipCombo->setMinimumWidth(180); |
|
formLayout->addWidget(m_ipCombo, 1, 1); |
|
|
|
formLayout->addWidget(new QLabel("Port:", this), 2, 0); |
|
m_portSpin = new QSpinBox(this); |
|
m_portSpin->setRange(1, 65535); |
|
m_portSpin->setValue(1333); |
|
formLayout->addWidget(m_portSpin, 2, 1); |
|
|
|
formLayout->addWidget(new QLabel("Protocol:", this), 3, 0); |
|
m_protoCombo = new QComboBox(this); |
|
m_protoCombo->addItems({"UDP", "TCP"}); |
|
formLayout->addWidget(m_protoCombo, 3, 1); |
|
|
|
formLayout->addWidget(new QLabel("Type:", this), 4, 0); |
|
m_typeCombo = new QComboBox(this); |
|
m_typeCombo->addItems({"public", "nat", "private", "local"}); |
|
formLayout->addWidget(m_typeCombo, 4, 1); |
|
|
|
formLayout->addWidget(new QLabel("Interface:", this), 5, 0); |
|
m_interfaceCombo = new QComboBox(this); |
|
m_interfaceCombo->setMinimumWidth(180); |
|
formLayout->addWidget(m_interfaceCombo, 5, 1); |
|
|
|
layout->addLayout(formLayout); |
|
|
|
auto* note = new QLabel("Changes take effect after restart.", this); |
|
note->setStyleSheet("color: gray; font-size: 11px;"); |
|
layout->addWidget(note); |
|
layout->addStretch(); |
|
|
|
populateInterfaces(); |
|
populateAddresses(); |
|
|
|
connect(m_autoSocketsCheck, &QCheckBox::toggled, this, &NetworkSettingsPage::onAutoSocketsToggled); |
|
connect(m_addBtn, &QPushButton::clicked, this, &NetworkSettingsPage::onAddServer); |
|
connect(m_removeBtn, &QPushButton::clicked, this, &NetworkSettingsPage::onRemoveServer); |
|
connect(m_serverList, &QListWidget::currentRowChanged, this, [this](int) { onServerSelected(); }); |
|
|
|
connect(m_nameEdit, &QLineEdit::textChanged, this, [this](const QString&) { onFieldChanged(); }); |
|
connect(m_ipCombo, &QComboBox::editTextChanged, this, [this](const QString&) { onFieldChanged(); }); |
|
connect(m_portSpin, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int) { onFieldChanged(); }); |
|
connect(m_protoCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int) { onFieldChanged(); }); |
|
connect(m_typeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int) { onFieldChanged(); }); |
|
connect(m_interfaceCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int) { onFieldChanged(); }); |
|
} |
|
|
|
static void splitAddr(const QString& addr, QString& ip, int& port) { |
|
int lastColon = addr.lastIndexOf(':'); |
|
if (lastColon > 0) { |
|
ip = addr.left(lastColon); |
|
port = addr.mid(lastColon + 1).toInt(); |
|
} else { |
|
ip = addr; |
|
port = 0; |
|
} |
|
} |
|
|
|
void NetworkSettingsPage::populateInterfaces() { |
|
m_interfaceCombo->addItem("Auto (any)"); |
|
for (const QNetworkInterface& iface : QNetworkInterface::allInterfaces()) { |
|
if (iface.flags() & QNetworkInterface::IsLoopBack) continue; |
|
if (!(iface.flags() & QNetworkInterface::IsUp)) continue; |
|
m_interfaceCombo->addItem(iface.name()); |
|
} |
|
} |
|
|
|
void NetworkSettingsPage::populateAddresses() { |
|
m_ipCombo->addItem(AUTO_IP_LABEL); |
|
for (const QNetworkInterface& iface : QNetworkInterface::allInterfaces()) { |
|
if (iface.flags() & QNetworkInterface::IsLoopBack) continue; |
|
if (!(iface.flags() & QNetworkInterface::IsUp)) continue; |
|
for (const QNetworkAddressEntry& entry : iface.addressEntries()) { |
|
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) |
|
m_ipCombo->addItem(entry.ip().toString()); |
|
} |
|
} |
|
} |
|
|
|
void NetworkSettingsPage::applyAutoSocketsState() { |
|
bool manual = !m_autoSocketsCheck->isChecked(); |
|
m_serverList->setEnabled(manual); |
|
m_addBtn->setEnabled(manual); |
|
m_removeBtn->setEnabled(manual); |
|
m_nameEdit->setEnabled(manual); |
|
m_ipCombo->setEnabled(manual); |
|
m_portSpin->setEnabled(manual); |
|
m_protoCombo->setEnabled(manual); |
|
m_typeCombo->setEnabled(manual); |
|
m_interfaceCombo->setEnabled(manual); |
|
} |
|
|
|
void NetworkSettingsPage::refreshServerList(int selectRow) { |
|
m_updating = true; |
|
m_serverList->clear(); |
|
for (const ConfigServer& s : m_servers) { |
|
m_serverList->addItem(QString("%1 %2 (%3)").arg(s.name, s.addr, s.transport)); |
|
} |
|
if (selectRow >= 0 && selectRow < m_serverList->count()) |
|
m_serverList->setCurrentRow(selectRow); |
|
else if (m_serverList->count() > 0) |
|
m_serverList->setCurrentRow(0); |
|
m_updating = false; |
|
} |
|
|
|
void NetworkSettingsPage::loadFormFromSelected() { |
|
int row = m_serverList->currentRow(); |
|
if (row < 0 || row >= m_servers.size()) return; |
|
const ConfigServer& s = m_servers[row]; |
|
|
|
m_updating = true; |
|
m_nameEdit->setText(s.name); |
|
QString ip; int port = 0; |
|
splitAddr(s.addr, ip, port); |
|
if (ip == "0.0.0.0") ip = AUTO_IP_LABEL; |
|
m_ipCombo->setCurrentText(ip); |
|
m_portSpin->setValue(port > 0 ? port : 1333); |
|
m_protoCombo->setCurrentIndex(s.transport == "tcp" ? 1 : 0); |
|
int typeIdx = m_typeCombo->findText(s.type); |
|
m_typeCombo->setCurrentIndex(typeIdx >= 0 ? typeIdx : 0); |
|
int ifIdx = m_interfaceCombo->findText(s.netif); |
|
m_interfaceCombo->setCurrentIndex(ifIdx >= 0 ? ifIdx : 0); |
|
m_updating = false; |
|
} |
|
|
|
void NetworkSettingsPage::updateSelectedServerFromForm() { |
|
int row = m_serverList->currentRow(); |
|
if (row < 0 || row >= m_servers.size()) return; |
|
|
|
ConfigServer& s = m_servers[row]; |
|
s.name = m_nameEdit->text().trimmed(); |
|
|
|
QString ip = m_ipCombo->currentText().trimmed(); |
|
if (ip == AUTO_IP_LABEL) ip = "0.0.0.0"; |
|
s.addr = ip + ":" + QString::number(m_portSpin->value()); |
|
|
|
s.transport = (m_protoCombo->currentIndex() == 1) ? "tcp" : "udp"; |
|
s.type = m_typeCombo->currentText().trimmed(); |
|
QString iface = m_interfaceCombo->currentText(); |
|
s.netif = iface.startsWith("Auto") ? QString() : iface; |
|
} |
|
|
|
void NetworkSettingsPage::updateSelectedListItemText() { |
|
int row = m_serverList->currentRow(); |
|
if (row < 0 || row >= m_servers.size()) return; |
|
const ConfigServer& s = m_servers[row]; |
|
m_updating = true; |
|
m_serverList->item(row)->setText(QString("%1 %2 (%3)").arg(s.name, s.addr, s.transport)); |
|
m_updating = false; |
|
} |
|
|
|
void NetworkSettingsPage::loadFromConfig(const QString& configPath) { |
|
m_configPath = configPath; |
|
m_servers.clear(); |
|
|
|
QFile f(configPath); |
|
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { |
|
QString section; |
|
ConfigServer cur; |
|
bool inServer = false; |
|
QTextStream in(&f); |
|
while (!in.atEnd()) { |
|
QString line = in.readLine().trimmed(); |
|
if (line.startsWith('[') && line.endsWith(']')) { |
|
if (inServer) m_servers.append(cur); |
|
inServer = false; |
|
section = line.mid(1, line.length() - 2); |
|
if (section.startsWith("server:")) { |
|
cur = ConfigServer(); |
|
cur.name = section.mid(7); |
|
cur.transport = "udp"; |
|
cur.type = "public"; |
|
inServer = true; |
|
} |
|
continue; |
|
} |
|
if (!inServer || !line.contains('=')) continue; |
|
QString key = line.section('=', 0, 0).trimmed(); |
|
QString val = line.section('=', 1).trimmed(); |
|
if (key == "addr") cur.addr = val; |
|
else if (key == "transport") cur.transport = val; |
|
else if (key == "type") cur.type = val; |
|
else if (key == "netif") cur.netif = val; |
|
} |
|
if (inServer) m_servers.append(cur); |
|
f.close(); |
|
} |
|
|
|
/* auto_sockets из [global] */ |
|
bool autoSockets = false; |
|
f.setFileName(configPath); |
|
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { |
|
QString section; |
|
QTextStream in(&f); |
|
while (!in.atEnd()) { |
|
QString line = in.readLine().trimmed(); |
|
if (line.startsWith('[') && line.endsWith(']')) { |
|
section = line.mid(1, line.length() - 2); |
|
continue; |
|
} |
|
if ((section == "global" || section.isEmpty()) && line.startsWith("auto_sockets=")) { |
|
QString v = line.section('=', 1).trimmed(); |
|
autoSockets = (v.compare("yes", Qt::CaseInsensitive) == 0 || v == "1"); |
|
break; |
|
} |
|
} |
|
f.close(); |
|
} |
|
m_autoSocketsCheck->setChecked(autoSockets); |
|
|
|
refreshServerList(); |
|
applyAutoSocketsState(); |
|
} |
|
|
|
void NetworkSettingsPage::saveToConfig(const QString& configPath) const { |
|
/* 1. Серверные секции — полная перезапись */ |
|
config_rewrite_servers(configPath, m_servers); |
|
|
|
/* 2. auto_sockets */ |
|
config_update_option(configPath, "global", "auto_sockets", |
|
m_autoSocketsCheck->isChecked() ? "yes" : "no", |
|
CONFIG_UPDATE_REPLACE); |
|
} |
|
|
|
void NetworkSettingsPage::onAutoSocketsToggled(bool /*on*/) { |
|
applyAutoSocketsState(); |
|
} |
|
|
|
void NetworkSettingsPage::onAddServer() { |
|
int n = m_servers.size() + 1; |
|
QString name; |
|
for (;;) { |
|
name = QString("srv%1").arg(n); |
|
bool used = false; |
|
for (const ConfigServer& s : m_servers) if (s.name == name) { used = true; break; } |
|
if (!used) break; |
|
n++; |
|
} |
|
ConfigServer s; |
|
s.name = name; |
|
s.addr = "0.0.0.0:1333"; |
|
s.transport = "udp"; |
|
s.type = "public"; |
|
m_servers.append(s); |
|
refreshServerList(m_servers.size() - 1); |
|
loadFormFromSelected(); |
|
} |
|
|
|
void NetworkSettingsPage::onRemoveServer() { |
|
int row = m_serverList->currentRow(); |
|
if (row < 0 || row >= m_servers.size()) return; |
|
m_servers.removeAt(row); |
|
refreshServerList(row); |
|
loadFormFromSelected(); |
|
} |
|
|
|
void NetworkSettingsPage::onServerSelected() { |
|
loadFormFromSelected(); |
|
} |
|
|
|
void NetworkSettingsPage::onFieldChanged() { |
|
if (m_updating) return; |
|
updateSelectedServerFromForm(); |
|
updateSelectedListItemText(); |
|
}
|
|
|