Allow to show start/stop button for launcher

This commit is contained in:
Martchus 2019-07-12 20:26:56 +02:00
parent 31bb92da89
commit 2112b00347
10 changed files with 314 additions and 98 deletions

View File

@ -210,12 +210,13 @@ void SyncthingApplet::setCurrentConnectionConfigIndex(int index)
// apply systemd settings, reconnect if required and possible
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
settings.systemd.apply(m_connection, currentConnectionConfig(), reconnectRequired);
const auto systemdRelevantForReconnect = settings.systemd.apply(m_connection, currentConnectionConfig(), reconnectRequired).relevant;
#else
if (reconnectRequired || !m_connection.isConnected()) {
const auto systemdRelevantForReconnect = false;
#endif
if (!systemdRelevantForReconnect && (reconnectRequired || !m_connection.isConnected())) {
m_connection.reconnect();
}
#endif
}
bool SyncthingApplet::isStartStopEnabled() const

View File

@ -4,6 +4,7 @@
#include "../../widgets/misc/otherdialogs.h"
#include "../../widgets/misc/textviewdialog.h"
#include "../../widgets/misc/syncthinglauncher.h"
#include "../../widgets/settings/settingsdialog.h"
#include "../../widgets/webview/webviewdialog.h"
@ -73,6 +74,7 @@ TrayWidget::TrayWidget(TrayMenu *parent)
, m_devModel(m_connection)
, m_dlModel(m_connection)
, m_selectedConnection(nullptr)
, m_startStopButtonTarget(StartStopButtonTarget::None)
{
m_instances.push_back(this);
@ -134,9 +136,6 @@ TrayWidget::TrayWidget(TrayMenu *parent)
m_ui->localTextLabel->setPixmap(
QIcon::fromTheme(QStringLiteral("user-home"), QIcon(QStringLiteral(":/icons/hicolor/scalable/places/user-home.svg"))).pixmap(16));
updateTraffic();
#ifndef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
delete m_ui->startStopPushButton;
#endif
// connect signals and slots
connect(m_ui->statusPushButton, &QPushButton::clicked, this, &TrayWidget::changeStatus);
@ -161,9 +160,12 @@ TrayWidget::TrayWidget(TrayMenu *parent)
connect(m_connectionsActionGroup, &QActionGroup::triggered, this, &TrayWidget::handleConnectionSelected);
connect(m_ui->actionShowNotifications, &QAction::triggered, this, &TrayWidget::showNotifications);
connect(m_ui->actionDismissNotifications, &QAction::triggered, this, &TrayWidget::dismissNotifications);
connect(m_ui->startStopPushButton, &QPushButton::clicked, this, &TrayWidget::toggleRunning);
if (const auto *const launcher = SyncthingLauncher::mainInstance()) {
connect(launcher, &SyncthingLauncher::runningChanged, this, &TrayWidget::handleLauncherStatusChanged);
}
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
if (const auto *const service = SyncthingService::mainInstance()) {
connect(m_ui->startStopPushButton, &QPushButton::clicked, service, &SyncthingService::toggleRunning);
connect(service, &SyncthingService::systemdAvailableChanged, this, &TrayWidget::handleSystemdStatusChanged);
connect(service, &SyncthingService::stateChanged, this, &TrayWidget::handleSystemdStatusChanged);
}
@ -376,15 +378,22 @@ void TrayWidget::applySettings(const QString &connectionConfig)
// apply notification settings
settings.apply(m_notifier);
// apply systemd and launcher settings enforcing a reconnect if required and possible
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
// apply systemd settings, also enforce reconnect if required and possible
applySystemdSettings(reconnectRequired);
const auto systemdStatus = applySystemdSettings(reconnectRequired);
const auto launcherStatus = applyLauncherSettings(reconnectRequired, systemdStatus.relevant, systemdStatus.showStartStopButton);
const auto showStartStopButton = systemdStatus.showStartStopButton || launcherStatus.showStartStopButton;
const auto systemdOrLauncherRelevantForReconnect = systemdStatus.relevant || launcherStatus.relevant;
#else
// reconnect if required, not checking whether possible
if (reconnectRequired) {
const auto launcherStatus = applyLauncherSettings(reconnectRequired);
const auto showStartStopButton = launcherStatus.showStartStopButton;
const auto systemdOrLauncherRelevantForReconnect = launcherStatus.relevant;
#endif
m_ui->startStopPushButton->setVisible(showStartStopButton);
if (reconnectRequired && !systemdOrLauncherRelevantForReconnect) {
// simply enforce the reconnect for this connection if the systemd or launcher status are relevant for it
m_connection.reconnect();
}
#endif
#ifndef SYNCTHINGWIDGETS_NO_WEBVIEW
// web view
@ -538,38 +547,112 @@ void TrayWidget::updateOverallStatistics()
m_ui->localStatisticsLabel->setText(directoryStatusString(overallStats.local));
}
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
bool TrayWidget::handleSystemdStatusChanged()
void TrayWidget::toggleRunning()
{
return applySystemdSettings();
switch(m_startStopButtonTarget) {
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
case StartStopButtonTarget::Service:
if (auto *const service = SyncthingService::mainInstance()) {
service->toggleRunning();
}
break;
#endif
case StartStopButtonTarget::Launcher:
if (auto *const launcher = SyncthingLauncher::mainInstance()) {
if (launcher->isRunning()) {
launcher->terminate();
} else {
launcher->launch(Settings::values().launcher);
}
}
break;
default:
;
}
}
bool TrayWidget::applySystemdSettings(bool reconnectRequired)
Settings::Launcher::LauncherStatus TrayWidget::handleLauncherStatusChanged()
{
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
const auto systemdStatus = Settings::values().systemd.status(m_connection);
const auto launcherStatus = applyLauncherSettings(false, systemdStatus.relevant, systemdStatus.showStartStopButton);
const auto showStartStopButton = systemdStatus.showStartStopButton || launcherStatus.showStartStopButton;
#else
const auto launcherStatus = applyLauncherSettings(reconnectRequired);
const auto showStartStopButton = launcherStatus.showStartStopButton;
#endif
m_ui->startStopPushButton->setVisible(showStartStopButton);
return launcherStatus;
}
Settings::Launcher::LauncherStatus TrayWidget::applyLauncherSettings(bool reconnectRequired, bool skipApplyingToConnection, bool skipStartStopButton)
{
// update connection
const Settings::Systemd &systemdSettings(Settings::values().systemd);
bool isServiceRelevant, isServiceRunning;
tie(isServiceRelevant, isServiceRunning) = systemdSettings.apply(m_connection, m_selectedConnection, reconnectRequired);
const auto &launcherSettings = Settings::values().launcher;
const auto launcherStatus = skipApplyingToConnection
? launcherSettings.status(m_connection)
: launcherSettings.apply(m_connection, m_selectedConnection, reconnectRequired);
if (skipStartStopButton || !launcherStatus.showStartStopButton) {
return launcherStatus;
}
// update start/stop button
if (isServiceRelevant && systemdSettings.showButton) {
m_startStopButtonTarget = StartStopButtonTarget::Launcher;
if (launcherStatus.running) {
m_ui->startStopPushButton->setText(tr("Stop"));
m_ui->startStopPushButton->setToolTip(tr("Stop Syncthing instance launched via tray icon"));
m_ui->startStopPushButton->setIcon(
QIcon::fromTheme(QStringLiteral("process-stop"), QIcon(QStringLiteral(":/icons/hicolor/scalable/actions/process-stop.svg"))));
} else {
m_ui->startStopPushButton->setText(tr("Start"));
m_ui->startStopPushButton->setToolTip(tr("Launch Syncthing instance via tray icon"));
m_ui->startStopPushButton->setIcon(
QIcon::fromTheme(QStringLiteral("system-run"), QIcon(QStringLiteral(":/icons/hicolor/scalable/apps/system-run.svg"))));
}
return launcherStatus;
}
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
Settings::Systemd::ServiceStatus TrayWidget::handleSystemdStatusChanged()
{
const auto systemdStatus = applySystemdSettings();
if (systemdStatus.showStartStopButton) {
m_ui->startStopPushButton->setVisible(true);
if (isServiceRunning) {
m_ui->startStopPushButton->setText(tr("Stop"));
m_ui->startStopPushButton->setToolTip(QStringLiteral("systemctl --user stop ") + systemdSettings.syncthingUnit);
m_ui->startStopPushButton->setIcon(
QIcon::fromTheme(QStringLiteral("process-stop"), QIcon(QStringLiteral(":/icons/hicolor/scalable/actions/process-stop.svg"))));
} else {
m_ui->startStopPushButton->setText(tr("Start"));
m_ui->startStopPushButton->setToolTip(QStringLiteral("systemctl --user start ") + systemdSettings.syncthingUnit);
m_ui->startStopPushButton->setIcon(
QIcon::fromTheme(QStringLiteral("system-run"), QIcon(QStringLiteral(":/icons/hicolor/scalable/apps/system-run.svg"))));
}
return systemdStatus;
}
if (!systemdSettings.showButton || !isServiceRelevant) {
m_ui->startStopPushButton->setVisible(false);
// update the start/stop button which might now control the internal launcher
const auto launcherStatus = applyLauncherSettings(false, true, false);
m_ui->startStopPushButton->setVisible(launcherStatus.showStartStopButton);
return systemdStatus;
}
Settings::Systemd::ServiceStatus TrayWidget::applySystemdSettings(bool reconnectRequired)
{
// update connection
const auto &systemdSettings = Settings::values().systemd;
const auto serviceStatus = systemdSettings.apply(m_connection, m_selectedConnection, reconnectRequired);
if (!serviceStatus.showStartStopButton) {
return serviceStatus;
}
return isServiceRelevant && isServiceRunning;
// update start/stop button
m_startStopButtonTarget = StartStopButtonTarget::Service;
if (serviceStatus.running) {
m_ui->startStopPushButton->setText(tr("Stop"));
m_ui->startStopPushButton->setToolTip(QStringLiteral("systemctl --user stop ") + systemdSettings.syncthingUnit);
m_ui->startStopPushButton->setIcon(
QIcon::fromTheme(QStringLiteral("process-stop"), QIcon(QStringLiteral(":/icons/hicolor/scalable/actions/process-stop.svg"))));
} else {
m_ui->startStopPushButton->setText(tr("Start"));
m_ui->startStopPushButton->setToolTip(QStringLiteral("systemctl --user start ") + systemdSettings.syncthingUnit);
m_ui->startStopPushButton->setIcon(
QIcon::fromTheme(QStringLiteral("system-run"), QIcon(QStringLiteral(":/icons/hicolor/scalable/apps/system-run.svg"))));
}
return serviceStatus;
}
#endif

View File

@ -76,9 +76,12 @@ private slots:
void changeStatus();
void updateTraffic();
void updateOverallStatistics();
void toggleRunning();
Settings::Launcher::LauncherStatus handleLauncherStatusChanged();
Settings::Launcher::LauncherStatus applyLauncherSettings(bool reconnectRequired = false, bool skipApplyingToConnection = false, bool skipStartStopButton = false);
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
bool handleSystemdStatusChanged();
bool applySystemdSettings(bool reconnectRequired = false);
Settings::Systemd::ServiceStatus handleSystemdStatusChanged();
Settings::Systemd::ServiceStatus applySystemdSettings(bool reconnectRequired = false);
#endif
#ifndef SYNCTHINGWIDGETS_NO_WEBVIEW
void handleWebViewDeleted();
@ -106,6 +109,9 @@ private:
Data::SyncthingConnectionSettings *m_selectedConnection;
QMenu *m_notificationsMenu;
std::vector<Data::SyncthingLogEntry> m_notifications;
enum class StartStopButtonTarget {
None, Service, Launcher
} m_startStopButtonTarget;
static std::vector<TrayWidget *> m_instances;
};

View File

@ -1,5 +1,7 @@
#include "./syncthinglauncher.h"
#include "../settings/settings.h"
#include <QtConcurrentRun>
#include <algorithm>
@ -20,6 +22,7 @@ SyncthingLauncher::SyncthingLauncher(QObject *parent)
connect(&m_process, &SyncthingProcess::readyRead, this, &SyncthingLauncher::handleProcessReadyRead);
connect(&m_process, static_cast<void (SyncthingProcess::*)(int exitCode, QProcess::ExitStatus exitStatus)>(&SyncthingProcess::finished), this,
&SyncthingLauncher::handleProcessFinished);
connect(&m_process, &SyncthingProcess::stateChanged, this, &SyncthingLauncher::handleProcessStateChanged);
connect(&m_process, &SyncthingProcess::errorOccurred, this, &SyncthingLauncher::errorOccurred);
connect(&m_process, &SyncthingProcess::confirmKill, this, &SyncthingLauncher::confirmKill);
}
@ -57,6 +60,15 @@ void SyncthingLauncher::launch(const QString &program, const QStringList &argume
}
}
void SyncthingLauncher::launch(const Settings::Launcher &launcherSettings)
{
if (!launcherSettings.useLibSyncthing && launcherSettings.syncthingPath.isEmpty()) {
emit errorOccurred(QProcess::FailedToStart);
return;
}
launch(launcherSettings.useLibSyncthing ? QString() : launcherSettings.syncthingPath, SyncthingProcess::splitArguments(launcherSettings.syncthingArgs));
}
/*!
* \brief Launches a Syncthing instance using the internal library with the specified \a runtimeOptions.
*/
@ -102,9 +114,22 @@ void SyncthingLauncher::handleProcessReadyRead()
emit outputAvailable(m_process.readAll());
}
void SyncthingLauncher::handleProcessStateChanged(QProcess::ProcessState newState)
{
switch(newState) {
case QProcess::NotRunning:
emit runningChanged(false);
break;
case QProcess::Starting:
emit runningChanged(true);
break;
default:
;
}
}
void SyncthingLauncher::handleProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
emit runningChanged(false);
emit exited(exitCode, exitStatus);
}
@ -143,8 +168,10 @@ void SyncthingLauncher::runLibSyncthing(const LibSyncthing::RuntimeOptions &runt
{
#ifdef SYNCTHINGWIDGETS_USE_LIBSYNCTHING
LibSyncthing::setLoggingCallback(bind(&SyncthingLauncher::handleLoggingCallback, this, _1, _2, _3));
emit runningChanged(true);
const auto exitCode = LibSyncthing::runSyncthing(runtimeOptions);
emit exited(static_cast<int>(exitCode), exitCode == 0 ? QProcess::NormalExit : QProcess::CrashExit);
emit runningChanged(false);
#else
CPP_UTILITIES_UNUSED(runtimeOptions)
emit outputAvailable("libsyncthing support not enabled");
@ -156,8 +183,10 @@ void SyncthingLauncher::runLibSyncthing(const std::vector<string> &arguments)
{
#ifdef SYNCTHINGWIDGETS_USE_LIBSYNCTHING
LibSyncthing::setLoggingCallback(bind(&SyncthingLauncher::handleLoggingCallback, this, _1, _2, _3));
emit runningChanged(true);
const auto exitCode = LibSyncthing::runSyncthing(arguments);
emit exited(static_cast<int>(exitCode), exitCode == 0 ? QProcess::NormalExit : QProcess::CrashExit);
emit runningChanged(false);
#else
CPP_UTILITIES_UNUSED(arguments)
emit outputAvailable("libsyncthing support not enabled");

View File

@ -12,6 +12,10 @@ namespace LibSyncthing {
struct RuntimeOptions;
}
namespace Settings {
struct Launcher;
}
namespace Data {
class SYNCTHINGWIDGETS_EXPORT SyncthingLauncher : public QObject {
@ -43,12 +47,14 @@ Q_SIGNALS:
public Q_SLOTS:
void setUseLibSyncthing(bool useLibSyncthing);
void launch(const QString &program, const QStringList &arguments);
void launch(const Settings::Launcher &launcherSettings);
void launch(const LibSyncthing::RuntimeOptions &runtimeOptions);
void terminate();
void kill();
private Q_SLOTS:
void handleProcessReadyRead();
void handleProcessStateChanged(QProcess::ProcessState newState);
void handleProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void handleLoggingCallback(LibSyncthing::LogLevel, const char *message, std::size_t messageSize);
void runLibSyncthing(const LibSyncthing::RuntimeOptions &runtimeOptions);

View File

@ -2,14 +2,6 @@
<ui version="4.0">
<class>QtGui::LauncherOptionPage</class>
<widget class="QWidget" name="QtGui::LauncherOptionPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>507</width>
<height>391</height>
</rect>
</property>
<property name="windowTitle">
<string>Syncthing launcher</string>
</property>
@ -29,9 +21,6 @@
</item>
<item>
<widget class="QWidget" name="launcherFormWidget" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="leftMargin">
<number>30</number>
@ -75,6 +64,13 @@
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showButtonCheckBox">
<property name="text">
<string>Show start/stop button on tray for local instance</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="considerForReconnectCheckBox">
<property name="text">
@ -198,22 +194,6 @@
<include location="../resources/syncthingwidgetsicons.qrc"/>
</resources>
<connections>
<connection>
<sender>enabledCheckBox</sender>
<signal>toggled(bool)</signal>
<receiver>launcherFormWidget</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>142</x>
<y>17</y>
</hint>
<hint type="destinationlabel">
<x>142</x>
<y>65</y>
</hint>
</hints>
</connection>
<connection>
<sender>enabledCheckBox</sender>
<signal>toggled(bool)</signal>

View File

@ -74,9 +74,8 @@ std::vector<SyncthingProcess *> Launcher::allProcesses()
void Launcher::autostart() const
{
auto *const launcher(SyncthingLauncher::mainInstance());
// TODO: allow using libsyncthing
if (enabled && !syncthingPath.isEmpty() && launcher) {
launcher->launch(useLibSyncthing ? QString() : syncthingPath, SyncthingProcess::splitArguments(syncthingArgs));
if (autostartEnabled && launcher) {
launcher->launch(*this);
}
for (auto i = tools.cbegin(), end = tools.cend(); i != end; ++i) {
const ToolParameter &toolParams = i.value();
@ -95,6 +94,62 @@ void Launcher::terminate()
QtGui::SyncthingKiller(allProcesses()).waitForFinished();
}
/*!
* \brief Applies the launcher settings to the specified \a connection considering the status of the main SyncthingLauncher instance.
* \remarks
* - Called by TrayWidget when the launcher settings have been changed.
* - \a currentConnectionSettings might be nullptr.
* - Currently this is only about the auto-reconnect interval and connecting instantly.
* \returns Returns the launcher status with respect to the specified \a connection.
*/
Launcher::LauncherStatus Launcher::apply(
Data::SyncthingConnection &connection, const SyncthingConnectionSettings *currentConnectionSettings, bool reconnectRequired) const
{
auto *const launcher(SyncthingLauncher::mainInstance());
if (!launcher) {
return LauncherStatus{};
}
const auto isRelevant = connection.isLocal();
const auto isRunning = launcher->isRunning();
const auto consideredForReconnect = considerForReconnect && isRelevant;
if (currentConnectionSettings && (!considerForReconnect || !isRelevant || isRunning)) {
// ensure auto-reconnect is configured according to settings
connection.setAutoReconnectInterval(currentConnectionSettings->reconnectInterval);
} else {
// disable auto-reconnect regardless of the overall settings
connection.setAutoReconnectInterval(0);
}
// connect instantly if service is running
if (consideredForReconnect) {
// give the service (which has just started) a few seconds to initialize
constexpr auto minActiveTimeInSeconds(5);
if (reconnectRequired) {
connection.reconnectLater(minActiveTimeInSeconds * 1000);
} else if (isRunning && !connection.isConnected()) {
connection.connectLater(minActiveTimeInSeconds * 1000);
}
}
return LauncherStatus{isRelevant, isRunning, consideredForReconnect, showButton && isRelevant};
}
/*!
* \brief Returns the launcher status with respect to the specified \a connection.
*/
Launcher::LauncherStatus Launcher::status(SyncthingConnection &connection) const
{
auto *const launcher(SyncthingLauncher::mainInstance());
if (!launcher) {
return LauncherStatus{};
}
const auto isRelevant = connection.isLocal();
return LauncherStatus{
isRelevant, launcher->isRunning(), considerForReconnect && isRelevant, showButton && isRelevant
};
}
Settings &values()
{
static Settings settings;
@ -181,11 +236,12 @@ void restore()
settings.beginGroup(QStringLiteral("startup"));
auto &launcher = v.launcher;
launcher.enabled = settings.value(QStringLiteral("syncthingAutostart"), launcher.enabled).toBool();
launcher.autostartEnabled = settings.value(QStringLiteral("syncthingAutostart"), launcher.autostartEnabled).toBool();
launcher.useLibSyncthing = settings.value(QStringLiteral("useLibSyncthing"), launcher.useLibSyncthing).toBool();
launcher.syncthingPath = settings.value(QStringLiteral("syncthingPath"), launcher.syncthingPath).toString();
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();
settings.beginGroup(QStringLiteral("tools"));
for (const QString &tool : settings.childGroups()) {
settings.beginGroup(tool);
@ -270,11 +326,12 @@ void save()
settings.beginGroup(QStringLiteral("startup"));
const auto &launcher = v.launcher;
settings.setValue(QStringLiteral("syncthingAutostart"), launcher.enabled);
settings.setValue(QStringLiteral("syncthingAutostart"), launcher.autostartEnabled);
settings.setValue(QStringLiteral("useLibSyncthing"), launcher.useLibSyncthing);
settings.setValue(QStringLiteral("syncthingPath"), launcher.syncthingPath);
settings.setValue(QStringLiteral("syncthingArgs"), launcher.syncthingArgs);
settings.setValue(QStringLiteral("considerLauncherForReconnect"), launcher.considerForReconnect);
settings.setValue(QStringLiteral("showLauncherButton"), launcher.showButton);
settings.beginGroup(QStringLiteral("tools"));
for (auto i = launcher.tools.cbegin(), end = launcher.tools.cend(); i != end; ++i) {
const ToolParameter &toolParams = i.value();
@ -338,20 +395,21 @@ void Settings::apply(SyncthingNotifier &notifier) const
/*!
* \brief Applies the systemd settings to the specified \a connection considering the status of the global SyncthingService instance.
* \remarks
* - Called by SyncthingApplet and TrayWidget when the status of the SyncthingService changes.
* - Called by SyncthingApplet and TrayWidget when the status of the SyncthingService changes or the systemd settings have been changed.
* - \a currentConnectionSettings might be nullptr.
* - Currently this is only about the auto-reconnect interval and connecting instantly.
* \returns Returns whether the service is relevant and running.
* \returns Returns the service status with respect to the specified \a connection.
*/
std::tuple<bool, bool> Systemd::apply(
Systemd::ServiceStatus Systemd::apply(
Data::SyncthingConnection &connection, const SyncthingConnectionSettings *currentConnectionSettings, bool reconnectRequired) const
{
auto *const service(SyncthingService::mainInstance());
if (!service) {
return make_tuple(false, false);
return ServiceStatus{};
}
const auto isRelevant = service->isSystemdAvailable() && connection.isLocal();
const auto isRunning = service->isRunning();
const auto consideredForReconnect = considerForReconnect && isRelevant;
if (currentConnectionSettings && (!considerForReconnect || !isRelevant || isRunning)) {
// ensure auto-reconnect is configured according to settings
@ -362,7 +420,7 @@ std::tuple<bool, bool> Systemd::apply(
}
// connect instantly if service is running
if (considerForReconnect && isRelevant) {
if (consideredForReconnect) {
constexpr auto minActiveTimeInSeconds(5);
if (reconnectRequired) {
if (service->isActiveWithoutSleepFor(minActiveTimeInSeconds)) {
@ -379,11 +437,22 @@ std::tuple<bool, bool> Systemd::apply(
connection.connectLater(minActiveTimeInSeconds * 1000);
}
}
} else if (reconnectRequired) {
connection.reconnect();
}
return make_tuple(isRelevant, isRunning);
return ServiceStatus{isRelevant, isRunning, consideredForReconnect, showButton && isRelevant};
}
/*!
* \brief Returns the service status with respect to the specified \a connection.
*/
Systemd::ServiceStatus Systemd::status(SyncthingConnection &connection) const
{
auto *const service(SyncthingService::mainInstance());
if (!service) {
return ServiceStatus{};
}
const auto isRelevant = service->isSystemdAvailable() && connection.isLocal();
return ServiceStatus{isRelevant, service->isRunning(), considerForReconnect && isRelevant, showButton && isRelevant};
}
#endif

View File

@ -37,6 +37,7 @@ struct SYNCTHINGWIDGETS_EXPORT Connection {
struct SYNCTHINGWIDGETS_EXPORT NotifyOn {
bool disconnect = true;
bool internalErrors = true;
bool launcherErrors = true;
bool localSyncComplete = false;
bool remoteSyncComplete = false;
bool syncthingErrors = true;
@ -59,7 +60,7 @@ struct SYNCTHINGWIDGETS_EXPORT ToolParameter {
};
struct SYNCTHINGWIDGETS_EXPORT Launcher {
bool enabled = false;
bool autostartEnabled = false;
bool useLibSyncthing = false;
QString syncthingPath =
#ifdef PLATFORM_WINDOWS
@ -70,10 +71,21 @@ struct SYNCTHINGWIDGETS_EXPORT Launcher {
QString syncthingArgs = QStringLiteral("-no-browser -no-restart -logflags=3");
QHash<QString, ToolParameter> tools;
bool considerForReconnect = false;
bool showButton = false;
static Data::SyncthingProcess &toolProcess(const QString &tool);
static std::vector<Data::SyncthingProcess *> allProcesses();
void autostart() const;
static void terminate();
struct LauncherStatus {
bool relevant = false;
bool running = false;
bool consideredForReconnect = false;
bool showStartStopButton = false;
};
LauncherStatus apply(Data::SyncthingConnection &connection, const Data::SyncthingConnectionSettings *currentConnectionSettings,
bool preventReconnect = false) const;
LauncherStatus status(Data::SyncthingConnection &connection) const;
};
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
@ -82,10 +94,15 @@ struct SYNCTHINGWIDGETS_EXPORT Systemd {
bool showButton = false;
bool considerForReconnect = false;
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
std::tuple<bool, bool> apply(Data::SyncthingConnection &connection, const Data::SyncthingConnectionSettings *currentConnectionSettings,
struct ServiceStatus {
bool relevant = false;
bool running = false;
bool consideredForReconnect = false;
bool showStartStopButton = false;
};
ServiceStatus apply(Data::SyncthingConnection &connection, const Data::SyncthingConnectionSettings *currentConnectionSettings,
bool preventReconnect = false) const;
#endif
ServiceStatus status(Data::SyncthingConnection &connection) const;
};
#endif

View File

@ -759,18 +759,20 @@ QWidget *LauncherOptionPage::setupWidget()
auto *const widget = LauncherOptionPageBase::setupWidget();
// adjust labels to use name of additional tool instead of "Syncthing"
if (!m_tool.isEmpty()) {
const auto isSyncthing = m_tool.isEmpty();
if (!isSyncthing) {
widget->setWindowTitle(tr("%1-launcher").arg(m_tool));
ui()->enabledCheckBox->setText(tr("Launch %1 when starting the tray icon").arg(m_tool));
ui()->syncthingPathLabel->setText(tr("%1 executable").arg(m_tool));
ui()->logLabel->setText(tr("%1 log (interleaved stdout/stderr)").arg(m_tool));
// hide "consider for reconnect" and "show start/stop button on tray" checkboxes for tools
ui()->considerForReconnectCheckBox->setVisible(false);
ui()->showButtonCheckBox->setVisible(false);
}
// hide "consider for reconnect" checkbox for tools
ui()->considerForReconnectCheckBox->setVisible(m_tool.isEmpty());
// add "restore to defaults" action for arguments
if (m_tool.isEmpty()) {
// add "restore to defaults" action for Syncthing arguments
if (isSyncthing) {
m_restoreArgsButton = new IconButton(ui()->argumentsLineEdit);
m_restoreArgsButton->setPixmap(
QIcon::fromTheme(QStringLiteral("edit-undo"), QIcon(QStringLiteral(":/icons/hicolor/scalable/actions/edit-paste.svg"))).pixmap(16));
@ -794,6 +796,7 @@ QWidget *LauncherOptionPage::setupWidget()
&LauncherOptionPage::handleSyncthingExited, Qt::QueuedConnection);
connect(m_process, &SyncthingProcess::errorOccurred, this, &LauncherOptionPage::handleSyncthingError, Qt::QueuedConnection);
} else if (m_launcher) {
connect(m_launcher, &SyncthingLauncher::runningChanged, this, &LauncherOptionPage::handleSyncthingLaunched);
connect(m_launcher, &SyncthingLauncher::outputAvailable, this, &LauncherOptionPage::handleSyncthingOutputAvailable, Qt::QueuedConnection);
connect(m_launcher, &SyncthingLauncher::exited, this, &LauncherOptionPage::handleSyncthingExited, Qt::QueuedConnection);
connect(m_launcher, &SyncthingLauncher::errorOccurred, this, &LauncherOptionPage::handleSyncthingError, Qt::QueuedConnection);
@ -808,11 +811,12 @@ bool LauncherOptionPage::apply()
{
auto &settings = values().launcher;
if (m_tool.isEmpty()) {
settings.enabled = ui()->enabledCheckBox->isChecked();
settings.autostartEnabled = ui()->enabledCheckBox->isChecked();
settings.useLibSyncthing = ui()->useBuiltInVersionCheckBox->isChecked();
settings.syncthingPath = ui()->syncthingPathSelection->lineEdit()->text();
settings.syncthingArgs = ui()->argumentsLineEdit->text();
settings.considerForReconnect = ui()->considerForReconnectCheckBox->isChecked();
settings.showButton = ui()->showButtonCheckBox->isChecked();
} else {
ToolParameter &params = settings.tools[m_tool];
params.autostart = ui()->enabledCheckBox->isChecked();
@ -826,12 +830,13 @@ void LauncherOptionPage::reset()
{
const auto &settings = values().launcher;
if (m_tool.isEmpty()) {
ui()->enabledCheckBox->setChecked(settings.enabled);
ui()->enabledCheckBox->setChecked(settings.autostartEnabled);
ui()->useBuiltInVersionCheckBox->setChecked(settings.useLibSyncthing);
ui()->useBuiltInVersionCheckBox->setVisible(settings.useLibSyncthing || SyncthingLauncher::isLibSyncthingAvailable());
ui()->syncthingPathSelection->lineEdit()->setText(settings.syncthingPath);
ui()->argumentsLineEdit->setText(settings.syncthingArgs);
ui()->considerForReconnectCheckBox->setChecked(settings.considerForReconnect);
ui()->showButtonCheckBox->setChecked(settings.showButton);
} else {
const ToolParameter params = settings.tools.value(m_tool);
ui()->useBuiltInVersionCheckBox->setChecked(false);
@ -842,6 +847,17 @@ void LauncherOptionPage::reset()
}
}
void LauncherOptionPage::handleSyncthingLaunched(bool running)
{
if (!running) {
return; // Syncthing being stopped is handled elsewhere
}
ui()->launchNowPushButton->hide();
ui()->stopPushButton->show();
ui()->stopPushButton->setText(tr("Stop launched instance"));
m_kill = false;
}
void LauncherOptionPage::handleSyncthingReadyRead()
{
handleSyncthingOutputAvailable(m_process->readAll());
@ -943,10 +959,6 @@ void LauncherOptionPage::launch()
if (isRunning()) {
return;
}
ui()->launchNowPushButton->hide();
ui()->stopPushButton->show();
ui()->stopPushButton->setText(tr("Stop launched instance"));
m_kill = false;
const auto launcherSettings(values().launcher);
if (m_tool.isEmpty()) {
m_launcher->launch(launcherSettings.useLibSyncthing ? QString() : launcherSettings.syncthingPath,
@ -954,6 +966,7 @@ void LauncherOptionPage::launch()
} else {
const auto toolParams(launcherSettings.tools.value(m_tool));
m_process->startSyncthing(toolParams.path, SyncthingProcess::splitArguments(toolParams.args));
handleSyncthingLaunched(true);
}
}
@ -1018,11 +1031,22 @@ QWidget *SystemdOptionPage::setupWidget()
bool SystemdOptionPage::apply()
{
auto &settings = values().systemd;
settings.syncthingUnit = ui()->syncthingUnitLineEdit->text();
settings.showButton = ui()->showButtonCheckBox->isChecked();
settings.considerForReconnect = ui()->considerForReconnectCheckBox->isChecked();
return true;
auto &settings = values();
auto &systemdSettings = settings.systemd;
auto &launcherSettings = settings.launcher;
systemdSettings.syncthingUnit = ui()->syncthingUnitLineEdit->text();
systemdSettings.showButton = ui()->showButtonCheckBox->isChecked();
systemdSettings.considerForReconnect = ui()->considerForReconnectCheckBox->isChecked();
auto result = true;
if (systemdSettings.showButton && launcherSettings.showButton) {
errors().append(QCoreApplication::translate("QtGui::SystemdOptionPage", "It is not possible to show the start/stop button for the systemd service and the internal launcher at the same time. The systemd service precedes."));
result = false;
}
if (systemdSettings.considerForReconnect && launcherSettings.considerForReconnect) {
errors().append(QCoreApplication::translate("QtGui::SystemdOptionPage", "It is not possible to consider the systemd service and the internal launcher for reconnects at the same time. The systemd service precedes."));
result = false;
}
return result;
}
void SystemdOptionPage::reset()

View File

@ -106,6 +106,7 @@ public:
void reset() override;
private slots:
void handleSyncthingLaunched(bool running);
void handleSyncthingReadyRead();
void handleSyncthingOutputAvailable(const QByteArray &output);
void handleSyncthingExited(int exitCode, QProcess::ExitStatus exitStatus);