// networksettingspage.cpp — Network configuration form (список listen-сокетов) #include "networksettingspage.h" #include "../transport/config_updater.h" #include #include #include #include #include #include #include #include #include #include #include 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::of(&QSpinBox::valueChanged), this, [this](int) { onFieldChanged(); }); connect(m_protoCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int) { onFieldChanged(); }); connect(m_typeCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int) { onFieldChanged(); }); connect(m_interfaceCombo, QOverload::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(); }