feat: add network monitoring and IP address management
- introduce NetworkMonitor class to track network addresses change - replace QLineEdit with QComboBox for interface selection in settings - implement automatic IP detection and suggestion logic - add signal-slot connections for real-time network updates Log: add network monitoring and IP address management.
This commit is contained in:
@ -54,6 +54,8 @@ add_library(${target} STATIC
|
||||
core/CommandProcess.h
|
||||
core/CoreProcess.cpp
|
||||
core/CoreProcess.h
|
||||
core/NetworkMonitor.cpp
|
||||
core/NetworkMonitor.h
|
||||
core/ServerConnection.cpp
|
||||
core/ServerConnection.h
|
||||
core/ServerMessage.cpp
|
||||
|
||||
@ -86,7 +86,9 @@ MainWindow::MainWindow()
|
||||
m_actionSettings{new QAction(this)},
|
||||
m_actionStartCore{new QAction(this)},
|
||||
m_actionRestartCore{new QAction(this)},
|
||||
m_actionStopCore{new QAction(this)}
|
||||
m_actionStopCore{new QAction(this)},
|
||||
m_networkMonitor{new NetworkMonitor(this)},
|
||||
m_currentIPValid(true)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
@ -165,6 +167,11 @@ MainWindow::MainWindow()
|
||||
}
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
// Stop network monitoring
|
||||
if (m_networkMonitor) {
|
||||
m_networkMonitor->stopMonitoring();
|
||||
}
|
||||
|
||||
m_guiDupeChecker->close();
|
||||
m_coreProcess.cleanup();
|
||||
}
|
||||
@ -198,8 +205,6 @@ void MainWindow::setupControls()
|
||||
|
||||
ui->btnConfigureServer->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
|
||||
|
||||
updateNetworkInfo();
|
||||
|
||||
if (Settings::value(Settings::Core::LastVersion).toString() != kVersion) {
|
||||
Settings::setValue(Settings::Core::LastVersion, kVersion);
|
||||
}
|
||||
@ -334,6 +339,8 @@ void MainWindow::connectSlots()
|
||||
connect(ui->btnEditName, &QPushButton::clicked, this, &MainWindow::showHostNameEditor);
|
||||
|
||||
connect(ui->lineEditName, &QLineEdit::editingFinished, this, &MainWindow::setHostName);
|
||||
|
||||
connect(m_networkMonitor, &NetworkMonitor::ipAddressesChanged, this, &MainWindow::updateIpLabel);
|
||||
}
|
||||
|
||||
void MainWindow::toggleLogVisible(bool visible)
|
||||
@ -425,6 +432,12 @@ void MainWindow::coreProcessError(CoreProcess::Error error)
|
||||
|
||||
void MainWindow::startCore()
|
||||
{
|
||||
// Save current IP state when server starts
|
||||
if (m_coreProcess.mode() == CoreMode::Server) {
|
||||
m_serverStartIPs = m_networkMonitor->getAvailableIPv4Addresses();
|
||||
m_serverStartSuggestedIP = m_networkMonitor->getSuggestedIPv4Address();
|
||||
}
|
||||
|
||||
m_coreProcess.start();
|
||||
m_actionStartCore->setVisible(false);
|
||||
m_actionRestartCore->setVisible(true);
|
||||
@ -521,7 +534,6 @@ void MainWindow::coreModeToggled()
|
||||
|
||||
void MainWindow::updateModeControls(bool serverMode)
|
||||
{
|
||||
ui->lblIpAddresses->setVisible(serverMode);
|
||||
ui->serverOptions->setVisible(serverMode);
|
||||
ui->clientOptions->setVisible(!serverMode);
|
||||
ui->lblNoMode->setVisible(false);
|
||||
@ -534,6 +546,15 @@ void MainWindow::updateModeControls(bool serverMode)
|
||||
updateModeControlLabels();
|
||||
|
||||
toggleCanRunCore((!serverMode && !ui->lineHostname->text().isEmpty()) || serverMode);
|
||||
|
||||
ui->lblIpAddresses->setVisible(serverMode);
|
||||
if (serverMode) {
|
||||
// Initialize network monitoring
|
||||
updateNetworkInfo();
|
||||
m_networkMonitor->startMonitoring();
|
||||
} else {
|
||||
m_networkMonitor->stopMonitoring();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::updateModeControlLabels()
|
||||
@ -590,43 +611,7 @@ void MainWindow::updateSecurityIcon(bool visible)
|
||||
|
||||
void MainWindow::updateNetworkInfo()
|
||||
{
|
||||
static const auto colorText = QStringLiteral(R"(<span style="color:%1;">%2</span>)");
|
||||
|
||||
QStringList ipList;
|
||||
QString suggestedAddress;
|
||||
|
||||
bool hinted = false;
|
||||
|
||||
const auto addresses = QNetworkInterface::allAddresses();
|
||||
for (const auto &address : addresses) {
|
||||
if (address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress(QHostAddress::LocalHost) &&
|
||||
!address.isInSubnet(QHostAddress::parseSubnet("169.254.0.0/16"))) {
|
||||
// usually 192.168.x.x is a useful ip for the user, so indicate
|
||||
// this by coloring it in the "link" color
|
||||
if (!hinted && address.isInSubnet(QHostAddress::parseSubnet("192.168/16"))) {
|
||||
suggestedAddress = address.toString();
|
||||
ipList.append(colorText.arg(palette().link().color().name(), suggestedAddress));
|
||||
hinted = true;
|
||||
} else {
|
||||
ipList.append(address.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ipList.isEmpty()) {
|
||||
ui->lblIpAddresses->setText(colorText.arg(palette().linkVisited().color().name(), tr("No IP Detected")));
|
||||
ui->lblIpAddresses->setToolTip(tr("Unable to detect an IP address. Check your network connection is active."));
|
||||
return;
|
||||
}
|
||||
|
||||
ui->lblIpAddresses->setText(tr("Suggested IP: %1").arg(suggestedAddress.isEmpty() ? ipList.first() : suggestedAddress)
|
||||
);
|
||||
|
||||
if (auto toolTipBase = tr("<p>If connecting via the hostname fails, try %1</p>"); ipList.count() < 2) {
|
||||
ui->lblIpAddresses->setToolTip(toolTipBase.arg(tr("the suggested IP.")));
|
||||
} else {
|
||||
ui->lblIpAddresses->setToolTip(toolTipBase.arg(tr("one of the following IPs:<br/>%1").arg(ipList.join("<br/>"))));
|
||||
}
|
||||
updateIpLabel(m_networkMonitor->getAvailableIPv4Addresses());
|
||||
}
|
||||
|
||||
void MainWindow::serverConnectionConfigureClient(const QString &clientName)
|
||||
@ -915,10 +900,12 @@ void MainWindow::updateStatus()
|
||||
break;
|
||||
|
||||
case Stopped:
|
||||
updateNetworkInfo();
|
||||
setStatus(tr("%1 is not running").arg(kAppName));
|
||||
break;
|
||||
|
||||
case Started: {
|
||||
updateNetworkInfo();
|
||||
switch (connection) {
|
||||
using enum CoreConnectionState;
|
||||
|
||||
@ -1258,3 +1245,92 @@ void MainWindow::handleNewClientPromptRequest(const QString &clientName, bool us
|
||||
bool result = deskflow::gui::messages::showNewClientPrompt(this, clientName, usePeerAuth);
|
||||
m_serverConnection.handleNewClientResult(clientName, result);
|
||||
}
|
||||
|
||||
void MainWindow::updateIpLabel(const QList<QHostAddress> &addresses)
|
||||
{
|
||||
if (m_coreProcess.mode() != CoreMode::Server) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const auto colorText = QStringLiteral(R"(<span style="color:%1;">%2</span>)");
|
||||
|
||||
if (addresses.isEmpty()) {
|
||||
ui->lblIpAddresses->setText(colorText.arg(palette().linkVisited().color().name(), tr("No IP Detected")));
|
||||
ui->lblIpAddresses->setToolTip(tr("Unable to detect an IP address. Check your network connection is active."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all available IPs for tooltip
|
||||
QStringList ipList;
|
||||
for (const auto &address : addresses) {
|
||||
ipList.append(address.toString());
|
||||
}
|
||||
|
||||
QString labelText;
|
||||
QString toolTipText;
|
||||
|
||||
// If we have a fixed IP we will use it
|
||||
if (const auto ip = Settings::value(Settings::Core::Interface).toString(); !ip.isEmpty()) {
|
||||
labelText = tr("Using IP: ");
|
||||
toolTipText = tr("Selected as the interface in settings.");
|
||||
if (ipList.contains(ip, Qt::CaseInsensitive)) {
|
||||
labelText.append(ip);
|
||||
} else {
|
||||
labelText.append(colorText.arg(palette().linkVisited().color().name(), ip));
|
||||
toolTipText.append(tr("\nInterface is not active. Unable to start server."));
|
||||
}
|
||||
} else {
|
||||
labelText = tr("Suggested IP: ");
|
||||
toolTipText = tr("<p>If connecting via the hostname fails, try %1</p>");
|
||||
static auto s_toolTipSuggestIP = tr("the suggested IP.");
|
||||
static auto s_toolTipIpList = tr("one of the following IPs:<br/>%1");
|
||||
|
||||
// Determine which IP to show and tooltip based on server state
|
||||
if (m_coreProcess.isStarted()) {
|
||||
// ipList should only include valid ip from servers start
|
||||
ipList.clear();
|
||||
for (const auto &address : std::as_const(m_serverStartIPs)) {
|
||||
if (addresses.contains(address))
|
||||
ipList.append(address.toString());
|
||||
}
|
||||
|
||||
QString suggestedIP = m_serverStartSuggestedIP.toString();
|
||||
if ((suggestedIP != m_currentIpAddress.toString()) || !addresses.contains(m_serverStartSuggestedIP)) {
|
||||
m_currentIPValid = false;
|
||||
for (const auto &address : std::as_const(m_serverStartIPs)) {
|
||||
if (addresses.contains(address)) {
|
||||
suggestedIP = address.toString();
|
||||
m_currentIpAddress = address;
|
||||
m_currentIPValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_currentIPValid = true;
|
||||
}
|
||||
|
||||
if (m_currentIPValid) {
|
||||
labelText.append(suggestedIP);
|
||||
} else {
|
||||
labelText.append(colorText.arg(palette().linkVisited().color().name(), suggestedIP));
|
||||
toolTipText.append(tr("\nA bound IP is now invalid, you may need to restart the server."));
|
||||
}
|
||||
} else {
|
||||
// Server is not running - update normally
|
||||
const auto suggestedIp = m_networkMonitor->getSuggestedIPv4Address();
|
||||
QString displayIP = !suggestedIp.isNull() ? suggestedIp.toString() : ipList.first();
|
||||
m_currentIpAddress = !suggestedIp.isNull() ? suggestedIp : QHostAddress();
|
||||
m_currentIPValid = !suggestedIp.isNull();
|
||||
labelText.append(displayIP);
|
||||
}
|
||||
|
||||
if (ipList.count() < 2) {
|
||||
toolTipText = toolTipText.arg(s_toolTipSuggestIP);
|
||||
} else {
|
||||
toolTipText = toolTipText.arg(s_toolTipIpList.arg(ipList.join("<br/>")));
|
||||
}
|
||||
}
|
||||
|
||||
ui->lblIpAddresses->setText(labelText);
|
||||
ui->lblIpAddresses->setToolTip(toolTipText);
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QMainWindow>
|
||||
#include <QMutex>
|
||||
#include <QProcess>
|
||||
@ -20,6 +21,7 @@
|
||||
#include "config/ServerConfig.h"
|
||||
#include "gui/core/ClientConnection.h"
|
||||
#include "gui/core/CoreProcess.h"
|
||||
#include "gui/core/NetworkMonitor.h"
|
||||
#include "gui/core/ServerConnection.h"
|
||||
#include "gui/core/WaylandWarnings.h"
|
||||
#include "net/Fingerprint.h"
|
||||
@ -58,6 +60,7 @@ class MainWindow : public QMainWindow
|
||||
{
|
||||
using CoreMode = Settings::CoreMode;
|
||||
using CoreProcess = deskflow::gui::CoreProcess;
|
||||
using NetworkMonitor = deskflow::gui::NetworkMonitor;
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
@ -152,6 +155,8 @@ private:
|
||||
void toggleCanRunCore(bool enableButtons);
|
||||
void remoteHostChanged(const QString &newRemoteHost);
|
||||
void handleNewClientPromptRequest(const QString &clientName, bool usePeerAuth);
|
||||
void updateIpLabel(const QList<QHostAddress> &addresses);
|
||||
|
||||
/**
|
||||
* @brief showClientError
|
||||
* @param error Error Type
|
||||
@ -218,4 +223,13 @@ private:
|
||||
QAction *m_actionStartCore = nullptr;
|
||||
QAction *m_actionRestartCore = nullptr;
|
||||
QAction *m_actionStopCore = nullptr;
|
||||
|
||||
// Network monitoring
|
||||
NetworkMonitor *m_networkMonitor = nullptr;
|
||||
QHostAddress m_currentIpAddress;
|
||||
|
||||
// Server IP strategy optimization
|
||||
QList<QHostAddress> m_serverStartIPs;
|
||||
QHostAddress m_serverStartSuggestedIP;
|
||||
bool m_currentIPValid;
|
||||
};
|
||||
|
||||
143
src/lib/gui/core/NetworkMonitor.cpp
Normal file
143
src/lib/gui/core/NetworkMonitor.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#include "NetworkMonitor.h"
|
||||
|
||||
#include <QAbstractSocket>
|
||||
#include <QList>
|
||||
#include <QNetworkInterface>
|
||||
#include <QSet>
|
||||
#include <QTimer>
|
||||
|
||||
namespace deskflow::gui {
|
||||
|
||||
bool NetworkMonitor::isVirtualInterface(const QString &interfaceName) const
|
||||
{
|
||||
// Common virtual network interface patterns
|
||||
static const QStringList virtualPatterns = {
|
||||
QStringLiteral("vboxnet"), // VirtualBox host-only networks
|
||||
QStringLiteral("vmnet"), // VMware virtual networks
|
||||
QStringLiteral("docker"), // Docker bridge networks
|
||||
QStringLiteral("virbr"), // libvirt bridge networks
|
||||
QStringLiteral("veth"), // Virtual ethernet
|
||||
QStringLiteral("br-"), // Bridge interfaces (some are virtual)
|
||||
QStringLiteral("tun"), // Tunnel interfaces
|
||||
QStringLiteral("tap"), // TAP interfaces
|
||||
QStringLiteral("utun"), // User tunnel (macOS)
|
||||
QStringLiteral("awdl"), // Apple Wireless Direct Link
|
||||
QStringLiteral("p2p"), // Peer-to-peer
|
||||
QStringLiteral("llw"), // Link-local wireless
|
||||
QStringLiteral("anpi"), // Apple network interface
|
||||
};
|
||||
|
||||
return virtualPatterns.contains(interfaceName, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
NetworkMonitor::NetworkMonitor(QObject *parent) : QObject(parent), m_checkTimer(new QTimer(this)), m_isMonitoring(false)
|
||||
{
|
||||
connect(m_checkTimer, &QTimer::timeout, this, &NetworkMonitor::updateNetworkState);
|
||||
}
|
||||
|
||||
void NetworkMonitor::startMonitoring(int intervalMs)
|
||||
{
|
||||
if (m_isMonitoring) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateNetworkState();
|
||||
|
||||
m_checkTimer->start(intervalMs);
|
||||
m_isMonitoring = true;
|
||||
}
|
||||
|
||||
void NetworkMonitor::stopMonitoring()
|
||||
{
|
||||
if (!m_isMonitoring) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_checkTimer->stop();
|
||||
m_isMonitoring = false;
|
||||
}
|
||||
|
||||
QList<QHostAddress> NetworkMonitor::getAvailableIPv4Addresses() const
|
||||
{
|
||||
QList<QHostAddress> physicalIPs;
|
||||
QList<QHostAddress> virtualIPs;
|
||||
QSet<QHostAddress> uniqueAddresses;
|
||||
|
||||
const auto allInterfaces = QNetworkInterface::allInterfaces();
|
||||
for (const auto &interface : allInterfaces) {
|
||||
if (!(interface.flags() & QNetworkInterface::IsUp) || !(interface.flags() & QNetworkInterface::IsRunning) ||
|
||||
(interface.flags() & QNetworkInterface::IsLoopBack)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool isVirtual = isVirtualInterface(interface.name());
|
||||
const auto addressEntries = interface.addressEntries();
|
||||
|
||||
for (const auto &entry : addressEntries) {
|
||||
const QHostAddress address = entry.ip();
|
||||
|
||||
if (address.protocol() != QAbstractSocket::IPv4Protocol ||
|
||||
address.isInSubnet(QHostAddress::parseSubnet(QStringLiteral("169.254/16"))) || address.isLoopback() ||
|
||||
uniqueAddresses.contains(address)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uniqueAddresses.insert(address);
|
||||
|
||||
if (isVirtual) {
|
||||
virtualIPs.append(address);
|
||||
} else {
|
||||
physicalIPs.append(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto physicalComparator = [](const QHostAddress &a, const QHostAddress &b) {
|
||||
static const auto localIpFilter = QStringLiteral("192.168/16");
|
||||
if (a.isInSubnet(QHostAddress::parseSubnet(localIpFilter)) !=
|
||||
b.isInSubnet(QHostAddress::parseSubnet(localIpFilter))) {
|
||||
return true;
|
||||
}
|
||||
return a.toIPv4Address() < b.toIPv4Address();
|
||||
};
|
||||
|
||||
std::sort(physicalIPs.begin(), physicalIPs.end(), physicalComparator);
|
||||
|
||||
std::sort(virtualIPs.begin(), virtualIPs.end(), [](const QHostAddress &a, const QHostAddress &b) {
|
||||
return a.toIPv4Address() < b.toIPv4Address();
|
||||
});
|
||||
|
||||
auto result = physicalIPs;
|
||||
result.append(virtualIPs);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QHostAddress NetworkMonitor::getSuggestedIPv4Address() const
|
||||
{
|
||||
const auto addresses = getAvailableIPv4Addresses();
|
||||
if (addresses.isEmpty())
|
||||
return QHostAddress();
|
||||
return addresses.first();
|
||||
}
|
||||
|
||||
void NetworkMonitor::setIpAddresses(const QList<QHostAddress> &newAddresses)
|
||||
{
|
||||
if (newAddresses == m_lastAddresses)
|
||||
return;
|
||||
m_lastAddresses = newAddresses;
|
||||
Q_EMIT ipAddressesChanged(m_lastAddresses);
|
||||
}
|
||||
|
||||
void NetworkMonitor::updateNetworkState()
|
||||
{
|
||||
setIpAddresses(getAvailableIPv4Addresses());
|
||||
}
|
||||
|
||||
} // namespace deskflow::gui
|
||||
88
src/lib/gui/core/NetworkMonitor.h
Normal file
88
src/lib/gui/core/NetworkMonitor.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
|
||||
class QTimer;
|
||||
namespace deskflow::gui {
|
||||
|
||||
/**
|
||||
* @brief Monitor network activity changes and provide IP address updates
|
||||
*
|
||||
* The NetworkMonitor class monitors IP address changes
|
||||
* It periodically checks network status and emits signals when changes are detected.
|
||||
*/
|
||||
class NetworkMonitor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new NetworkMonitor object
|
||||
* @param parent Parent QObject
|
||||
*/
|
||||
explicit NetworkMonitor(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Destroy the NetworkMonitor object
|
||||
*/
|
||||
~NetworkMonitor() override = default;
|
||||
|
||||
/**
|
||||
* @brief Start network monitoring
|
||||
* @param intervalMs Check interval in milliseconds, default 3000ms (3 seconds)
|
||||
*/
|
||||
void startMonitoring(int intervalMs = 3000);
|
||||
|
||||
/**
|
||||
* @brief Stop network monitoring
|
||||
*/
|
||||
void stopMonitoring();
|
||||
|
||||
/**
|
||||
* @brief Get list of all available IPv4 addresses (excluding local and link-local addresses)
|
||||
* @return IPv4 address list
|
||||
*/
|
||||
QList<QHostAddress> getAvailableIPv4Addresses() const;
|
||||
|
||||
/**
|
||||
* @brief Get recommended IP address (192.168.x.x preferred)
|
||||
* @return Recommended IP address, returns null if none available
|
||||
*/
|
||||
QHostAddress getSuggestedIPv4Address() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* @brief Emitted when IP addresses change
|
||||
* @param addresses New IP address list
|
||||
*/
|
||||
void ipAddressesChanged(const QList<QHostAddress> &addresses);
|
||||
|
||||
private:
|
||||
void setIpAddresses(const QList<QHostAddress> &newAddresses);
|
||||
|
||||
/**
|
||||
* @brief Check if a network interface is virtual
|
||||
* @param interfaceName Network interface name
|
||||
* @return true if it's a virtual interface
|
||||
*/
|
||||
bool isVirtualInterface(const QString &interfaceName) const;
|
||||
|
||||
/**
|
||||
* @brief Update current network status
|
||||
*/
|
||||
void updateNetworkState();
|
||||
|
||||
QTimer *m_checkTimer; ///< Timer for periodic network checks
|
||||
QList<QHostAddress> m_lastAddresses; ///< Last known IP addresses
|
||||
bool m_isMonitoring; ///< Flag indicating if monitoring is active
|
||||
};
|
||||
|
||||
} // namespace deskflow::gui
|
||||
@ -14,6 +14,7 @@
|
||||
#include "common/Settings.h"
|
||||
#include "gui/Messages.h"
|
||||
#include "gui/TlsUtility.h"
|
||||
#include "gui/core/NetworkMonitor.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDir>
|
||||
@ -49,6 +50,16 @@ SettingsDialog::SettingsDialog(QWidget *parent, const IServerConfig &serverConfi
|
||||
// the developer was looking at, and it's easy to accidentally save that.
|
||||
ui->tabWidget->setCurrentIndex(0);
|
||||
|
||||
// Populate the list of IP addresses
|
||||
NetworkMonitor networkMonitor(this);
|
||||
const auto addresses = networkMonitor.getAvailableIPv4Addresses();
|
||||
for (const auto &address : addresses) {
|
||||
QString ipString = address.toString();
|
||||
if (ui->comboInterface->findText(ipString) == -1) {
|
||||
ui->comboInterface->addItem(ipString, ipString);
|
||||
}
|
||||
}
|
||||
|
||||
loadFromConfig();
|
||||
|
||||
adjustSize();
|
||||
@ -160,7 +171,7 @@ void SettingsDialog::updateText()
|
||||
void SettingsDialog::accept()
|
||||
{
|
||||
Settings::setValue(Settings::Core::Port, ui->sbPort->value());
|
||||
Settings::setValue(Settings::Core::Interface, ui->lineInterface->text());
|
||||
Settings::setValue(Settings::Core::Interface, ui->comboInterface->currentData());
|
||||
Settings::setValue(Settings::Log::Level, ui->comboLogLevel->currentIndex());
|
||||
Settings::setValue(Settings::Log::ToFile, ui->cbLogToFile->isChecked());
|
||||
Settings::setValue(Settings::Log::File, ui->lineLogFilename->text());
|
||||
@ -194,7 +205,6 @@ void SettingsDialog::accept()
|
||||
void SettingsDialog::loadFromConfig()
|
||||
{
|
||||
ui->sbPort->setValue(Settings::value(Settings::Core::Port).toInt());
|
||||
ui->lineInterface->setText(Settings::value(Settings::Core::Interface).toString());
|
||||
ui->comboLogLevel->setCurrentIndex(Settings::value(Settings::Log::Level).toInt());
|
||||
ui->cbLogToFile->setChecked(Settings::value(Settings::Log::ToFile).toBool());
|
||||
ui->lineLogFilename->setText(Settings::value(Settings::Log::File).toString());
|
||||
@ -222,6 +232,10 @@ void SettingsDialog::loadFromConfig()
|
||||
|
||||
ui->lblDebugWarning->setVisible(Settings::value(Settings::Log::Level).toInt() > 4);
|
||||
|
||||
ui->comboInterface->setCurrentText(Settings::value(Settings::Core::Interface).toString());
|
||||
if (ui->comboInterface->currentIndex() < 0)
|
||||
ui->comboInterface->setCurrentIndex(0);
|
||||
|
||||
qDebug() << "load from config done";
|
||||
updateControls();
|
||||
}
|
||||
@ -289,7 +303,7 @@ void SettingsDialog::updateControls()
|
||||
ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(writable);
|
||||
|
||||
ui->sbPort->setEnabled(writable);
|
||||
ui->lineInterface->setEnabled(writable);
|
||||
ui->comboInterface->setEnabled(writable);
|
||||
ui->comboLogLevel->setEnabled(writable);
|
||||
ui->cbLogToFile->setEnabled(writable);
|
||||
ui->cbAutoHide->setEnabled(writable);
|
||||
|
||||
@ -393,10 +393,12 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineInterface">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QComboBox" name="comboInterface">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Automatic</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@ -733,7 +735,7 @@
|
||||
<tabstop>btnTlsRegenCert</tabstop>
|
||||
<tabstop>cbRequireClientCert</tabstop>
|
||||
<tabstop>sbPort</tabstop>
|
||||
<tabstop>lineInterface</tabstop>
|
||||
<tabstop>comboInterface</tabstop>
|
||||
<tabstop>cbLogToFile</tabstop>
|
||||
<tabstop>lineLogFilename</tabstop>
|
||||
<tabstop>btnBrowseLog</tabstop>
|
||||
|
||||
@ -420,8 +420,16 @@ Do you want to connect to the server?
|
||||
<translation type="unfinished">No se puede detectar una dirección IP. Compruebe que su conexión de red esté activa.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Suggested IP: %1</source>
|
||||
<translation type="unfinished">IP sugerida: %1</translation>
|
||||
<source>Using IP: </source>
|
||||
<translation type="unfinished">Usando IP: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Selected as the interface in settings.</source>
|
||||
<translation type="unfinished">Seleccionado como la interfaz en la configuración.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Suggested IP: </source>
|
||||
<translation type="unfinished">IP sugerida: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><p>If connecting via the hostname fails, try %1</p></source>
|
||||
@ -435,6 +443,12 @@ Do you want to connect to the server?
|
||||
<source>one of the following IPs:<br/>%1</source>
|
||||
<translation type="unfinished">una de las siguientes IP:<br/>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>
|
||||
A bound IP is now invalid, you may need to restart the server.</source>
|
||||
<translation type="unfinished">
|
||||
La dirección IP asignada ahora no es válida; es posible que deba reiniciar el servidor.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&File</source>
|
||||
<translation type="unfinished">&Archivo</translation>
|
||||
@ -459,6 +473,12 @@ Do you want to connect to the server?
|
||||
<source>invalid certificate, generating a new one</source>
|
||||
<translation type="unfinished">certificado no válido, generando uno nuevo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>
|
||||
Interface is not active. Unable to start server.</source>
|
||||
<translation type="unfinished">
|
||||
La interfaz no está activa. No se puede iniciar el servidor.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 will retry in a moment...</source>
|
||||
<translation type="unfinished">%1 lo intentará nuevamente en un momento...</translation>
|
||||
@ -1287,6 +1307,10 @@ Al habilitar esta opción, se deshabilitará la interfaz gráfica de usuario (GU
|
||||
<source><html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make Deskflow harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html></source>
|
||||
<translation type="unfinished"><html><head/><body><p>Requiere el paquete wl-clipboard</p><p>Al usar wl-clipboard v2.2.1, existe un error que provoca la pérdida del foco y que puede dificultar el uso de Deskflow. Este error se ha corregido al usar la rama principal de wl-clipboard, a menos que su Compositor no sea compatible con el protocolo wlroots-data-control.</p></body></html></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatic</source>
|
||||
<translation type="unfinished">Automática</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>i18n</name>
|
||||
|
||||
@ -408,8 +408,16 @@ Vuoi connetterti al server?
|
||||
<translation>Impossibile rilevare un indirizzo IP. Controlla che la tua connessione di rete sia attiva.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Suggested IP: %1</source>
|
||||
<translation>IP suggerito: %1</translation>
|
||||
<source>Using IP: </source>
|
||||
<translation type="unfinished">Utilizzo dell'indirizzo IP: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Selected as the interface in settings.</source>
|
||||
<translation type="unfinished">Selezionata come interfaccia nelle impostazioni.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Suggested IP: </source>
|
||||
<translation type="unfinished">IP suggerito: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><p>If connecting via the hostname fails, try %1</p></source>
|
||||
@ -423,6 +431,12 @@ Vuoi connetterti al server?
|
||||
<source>one of the following IPs:<br/>%1</source>
|
||||
<translation>uno dei seguenti IP:<br/>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>
|
||||
A bound IP is now invalid, you may need to restart the server.</source>
|
||||
<translation type="unfinished">
|
||||
L'indirizzo IP associato non è più valido, potrebbe essere necessario riavviare il server.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&File</source>
|
||||
<translation>&File</translation>
|
||||
@ -467,6 +481,12 @@ Nomi validi:
|
||||
<source>invalid certificate, generating a new one</source>
|
||||
<translation type="unfinished">certificato non valido, ne viene generato uno nuovo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>
|
||||
Interface is not active. Unable to start server.</source>
|
||||
<translation type="unfinished">
|
||||
L'interfaccia non è attiva. Impossibile avviare il server.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 will retry in a moment...</source>
|
||||
<translation>%1 riproverà tra un momento...</translation>
|
||||
@ -1287,6 +1307,10 @@ L'abilitazione di questa impostazione disabiliterà l'interfaccia graf
|
||||
<source><html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make Deskflow harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html></source>
|
||||
<translation><html><head/><body><p>Richiede il pacchetto wl-clipboard</p><p>Quando si utilizza wl-clipboard v2.2.1, si verifica un bug di furto del focus che potrebbe rendere Deskflow più difficile da usare. Questo problema è stato risolto quando si utilizza il ramo master di wl-clipboard, a meno che il proprio compositore non supporti il protocollo wlroots-data-control.</p></body></html></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatic</source>
|
||||
<translation type="unfinished">Automatica</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>i18n</name>
|
||||
|
||||
@ -372,8 +372,12 @@ Do you want to connect to the server?
|
||||
<translation>IPアドレスが見つかりません。ネットワーク接続を確認してください。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Suggested IP: %1</source>
|
||||
<translation>推奨IPアドレス: %1</translation>
|
||||
<source>Using IP: </source>
|
||||
<translation type="unfinished">IPアドレスを使用する: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Selected as the interface in settings.</source>
|
||||
<translation type="unfinished">設定でインターフェースとして選択されています。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><p>If connecting via the hostname fails, try %1</p></source>
|
||||
@ -387,6 +391,12 @@ Do you want to connect to the server?
|
||||
<source>one of the following IPs:<br/>%1</source>
|
||||
<translation>以下のIPアドレスのいずれか:<br/>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>
|
||||
A bound IP is now invalid, you may need to restart the server.</source>
|
||||
<translation type="unfinished">
|
||||
割り当て済みのIPアドレスが無効になりました。サーバーを再起動する必要があるかもしれません。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 is starting...</source>
|
||||
<translation>%1 は起動処理中です…</translation>
|
||||
@ -531,6 +541,16 @@ Valid names:
|
||||
<translation>クライアント:
|
||||
%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>
|
||||
Interface is not active. Unable to start server.</source>
|
||||
<translation type="unfinished">
|
||||
インターフェースがアクティブではありません。サーバーを起動できません。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Suggested IP: </source>
|
||||
<translation type="unfinished">推奨IPアドレス: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The Core executable could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
|
||||
<translation>コア実行ファイルは存在していますが、起動できませんでした。このプログラムを実行するのに十分な権限があることを確認してください。</translation>
|
||||
@ -1288,6 +1308,10 @@ Enabling this setting will disable the server config GUI.</source>
|
||||
<source><html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make Deskflow harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html></source>
|
||||
<translation><html><head/><body><p>wl-clipboard パッケージが必要です。</p><p>wl-clipboard v2.2.1 を使用すると、フォーカス盗用のバグにより Deskflow の使い勝手が悪くなる可能性があります。この問題は wl-clipboard のマスターブランチで修正されていますが、使用しているコンポジターが wlroots-data-control プロトコルに対応している必要があります。</p></body></html></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatic</source>
|
||||
<translation type="unfinished">自動</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>i18n</name>
|
||||
|
||||
@ -374,8 +374,12 @@ Do you want to connect to the server?
|
||||
<translation>Не получаеться найти Ip адресc. Проверте подключение к сети.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Suggested IP: %1</source>
|
||||
<translation>Ваш (рекомендованый) IP адресс: %1</translation>
|
||||
<source>Using IP: </source>
|
||||
<translation type="unfinished">Использование IP-адреса: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Selected as the interface in settings.</source>
|
||||
<translation type="unfinished">Выбран в качестве интерфейса в настройках.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><p>If connecting via the hostname fails, try %1</p></source>
|
||||
@ -389,6 +393,12 @@ Do you want to connect to the server?
|
||||
<source>one of the following IPs:<br/>%1</source>
|
||||
<translation>один из следующих IP-адресов:<br/>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>
|
||||
A bound IP is now invalid, you may need to restart the server.</source>
|
||||
<translation type="unfinished">
|
||||
Привязанный IP-адрес теперь недействителен, возможно, потребуется перезапустить сервер.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 is starting...</source>
|
||||
<translation>%1 запускается...</translation>
|
||||
@ -535,6 +545,16 @@ Valid names:
|
||||
<translation>Клиент:
|
||||
%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>
|
||||
Interface is not active. Unable to start server.</source>
|
||||
<translation type="unfinished">
|
||||
Интерфейс неактивен. Невозможно запустить сервер.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Suggested IP: </source>
|
||||
<translation type="unfinished">Ваш (рекомендованый) IP адресс: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The Core executable could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
|
||||
<translation type="unfinished">Не удалось запустить исполняемый файл Core, хотя он существует. Проверьте, есть ли у вас достаточные права для запуска этой программы.</translation>
|
||||
@ -1292,6 +1312,10 @@ Enabling this setting will disable the server config GUI.</source>
|
||||
<source><html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make Deskflow harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html></source>
|
||||
<translation><html><head/><body><p>Для этого необходим пакет wl-clipboard.</p><p>Когда ты используешь wl-clipboard v2.2.1. Возникает ошибка перехвата фокуса, которая мешает использовать deskflowю. Это фиксется если использовать wl-clipboard из ветки master, если только ваш Compositor не поддерживает протокол wlroots-data-control.</p></body></html></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatic</source>
|
||||
<translation type="unfinished">Автоматический</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>i18n</name>
|
||||
|
||||
@ -372,8 +372,12 @@ Do you want to connect to the server?
|
||||
<translation>无法检测到 IP 地址。请检查您的网络连接是否正常。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Suggested IP: %1</source>
|
||||
<translation>建议 IP:%1</translation>
|
||||
<source>Using IP: </source>
|
||||
<translation type="unfinished">使用IP地址: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Selected as the interface in settings.</source>
|
||||
<translation type="unfinished">在设置中选择为接口。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><p>If connecting via the hostname fails, try %1</p></source>
|
||||
@ -387,6 +391,12 @@ Do you want to connect to the server?
|
||||
<source>one of the following IPs:<br/>%1</source>
|
||||
<translation>以下 IP 地址之一:<br/>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>
|
||||
A bound IP is now invalid, you may need to restart the server.</source>
|
||||
<translation type="unfinished">
|
||||
绑定的IP地址现在无效,您可能需要重启服务器。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 is starting...</source>
|
||||
<translation>%1 正在启动...</translation>
|
||||
@ -531,6 +541,16 @@ Valid names:
|
||||
<translation>客户端:
|
||||
%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>
|
||||
Interface is not active. Unable to start server.</source>
|
||||
<translation type="unfinished">
|
||||
接口未激活。无法启动服务器。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Suggested IP: </source>
|
||||
<translation type="unfinished">建议 IP: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The Core executable could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
|
||||
<translation type="unfinished">Core 可执行文件虽然存在,但无法成功启动。请检查您是否拥有运行此程序的足够权限。</translation>
|
||||
@ -1288,6 +1308,10 @@ Enabling this setting will disable the server config GUI.</source>
|
||||
<source><html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make Deskflow harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html></source>
|
||||
<translation><html><head/><body><p>需要 wl-clipboard 包</p><p>使用 wl-clipboard v2.2.1 时存在一个焦点抢夺 Bug,可能导致 Deskflow 使用不便。该问题已在 wl-clipboard 的 master 分支中修复,除非您的合成器不支持 wlroots-data-control 协议。</p></body></html></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatic</source>
|
||||
<translation type="unfinished">自动的</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>i18n</name>
|
||||
|
||||
Reference in New Issue
Block a user