Allow to pause automatically on metered network connections
* Allow to pause all devices on metered network connections (devices will be automatically resumed when network is no longer metered) * Allow to stop Syncthing when it was started via the built-in launcher on metered network connections (it will be automatically started again when the network connection is no longer metered) * See https://github.com/Martchus/syncthingtray/issues/231
This commit is contained in:
parent
9816b6cf51
commit
30fa37f048
|
@ -13,7 +13,7 @@ set(META_VERSION_MAJOR 1)
|
|||
set(META_VERSION_MINOR 5)
|
||||
set(META_VERSION_PATCH 0)
|
||||
set(META_RELEASE_DATE "2024-02-06")
|
||||
set(META_SOVERSION 12)
|
||||
set(META_SOVERSION 13)
|
||||
set(META_ADD_DEFAULT_CPP_UNIT_TEST_APPLICATION ON)
|
||||
|
||||
project(${META_PROJECT_NAME})
|
||||
|
|
|
@ -25,6 +25,11 @@
|
|||
#include <QStringBuilder>
|
||||
#include <QTimer>
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
|
||||
#include <QNetworkInformation>
|
||||
#define SYNCTHINGCONNECTION_SUPPORT_METERED
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
|
@ -108,6 +113,7 @@ SyncthingConnection::SyncthingConnection(
|
|||
, m_dirStatsAltered(false)
|
||||
, m_recordFileChanges(false)
|
||||
, m_useDeprecatedRoutes(true)
|
||||
, m_pausingOnMeteredConnection(false)
|
||||
{
|
||||
m_trafficPollTimer.setInterval(SyncthingConnectionSettings::defaultTrafficPollInterval);
|
||||
m_trafficPollTimer.setTimerType(Qt::VeryCoarseTimer);
|
||||
|
@ -129,6 +135,14 @@ SyncthingConnection::SyncthingConnection(
|
|||
setupTestData();
|
||||
#endif
|
||||
|
||||
// initialize handling of metered connections
|
||||
#ifdef SYNCTHINGCONNECTION_SUPPORT_METERED
|
||||
QNetworkInformation::loadBackendByFeatures(QNetworkInformation::Feature::Metered);
|
||||
if (const auto *const networkInformation = QNetworkInformation::instance()) {
|
||||
QObject::connect(networkInformation, &QNetworkInformation::isMeteredChanged, this, &SyncthingConnection::handleMeteredConnection);
|
||||
}
|
||||
#endif
|
||||
|
||||
setLoggingFlags(loggingFlags);
|
||||
|
||||
// allow initializing the default value for m_useDeprecatedRoutes via environment variable
|
||||
|
@ -248,6 +262,50 @@ void SyncthingConnection::disablePolling()
|
|||
setErrorsPollInterval(0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets whether to pause all devices on metered connections.
|
||||
*/
|
||||
void SyncthingConnection::setPausingOnMeteredConnection(bool pausingOnMeteredConnection)
|
||||
{
|
||||
if (m_pausingOnMeteredConnection != pausingOnMeteredConnection) {
|
||||
m_pausingOnMeteredConnection = pausingOnMeteredConnection;
|
||||
if (m_hasConfig) {
|
||||
handleMeteredConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Ensures that devices are paused/resumed depending on whether the network connection is metered.
|
||||
*/
|
||||
void SyncthingConnection::handleMeteredConnection()
|
||||
{
|
||||
#ifdef SYNCTHINGCONNECTION_SUPPORT_METERED
|
||||
const auto *const networkInformation = QNetworkInformation::instance();
|
||||
if (!networkInformation->supports(QNetworkInformation::Feature::Metered)) {
|
||||
return;
|
||||
}
|
||||
if (networkInformation->isMetered() && m_pausingOnMeteredConnection) {
|
||||
auto hasDevicesToPause = false;
|
||||
m_devsPausedDueToMeteredConnection.reserve(static_cast<qsizetype>(m_devs.size()));
|
||||
for (const auto &device : m_devs) {
|
||||
if (!device.paused && device.status != SyncthingDevStatus::ThisDevice) {
|
||||
if (!m_devsPausedDueToMeteredConnection.contains(device.id)) {
|
||||
m_devsPausedDueToMeteredConnection << device.id;
|
||||
hasDevicesToPause = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasDevicesToPause) {
|
||||
pauseDevice(m_devsPausedDueToMeteredConnection);
|
||||
}
|
||||
} else {
|
||||
resumeDevice(m_devsPausedDueToMeteredConnection);
|
||||
m_devsPausedDueToMeteredConnection.clear();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Connects asynchronously to Syncthing. Does nothing if already connected.
|
||||
*
|
||||
|
@ -445,6 +503,7 @@ void SyncthingConnection::continueReconnecting()
|
|||
m_hasDiskEvents = false;
|
||||
m_dirs.clear();
|
||||
m_devs.clear();
|
||||
m_devsPausedDueToMeteredConnection.clear();
|
||||
m_lastConnectionsUpdateEvent = 0;
|
||||
m_lastConnectionsUpdateTime = DateTime();
|
||||
m_lastFileEvent = 0;
|
||||
|
@ -886,6 +945,7 @@ bool SyncthingConnection::applySettings(SyncthingConnectionSettings &connectionS
|
|||
setRequestTimeout(connectionSettings.requestTimeout);
|
||||
setLongPollingTimeout(connectionSettings.longPollingTimeout);
|
||||
setStatusComputionFlags(connectionSettings.statusComputionFlags);
|
||||
setPausingOnMeteredConnection(connectionSettings.pauseOnMeteredConnection);
|
||||
|
||||
return reconnectRequired;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
#include <QSslError>
|
||||
#include <QTimer>
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
|
||||
#include <QNetworkInformation>
|
||||
#define SYNCTHINGCONNECTION_SUPPORT_METERED
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
@ -92,6 +97,7 @@ class LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingConnection : public QObject {
|
|||
Q_PROPERTY(QStringList deviceIds READ deviceIds)
|
||||
Q_PROPERTY(QJsonObject rawConfig READ rawConfig NOTIFY newConfig)
|
||||
Q_PROPERTY(bool useDeprecatedRoutes READ isUsingDeprecatedRoutes WRITE setUseDeprecatedRoutes)
|
||||
Q_PROPERTY(bool pausingOnMeteredConnection READ isPausingOnMeteredConnection WRITE setPausingOnMeteredConnection)
|
||||
|
||||
public:
|
||||
explicit SyncthingConnection(const QString &syncthingUrl = QStringLiteral("http://localhost:8080"), const QByteArray &apiKey = QByteArray(),
|
||||
|
@ -145,6 +151,8 @@ public:
|
|||
void setRequestTimeout(int requestTimeout);
|
||||
int longPollingTimeout() const;
|
||||
void setLongPollingTimeout(int longPollingTimeout);
|
||||
bool isPausingOnMeteredConnection() const;
|
||||
void setPausingOnMeteredConnection(bool pausingOnMeteredConnection);
|
||||
|
||||
// getter for information retrieved from Syncthing
|
||||
const QString &configDir() const;
|
||||
|
@ -340,6 +348,7 @@ private Q_SLOTS:
|
|||
void handleAdditionalRequestCanceled();
|
||||
void handleSslErrors(const QList<QSslError> &errors);
|
||||
void handleRedirection(const QUrl &url);
|
||||
void handleMeteredConnection();
|
||||
void recalculateStatus();
|
||||
QString configPath() const;
|
||||
QByteArray changeConfigVerb() const;
|
||||
|
@ -415,6 +424,7 @@ private:
|
|||
bool m_hasDiskEvents;
|
||||
std::vector<SyncthingDir> m_dirs;
|
||||
std::vector<SyncthingDev> m_devs;
|
||||
QStringList m_devsPausedDueToMeteredConnection;
|
||||
SyncthingEventId m_lastConnectionsUpdateEvent;
|
||||
CppUtilities::DateTime m_lastConnectionsUpdateTime;
|
||||
SyncthingEventId m_lastFileEvent = 0;
|
||||
|
@ -433,6 +443,7 @@ private:
|
|||
bool m_dirStatsAltered;
|
||||
bool m_recordFileChanges;
|
||||
bool m_useDeprecatedRoutes;
|
||||
bool m_pausingOnMeteredConnection;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -768,6 +779,14 @@ inline void SyncthingConnection::setLongPollingTimeout(int longPollingTimeout)
|
|||
m_longPollingTimeout = longPollingTimeout;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether to pause all devices on metered connections.
|
||||
*/
|
||||
inline bool SyncthingConnection::isPausingOnMeteredConnection() const
|
||||
{
|
||||
return m_pausingOnMeteredConnection;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns what information is considered to compute the overall status returned by status().
|
||||
*/
|
||||
|
|
|
@ -347,7 +347,7 @@ bool SyncthingConnection::pauseResumeDevice(const QStringList &devIds, bool paus
|
|||
if (devIds.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (!isConnected()) {
|
||||
if (!m_hasConfig) {
|
||||
emit error(tr("Unable to pause/resume a devices when not connected"), SyncthingErrorCategory::SpecificRequest, QNetworkReply::NoError);
|
||||
return false;
|
||||
}
|
||||
|
@ -794,6 +794,9 @@ void SyncthingConnection::readDevs(const QJsonArray &devs)
|
|||
|
||||
m_devs.swap(newDevs);
|
||||
emit this->newDevices(m_devs);
|
||||
if (m_pausingOnMeteredConnection) {
|
||||
handleMeteredConnection();
|
||||
}
|
||||
}
|
||||
|
||||
// status of Syncthing (own ID, startup time)
|
||||
|
|
|
@ -53,6 +53,7 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingConnectionSettings {
|
|||
QList<QSslError> expectedSslErrors;
|
||||
SyncthingStatusComputionFlags statusComputionFlags = SyncthingStatusComputionFlags::Default;
|
||||
bool autoConnect = false;
|
||||
bool pauseOnMeteredConnection = false;
|
||||
static QList<QSslError> compileSslErrors(const QSslCertificate &trustedCert);
|
||||
bool loadHttpsCert();
|
||||
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
#include "./syncthinglauncher.h"
|
||||
|
||||
#include <syncthingconnector/syncthingconnection.h>
|
||||
|
||||
#include "../settings/settings.h"
|
||||
|
||||
#include <QtConcurrentRun>
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
|
||||
#include <QNetworkInformation>
|
||||
#define SYNCTHINGCONNECTION_SUPPORT_METERED
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
@ -31,13 +38,17 @@ SyncthingLauncher *SyncthingLauncher::s_mainInstance = nullptr;
|
|||
*/
|
||||
SyncthingLauncher::SyncthingLauncher(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_lastLauncherSettings(nullptr)
|
||||
, m_guiListeningUrlSearch("Access the GUI via the following URL: ", "\n\r", std::string_view(),
|
||||
std::bind(&SyncthingLauncher::handleGuiListeningUrlFound, this, std::placeholders::_1, std::placeholders::_2))
|
||||
#ifdef SYNCTHINGWIDGETS_USE_LIBSYNCTHING
|
||||
, m_libsyncthingLogLevel(LibSyncthing::LogLevel::Info)
|
||||
#endif
|
||||
, m_manuallyStopped(true)
|
||||
, m_stoppedMetered(false)
|
||||
, m_emittingOutput(false)
|
||||
, m_useLibSyncthing(false)
|
||||
, m_stopOnMeteredConnection(false)
|
||||
{
|
||||
connect(&m_process, &SyncthingProcess::readyRead, this, &SyncthingLauncher::handleProcessReadyRead, Qt::QueuedConnection);
|
||||
connect(&m_process, static_cast<void (SyncthingProcess::*)(int exitCode, QProcess::ExitStatus exitStatus)>(&SyncthingProcess::finished), this,
|
||||
|
@ -45,6 +56,15 @@ SyncthingLauncher::SyncthingLauncher(QObject *parent)
|
|||
connect(&m_process, &SyncthingProcess::stateChanged, this, &SyncthingLauncher::handleProcessStateChanged, Qt::QueuedConnection);
|
||||
connect(&m_process, &SyncthingProcess::errorOccurred, this, &SyncthingLauncher::errorOccurred, Qt::QueuedConnection);
|
||||
connect(&m_process, &SyncthingProcess::confirmKill, this, &SyncthingLauncher::confirmKill);
|
||||
|
||||
// initialize handling of metered connections
|
||||
#ifdef SYNCTHINGCONNECTION_SUPPORT_METERED
|
||||
QNetworkInformation::loadBackendByFeatures(QNetworkInformation::Feature::Metered);
|
||||
if (const auto *const networkInformation = QNetworkInformation::instance(); networkInformation->supports(QNetworkInformation::Feature::Metered)) {
|
||||
connect(networkInformation, &QNetworkInformation::isMeteredChanged, this, [this](bool isMetered) { setNetworkConnectionMetered(isMetered); });
|
||||
setNetworkConnectionMetered(networkInformation->isMetered());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -60,6 +80,38 @@ void SyncthingLauncher::setEmittingOutput(bool emittingOutput)
|
|||
emit outputAvailable(std::move(data));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets whether the current network connection is metered and stops/starts Syncthing accordingly as needed.
|
||||
* \remarks
|
||||
* - This is detected and monitored automatically. A manually set value will be overridden again on the next change.
|
||||
* - One may set this manually for testing purposes or in case the automatic detection is not supported (then
|
||||
* isNetworkConnectionMetered() returns a std::optional<bool> without value).
|
||||
*/
|
||||
void SyncthingLauncher::setNetworkConnectionMetered(std::optional<bool> metered)
|
||||
{
|
||||
if (metered != m_metered) {
|
||||
m_metered = metered;
|
||||
if (m_stopOnMeteredConnection) {
|
||||
if (metered.value_or(false)) {
|
||||
terminateDueToMeteredConnection();
|
||||
} else if (!metered.value_or(true) && m_stoppedMetered && m_lastLauncherSettings) {
|
||||
launch(*m_lastLauncherSettings);
|
||||
}
|
||||
}
|
||||
emit networkConnectionMeteredChanged(metered);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets whether Syncthing should automatically be stopped as long as the network connection is metered.
|
||||
*/
|
||||
void SyncthingLauncher::setStoppingOnMeteredConnection(bool stopOnMeteredConnection)
|
||||
{
|
||||
if ((stopOnMeteredConnection != m_stopOnMeteredConnection) && (m_stopOnMeteredConnection = stopOnMeteredConnection) && m_metered) {
|
||||
terminateDueToMeteredConnection();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether the built-in Syncthing library is available.
|
||||
*/
|
||||
|
@ -139,6 +191,8 @@ void SyncthingLauncher::launch(const Settings::Launcher &launcherSettings)
|
|||
} else {
|
||||
launch(launcherSettings.syncthingPath, SyncthingProcess::splitArguments(launcherSettings.syncthingArgs));
|
||||
}
|
||||
m_stopOnMeteredConnection = launcherSettings.stopOnMeteredConnection;
|
||||
m_lastLauncherSettings = &launcherSettings;
|
||||
}
|
||||
|
||||
#ifdef SYNCTHINGWIDGETS_USE_LIBSYNCTHING
|
||||
|
@ -233,6 +287,9 @@ void SyncthingLauncher::handleProcessFinished(int exitCode, QProcess::ExitStatus
|
|||
void SyncthingLauncher::resetState()
|
||||
{
|
||||
m_manuallyStopped = false;
|
||||
m_stoppedMetered = false;
|
||||
delete m_relevantConnection;
|
||||
m_relevantConnection = nullptr;
|
||||
m_guiListeningUrlSearch.reset();
|
||||
if (!m_guiListeningUrl.isEmpty()) {
|
||||
m_guiListeningUrl.clear();
|
||||
|
@ -281,6 +338,15 @@ void SyncthingLauncher::handleGuiListeningUrlFound(CppUtilities::BufferSearch &,
|
|||
emit guiUrlChanged(m_guiListeningUrl);
|
||||
}
|
||||
|
||||
void SyncthingLauncher::terminateDueToMeteredConnection()
|
||||
{
|
||||
if (m_lastLauncherSettings && !m_relevantConnection) {
|
||||
m_relevantConnection = m_lastLauncherSettings->connectionForLauncher(this);
|
||||
}
|
||||
terminate(m_relevantConnection);
|
||||
m_stoppedMetered = true;
|
||||
}
|
||||
|
||||
#ifdef SYNCTHINGWIDGETS_USE_LIBSYNCTHING
|
||||
void SyncthingLauncher::runLibSyncthing(const LibSyncthing::RuntimeOptions &runtimeOptions)
|
||||
{
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <QFuture>
|
||||
#include <QUrl>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace Settings {
|
||||
struct Launcher;
|
||||
}
|
||||
|
@ -29,6 +31,9 @@ class SYNCTHINGWIDGETS_EXPORT SyncthingLauncher : public QObject {
|
|||
Q_PROPERTY(CppUtilities::DateTime activeSince READ activeSince)
|
||||
Q_PROPERTY(bool manuallyStopped READ isManuallyStopped)
|
||||
Q_PROPERTY(bool emittingOutput READ isEmittingOutput WRITE setEmittingOutput)
|
||||
Q_PROPERTY(std::optional<bool> networkConnectionMetered READ isNetworkConnectionMetered WRITE setNetworkConnectionMetered NOTIFY
|
||||
networkConnectionMeteredChanged)
|
||||
Q_PROPERTY(bool stoppingOnMeteredConnection READ isStoppingOnMeteredConnection WRITE setStoppingOnMeteredConnection)
|
||||
Q_PROPERTY(QUrl guiUrl READ guiUrl NOTIFY guiUrlChanged)
|
||||
Q_PROPERTY(SyncthingProcess *process READ process)
|
||||
|
||||
|
@ -41,6 +46,10 @@ public:
|
|||
bool isManuallyStopped() const;
|
||||
bool isEmittingOutput() const;
|
||||
void setEmittingOutput(bool emittingOutput);
|
||||
std::optional<bool> isNetworkConnectionMetered() const;
|
||||
void setNetworkConnectionMetered(std::optional<bool> metered);
|
||||
bool isStoppingOnMeteredConnection() const;
|
||||
void setStoppingOnMeteredConnection(bool stopOnMeteredConnection);
|
||||
QString errorString() const;
|
||||
QUrl guiUrl() const;
|
||||
SyncthingProcess *process();
|
||||
|
@ -64,6 +73,7 @@ Q_SIGNALS:
|
|||
void exited(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void errorOccurred(QProcess::ProcessError error);
|
||||
void guiUrlChanged(const QUrl &newUrl);
|
||||
void networkConnectionMeteredChanged(std::optional<bool> isMetered);
|
||||
|
||||
public Q_SLOTS:
|
||||
void launch(const QString &program, const QStringList &arguments);
|
||||
|
@ -90,9 +100,12 @@ private:
|
|||
#endif
|
||||
void handleOutputAvailable(QByteArray &&data);
|
||||
void handleGuiListeningUrlFound(CppUtilities::BufferSearch &bufferSearch, std::string &&searchResult);
|
||||
void terminateDueToMeteredConnection();
|
||||
|
||||
SyncthingProcess m_process;
|
||||
QUrl m_guiListeningUrl;
|
||||
const Settings::Launcher *m_lastLauncherSettings;
|
||||
SyncthingConnection *m_relevantConnection;
|
||||
QFuture<void> m_startFuture;
|
||||
QFuture<void> m_stopFuture;
|
||||
QByteArray m_outputBuffer;
|
||||
|
@ -102,8 +115,11 @@ private:
|
|||
LibSyncthing::LogLevel m_libsyncthingLogLevel;
|
||||
#endif
|
||||
bool m_manuallyStopped;
|
||||
bool m_stoppedMetered;
|
||||
bool m_emittingOutput;
|
||||
bool m_useLibSyncthing;
|
||||
bool m_stopOnMeteredConnection;
|
||||
std::optional<bool> m_metered;
|
||||
static SyncthingLauncher *s_mainInstance;
|
||||
};
|
||||
|
||||
|
@ -145,6 +161,19 @@ inline bool SyncthingLauncher::isEmittingOutput() const
|
|||
return m_emittingOutput;
|
||||
}
|
||||
|
||||
/// \brief Returns whether the current network connection is metered.
|
||||
/// \remarks Returns an std::optional<bool> without value if it is unknown whether the network connection is metered.
|
||||
inline std::optional<bool> SyncthingLauncher::isNetworkConnectionMetered() const
|
||||
{
|
||||
return m_metered;
|
||||
}
|
||||
|
||||
/// \brief Returns whether Syncthing should automatically be stopped as long as the network connection is metered.
|
||||
inline bool SyncthingLauncher::isStoppingOnMeteredConnection() const
|
||||
{
|
||||
return m_stopOnMeteredConnection;
|
||||
}
|
||||
|
||||
/// \brief Returns the last error message.
|
||||
inline QString SyncthingLauncher::errorString() const
|
||||
{
|
||||
|
|
|
@ -475,7 +475,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="21" column="1">
|
||||
<item row="22" column="1">
|
||||
<layout class="QVBoxLayout" name="statusComputionFlagsVerticalLayout">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
|
@ -520,21 +520,21 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="22" column="0">
|
||||
<item row="23" column="0">
|
||||
<widget class="QLabel" name="statusTextLabel">
|
||||
<property name="text">
|
||||
<string>Current status</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="22" column="1">
|
||||
<item row="23" column="1">
|
||||
<widget class="QLabel" name="statusLabel">
|
||||
<property name="text">
|
||||
<string>disconnected</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="25" column="1">
|
||||
<item row="26" column="1">
|
||||
<widget class="QPushButton" name="connectPushButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
|
@ -643,6 +643,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="21" column="1">
|
||||
<widget class="QCheckBox" name="pauseOnMeteredConnectionCheckBox">
|
||||
<property name="text">
|
||||
<string>Pause all devices while the local network connection is metered</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
|
|
|
@ -140,6 +140,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="stopOnMeteredCheckBox">
|
||||
<property name="text">
|
||||
<string>Stop automatically when network connection is metered</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="sizePolicy">
|
||||
|
|
|
@ -313,6 +313,8 @@ bool restore()
|
|||
connectionSettings->longPollingTimeout
|
||||
= settings.value(QStringLiteral("longPollingTimeout"), connectionSettings->longPollingTimeout).toInt();
|
||||
connectionSettings->autoConnect = settings.value(QStringLiteral("autoConnect"), connectionSettings->autoConnect).toBool();
|
||||
connectionSettings->pauseOnMeteredConnection
|
||||
= settings.value(QStringLiteral("pauseOnMetered"), connectionSettings->pauseOnMeteredConnection).toBool();
|
||||
const auto statusComputionFlags = settings.value(QStringLiteral("statusComputionFlags"),
|
||||
QVariant::fromValue(static_cast<UnderlyingFlagType>(connectionSettings->statusComputionFlags)));
|
||||
if (statusComputionFlags.canConvert<UnderlyingFlagType>()) {
|
||||
|
@ -392,6 +394,7 @@ bool restore()
|
|||
launcher.syncthingArgs = settings.value(QStringLiteral("syncthingArgs"), launcher.syncthingArgs).toString();
|
||||
launcher.considerForReconnect = settings.value(QStringLiteral("considerLauncherForReconnect"), launcher.considerForReconnect).toBool();
|
||||
launcher.showButton = settings.value(QStringLiteral("showLauncherButton"), launcher.showButton).toBool();
|
||||
launcher.stopOnMeteredConnection = settings.value(QStringLiteral("stopOnMetered"), launcher.stopOnMeteredConnection).toBool();
|
||||
settings.beginGroup(QStringLiteral("tools"));
|
||||
const auto childGroups = settings.childGroups();
|
||||
for (const QString &tool : childGroups) {
|
||||
|
@ -465,6 +468,7 @@ bool save()
|
|||
settings.setValue(QStringLiteral("requestTimeout"), connectionSettings->requestTimeout);
|
||||
settings.setValue(QStringLiteral("longPollingTimeout"), connectionSettings->longPollingTimeout);
|
||||
settings.setValue(QStringLiteral("autoConnect"), connectionSettings->autoConnect);
|
||||
settings.setValue(QStringLiteral("pauseOnMetered"), connectionSettings->pauseOnMeteredConnection);
|
||||
settings.setValue(QStringLiteral("statusComputionFlags"),
|
||||
QVariant::fromValue(static_cast<std::underlying_type_t<Data::SyncthingStatusComputionFlags>>(connectionSettings->statusComputionFlags)));
|
||||
settings.setValue(QStringLiteral("httpsCertPath"), connectionSettings->httpsCertPath);
|
||||
|
@ -519,6 +523,7 @@ bool save()
|
|||
settings.setValue(QStringLiteral("syncthingArgs"), launcher.syncthingArgs);
|
||||
settings.setValue(QStringLiteral("considerLauncherForReconnect"), launcher.considerForReconnect);
|
||||
settings.setValue(QStringLiteral("showLauncherButton"), launcher.showButton);
|
||||
settings.setValue(QStringLiteral("stopOnMetered"), launcher.stopOnMeteredConnection);
|
||||
settings.beginGroup(QStringLiteral("tools"));
|
||||
for (auto i = launcher.tools.cbegin(), end = launcher.tools.cend(); i != end; ++i) {
|
||||
const ToolParameter &toolParams = i.value();
|
||||
|
|
|
@ -95,6 +95,7 @@ struct SYNCTHINGWIDGETS_EXPORT Launcher {
|
|||
QHash<QString, ToolParameter> tools;
|
||||
bool considerForReconnect = false;
|
||||
bool showButton = false;
|
||||
bool stopOnMeteredConnection = false;
|
||||
|
||||
#ifdef SYNCTHINGWIDGETS_USE_LIBSYNCTHING
|
||||
struct SYNCTHINGWIDGETS_EXPORT LibSyncthing {
|
||||
|
|
|
@ -82,6 +82,15 @@ using namespace QtUtilities;
|
|||
|
||||
namespace QtGui {
|
||||
|
||||
/// \brief Returns the tooltip text for the specified \a isMetered value.
|
||||
static QString meteredToolTip(std::optional<bool> isMetered)
|
||||
{
|
||||
return isMetered.has_value()
|
||||
? (isMetered.value() ? QCoreApplication::translate("QtGui", "The network connection is currently considered metered.")
|
||||
: QCoreApplication::translate("QtGui", "The network connection is currently not considered metered."))
|
||||
: QCoreApplication::translate("QtGui", "Unable to determine whether the network connection is metered; assuming an unmetered connection.");
|
||||
}
|
||||
|
||||
// ConnectionOptionPage
|
||||
ConnectionOptionPage::ConnectionOptionPage(Data::SyncthingConnection *connection, QWidget *parentWidget)
|
||||
: ConnectionOptionPageBase(parentWidget)
|
||||
|
@ -133,6 +142,11 @@ QWidget *ConnectionOptionPage::setupWidget()
|
|||
QObject::connect(ui()->addPushButton, &QPushButton::clicked, bind(&ConnectionOptionPage::addNewConfig, this));
|
||||
QObject::connect(ui()->removePushButton, &QPushButton::clicked, bind(&ConnectionOptionPage::removeSelectedConfig, this));
|
||||
QObject::connect(ui()->advancedCheckBox, &QCheckBox::toggled, bind(&ConnectionOptionPage::toggleAdvancedSettings, this, std::placeholders::_1));
|
||||
if (const auto *const launcher = SyncthingLauncher::mainInstance()) {
|
||||
handleNetworkConnectionMeteredChanged(launcher->isNetworkConnectionMetered());
|
||||
QObject::connect(launcher, &SyncthingLauncher::networkConnectionMeteredChanged,
|
||||
bind(&ConnectionOptionPage::handleNetworkConnectionMeteredChanged, this, std::placeholders::_1));
|
||||
}
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
|
||||
ui()->timeoutSpinBox->setEnabled(false);
|
||||
#endif
|
||||
|
@ -220,6 +234,7 @@ bool ConnectionOptionPage::showConnectionSettings(int index)
|
|||
ui()->pollErrorsSpinBox->setValue(connectionSettings.errorsPollInterval);
|
||||
ui()->reconnectSpinBox->setValue(connectionSettings.reconnectInterval);
|
||||
ui()->autoConnectCheckBox->setChecked(connectionSettings.autoConnect);
|
||||
ui()->pauseOnMeteredConnectionCheckBox->setChecked(connectionSettings.pauseOnMeteredConnection);
|
||||
m_statusComputionModel->setStatusComputionFlags(connectionSettings.statusComputionFlags);
|
||||
setCurrentIndex(index);
|
||||
return true;
|
||||
|
@ -247,6 +262,7 @@ bool ConnectionOptionPage::cacheCurrentSettings(bool applying)
|
|||
connectionSettings.errorsPollInterval = ui()->pollErrorsSpinBox->value();
|
||||
connectionSettings.reconnectInterval = ui()->reconnectSpinBox->value();
|
||||
connectionSettings.autoConnect = ui()->autoConnectCheckBox->isChecked();
|
||||
connectionSettings.pauseOnMeteredConnection = ui()->pauseOnMeteredConnectionCheckBox->isChecked();
|
||||
connectionSettings.statusComputionFlags = m_statusComputionModel->statusComputionFlags();
|
||||
if (!connectionSettings.loadHttpsCert()) {
|
||||
const QString errorMessage = QCoreApplication::translate("QtGui::ConnectionOptionPage", "Unable to load specified certificate \"%1\".")
|
||||
|
@ -366,20 +382,26 @@ void ConnectionOptionPage::toggleAdvancedSettings(bool show)
|
|||
return;
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
|
||||
for (auto *const widget : std::initializer_list<QWidget *>{
|
||||
ui()->authLabel, ui()->userNameLabel, ui()->passwordLabel, ui()->timeoutLabel, ui()->longPollingLabel, ui()->pollLabel }) {
|
||||
for (auto *const widget : std::initializer_list<QWidget *>{ ui()->authLabel, ui()->userNameLabel, ui()->passwordLabel, ui()->timeoutLabel,
|
||||
ui()->longPollingLabel, ui()->pollLabel, ui()->pauseOnMeteredConnectionCheckBox }) {
|
||||
ui()->formLayout->setRowVisible(widget, show);
|
||||
}
|
||||
#else
|
||||
for (auto *const widget : std::initializer_list<QWidget *>{ ui()->authLabel, ui()->authCheckBox, ui()->userNameLabel, ui()->userNameLineEdit,
|
||||
ui()->passwordLabel, ui()->passwordLineEdit, ui()->timeoutLabel, ui()->timeoutSpinBox, ui()->longPollingLabel, ui()->longPollingSpinBox,
|
||||
ui()->pollLabel, ui()->pollDevStatsLabel, ui()->pollDevStatsSpinBox, ui()->pollErrorsLabel, ui()->pollErrorsSpinBox,
|
||||
ui()->pollTrafficLabel, ui()->pollTrafficSpinBox, ui()->reconnectLabel, ui()->reconnectSpinBox }) {
|
||||
for (auto *const widget :
|
||||
std::initializer_list<QWidget *>{ ui()->authLabel, ui()->authCheckBox, ui()->userNameLabel, ui()->userNameLineEdit, ui()->passwordLabel,
|
||||
ui()->passwordLineEdit, ui()->timeoutLabel, ui()->timeoutSpinBox, ui()->longPollingLabel, ui()->longPollingSpinBox, ui()->pollLabel,
|
||||
ui()->pollDevStatsLabel, ui()->pollDevStatsSpinBox, ui()->pollErrorsLabel, ui()->pollErrorsSpinBox, ui()->pollTrafficLabel,
|
||||
ui()->pollTrafficSpinBox, ui()->reconnectLabel, ui()->reconnectSpinBox, ui()->pauseOnMeteredConnectionCheckBox }) {
|
||||
widget->setVisible(show);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ConnectionOptionPage::handleNetworkConnectionMeteredChanged(std::optional<bool> isMetered)
|
||||
{
|
||||
ui()->pauseOnMeteredConnectionCheckBox->setToolTip(meteredToolTip(isMetered));
|
||||
}
|
||||
|
||||
bool ConnectionOptionPage::apply()
|
||||
{
|
||||
if (!cacheCurrentSettings(true)) {
|
||||
|
@ -1088,6 +1110,7 @@ QWidget *LauncherOptionPage::setupWidget()
|
|||
// hide "consider for reconnect" and "show start/stop button on tray" checkboxes for tools
|
||||
ui()->considerForReconnectCheckBox->setVisible(false);
|
||||
ui()->showButtonCheckBox->setVisible(false);
|
||||
ui()->stopOnMeteredCheckBox->setVisible(false);
|
||||
}
|
||||
|
||||
// hide libsyncthing-controls by default (as the checkbox is unchecked by default)
|
||||
|
@ -1126,7 +1149,7 @@ QWidget *LauncherOptionPage::setupWidget()
|
|||
ui()->useBuiltInVersionCheckBox->setToolTip(SyncthingLauncher::libSyncthingVersionInfo());
|
||||
}
|
||||
|
||||
// connect signals & slots
|
||||
// setup process/launcher
|
||||
if (m_process) {
|
||||
connect(m_process, &SyncthingProcess::readyRead, this, &LauncherOptionPage::handleSyncthingReadyRead, Qt::QueuedConnection);
|
||||
connect(m_process, static_cast<void (SyncthingProcess::*)(int exitCode, QProcess::ExitStatus exitStatus)>(&SyncthingProcess::finished), this,
|
||||
|
@ -1141,6 +1164,10 @@ QWidget *LauncherOptionPage::setupWidget()
|
|||
connect(ui()->logLevelComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
&LauncherOptionPage::updateLibSyncthingLogLevel);
|
||||
#endif
|
||||
|
||||
handleNetworkConnectionMeteredChanged(m_launcher->isNetworkConnectionMetered());
|
||||
connect(m_launcher, &SyncthingLauncher::networkConnectionMeteredChanged, this, &LauncherOptionPage::handleNetworkConnectionMeteredChanged);
|
||||
|
||||
m_launcher->setEmittingOutput(true);
|
||||
}
|
||||
connect(ui()->launchNowPushButton, &QPushButton::clicked, this, &LauncherOptionPage::launch);
|
||||
|
@ -1164,6 +1191,7 @@ bool LauncherOptionPage::apply()
|
|||
settings.syncthingArgs = ui()->argumentsLineEdit->text();
|
||||
settings.considerForReconnect = ui()->considerForReconnectCheckBox->isChecked();
|
||||
settings.showButton = ui()->showButtonCheckBox->isChecked();
|
||||
settings.stopOnMeteredConnection = ui()->stopOnMeteredCheckBox->isChecked();
|
||||
} else {
|
||||
ToolParameter ¶ms = settings.tools[m_tool];
|
||||
params.autostart = ui()->enabledCheckBox->isChecked();
|
||||
|
@ -1189,6 +1217,7 @@ void LauncherOptionPage::reset()
|
|||
ui()->argumentsLineEdit->setText(settings.syncthingArgs);
|
||||
ui()->considerForReconnectCheckBox->setChecked(settings.considerForReconnect);
|
||||
ui()->showButtonCheckBox->setChecked(settings.showButton);
|
||||
ui()->stopOnMeteredCheckBox->setChecked(settings.stopOnMeteredConnection);
|
||||
} else {
|
||||
const ToolParameter params = settings.tools.value(m_tool);
|
||||
ui()->useBuiltInVersionCheckBox->setChecked(false);
|
||||
|
@ -1304,6 +1333,11 @@ void LauncherOptionPage::handleSyncthingError(QProcess::ProcessError error)
|
|||
}
|
||||
}
|
||||
|
||||
void LauncherOptionPage::handleNetworkConnectionMeteredChanged(std::optional<bool> isMetered)
|
||||
{
|
||||
ui()->stopOnMeteredCheckBox->setToolTip(meteredToolTip(isMetered));
|
||||
}
|
||||
|
||||
bool LauncherOptionPage::isRunning() const
|
||||
{
|
||||
return (m_process && m_process->isRunning()) || (m_launcher && m_launcher->isRunning());
|
||||
|
|
|
@ -69,6 +69,7 @@ void moveSelectedConfigDown();
|
|||
void moveSelectedConfigUp();
|
||||
void setCurrentIndex(int currentIndex);
|
||||
void toggleAdvancedSettings(bool show);
|
||||
void handleNetworkConnectionMeteredChanged(std::optional<bool> isMetered);
|
||||
Data::SyncthingConnection *m_connection;
|
||||
Data::SyncthingConnectionSettings m_primarySettings;
|
||||
std::vector<Data::SyncthingConnectionSettings> m_secondarySettings;
|
||||
|
@ -137,6 +138,7 @@ private Q_SLOTS:
|
|||
void handleSyncthingOutputAvailable(const QByteArray &output);
|
||||
void handleSyncthingExited(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void handleSyncthingError(QProcess::ProcessError error);
|
||||
void handleNetworkConnectionMeteredChanged(std::optional<bool> isMetered);
|
||||
void launch();
|
||||
#ifdef SYNCTHINGWIDGETS_USE_LIBSYNCTHING
|
||||
void updateLibSyncthingLogLevel();
|
||||
|
|
Loading…
Reference in New Issue