diff --git a/connector/syncthingconnection.cpp b/connector/syncthingconnection.cpp index 7c6e636..e8ade71 100644 --- a/connector/syncthingconnection.cpp +++ b/connector/syncthingconnection.cpp @@ -184,6 +184,15 @@ void SyncthingConnection::connect(SyncthingConnectionSettings &connectionSetting } } +void SyncthingConnection::connectLater(int milliSeconds) +{ + // skip if conneting via auto-reconnect anyways + if (autoReconnectInterval() > 0 && milliSeconds < autoReconnectInterval()) { + return; + } + QTimer::singleShot(milliSeconds, this, static_cast(&SyncthingConnection::connect)); +} + /*! * \brief Disconnects. Does nothing if not connected. */ @@ -223,6 +232,11 @@ void SyncthingConnection::reconnect(SyncthingConnectionSettings &connectionSetti reconnect(); } +void SyncthingConnection::reconnectLater(int milliSeconds) +{ + QTimer::singleShot(milliSeconds, this, static_cast(&SyncthingConnection::reconnect)); +} + /*! * \brief Internally called to reconnect; ensures currently cached config is cleared. */ diff --git a/connector/syncthingconnection.h b/connector/syncthingconnection.h index b01bfa8..f690ad1 100644 --- a/connector/syncthingconnection.h +++ b/connector/syncthingconnection.h @@ -137,9 +137,11 @@ public Q_SLOTS: bool applySettings(SyncthingConnectionSettings &connectionSettings); void connect(); void connect(SyncthingConnectionSettings &connectionSettings); + void connectLater(int milliSeconds); void disconnect(); void reconnect(); void reconnect(SyncthingConnectionSettings &connectionSettings); + void reconnectLater(int milliSeconds); bool pauseDevice(const QStringList &devIds); bool pauseAllDevs(); bool resumeDevice(const QStringList &devIds); diff --git a/connector/syncthingnotifier.cpp b/connector/syncthingnotifier.cpp index 51ff132..8587642 100644 --- a/connector/syncthingnotifier.cpp +++ b/connector/syncthingnotifier.cpp @@ -1,5 +1,6 @@ #include "./syncthingnotifier.h" #include "./syncthingconnection.h" +#include "./syncthingprocess.h" #include "./utils.h" #ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD @@ -30,8 +31,10 @@ SyncthingNotifier::SyncthingNotifier(const SyncthingConnection &connection, QObj #ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD , m_service(syncthingService()) #endif + , m_process(syncthingProcess()) , m_enabledNotifications(SyncthingHighLevelNotification::None) , m_previousStatus(SyncthingStatus::Disconnected) + , m_ignoreInavailabilityAfterStart(15) , m_initialized(false) { connect(&connection, &SyncthingConnection::statusChanged, this, &SyncthingNotifier::handleStatusChangedEvent); @@ -54,6 +57,41 @@ void SyncthingNotifier::handleStatusChangedEvent(SyncthingStatus newStatus) m_previousStatus = newStatus; } +bool SyncthingNotifier::isDisconnectRelevant() const +{ + // skip disconnect if not initialized + if (!m_initialized) { + return false; + } + + // skip further considerations if connection is remote + if (!m_connection.isLocal()) { + return true; + } + + // consider process/launcher or systemd unit status + if (m_process.isManuallyStopped()) { + return false; + } +#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD + if (m_service.isManuallyStopped()) { + return false; + } + + // ignore inavailability after start or standby-wakeup + if (m_ignoreInavailabilityAfterStart) { + if (m_process.isRunning() && !m_service.isActiveWithoutSleepFor(m_process.activeSince(), m_ignoreInavailabilityAfterStart)) { + return false; + } + if (m_service.isRunning() && !m_service.isActiveWithoutSleepFor(m_ignoreInavailabilityAfterStart)) { + return false; + } + } +#endif + + return true; +} + /*! * \brief Emits the connected() or disconnected() signal. */ @@ -66,11 +104,7 @@ void SyncthingNotifier::emitConnectedAndDisconnected(SyncthingStatus newStatus) switch (newStatus) { case SyncthingStatus::Disconnected: - if (m_initialized -#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD - && m_service.isManuallyStopped() -#endif - ) { + if (isDisconnectRelevant()) { emit disconnected(); } break; diff --git a/connector/syncthingnotifier.h b/connector/syncthingnotifier.h index bdfd501..5e6ed56 100644 --- a/connector/syncthingnotifier.h +++ b/connector/syncthingnotifier.h @@ -14,6 +14,7 @@ namespace Data { enum class SyncthingStatus; class SyncthingConnection; class SyncthingService; +class SyncthingProcess; struct SyncthingDir; struct SyncthingDev; @@ -48,14 +49,17 @@ constexpr bool operator&(SyncthingHighLevelNotification lhs, SyncthingHighLevelN class LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingNotifier : public QObject { Q_OBJECT Q_PROPERTY(SyncthingHighLevelNotification enabledNotifications READ enabledNotifications WRITE setEnabledNotifications) + Q_PROPERTY(bool ignoreInavailabilityAfterStart READ ignoreInavailabilityAfterStart WRITE setIgnoreInavailabilityAfterStart) public: SyncthingNotifier(const SyncthingConnection &connection, QObject *parent = nullptr); SyncthingHighLevelNotification enabledNotifications() const; + unsigned int ignoreInavailabilityAfterStart() const; public Q_SLOTS: void setEnabledNotifications(SyncthingHighLevelNotification enabledNotifications); + void setIgnoreInavailabilityAfterStart(unsigned int seconds); Q_SIGNALS: ///! \brief Emitted when the connection status changes. Also provides the previous status. @@ -71,6 +75,7 @@ private Q_SLOTS: void handleStatusChangedEvent(SyncthingStatus newStatus); private: + bool isDisconnectRelevant() const; void emitConnectedAndDisconnected(SyncthingStatus newStatus); void emitSyncComplete(ChronoUtilities::DateTime when, const SyncthingDir &dir, int index, const SyncthingDev *remoteDev); @@ -78,8 +83,10 @@ private: #ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD const SyncthingService &m_service; #endif + const SyncthingProcess &m_process; SyncthingHighLevelNotification m_enabledNotifications; SyncthingStatus m_previousStatus; + unsigned int m_ignoreInavailabilityAfterStart; bool m_initialized; }; @@ -99,6 +106,22 @@ inline void SyncthingNotifier::setEnabledNotifications(SyncthingHighLevelNotific m_enabledNotifications = enabledNotifications; } +/*! + * \brief Returns the number of seconds after startup or standby-wakeup to supress disconnect notifications. + */ +inline unsigned int SyncthingNotifier::ignoreInavailabilityAfterStart() const +{ + return m_ignoreInavailabilityAfterStart; +} + +/*! + * \brief Prevents disconnect notifications in the first \a seconds after startup or standby-wakeup. + */ +inline void SyncthingNotifier::setIgnoreInavailabilityAfterStart(unsigned int seconds) +{ + m_ignoreInavailabilityAfterStart = seconds; +} + } // namespace Data #endif // DATA_SYNCTHINGNOTIFIER_H diff --git a/connector/syncthingprocess.cpp b/connector/syncthingprocess.cpp index 2bfb00a..a063a3b 100644 --- a/connector/syncthingprocess.cpp +++ b/connector/syncthingprocess.cpp @@ -2,52 +2,68 @@ #include +using namespace ChronoUtilities; + namespace Data { SyncthingProcess::SyncthingProcess(QObject *parent) : QProcess(parent) + , m_manuallyStopped(false) { setProcessChannelMode(QProcess::MergedChannels); + connect(this, &SyncthingProcess::started, this, &SyncthingProcess::handleStarted); connect(this, static_cast(&SyncthingProcess::finished), this, &SyncthingProcess::handleFinished); } void SyncthingProcess::restartSyncthing(const QString &cmd) { - if (state() == QProcess::Running) { - m_cmd = cmd; - // give Syncthing 5 seconds to terminate, otherwise kill it - QTimer::singleShot(5000, this, &SyncthingProcess::killToRestart); - terminate(); - } else { + if (!isRunning()) { startSyncthing(cmd); + return; } + + m_cmd = cmd; + m_manuallyStopped = true; + // give Syncthing 5 seconds to terminate, otherwise kill it + QTimer::singleShot(5000, this, &SyncthingProcess::killToRestart); + terminate(); } void SyncthingProcess::startSyncthing(const QString &cmd) { - if (state() == QProcess::NotRunning) { - if (cmd.isEmpty()) { - start(QProcess::ReadOnly); - } else { - start(cmd, QProcess::ReadOnly); - } + if (isRunning()) { + return; + } + m_manuallyStopped = false; + if (cmd.isEmpty()) { + start(QProcess::ReadOnly); + } else { + start(cmd, QProcess::ReadOnly); } } void SyncthingProcess::stopSyncthing() { - if (state() == QProcess::Running) { - // give Syncthing 5 seconds to terminate, otherwise kill it - QTimer::singleShot(5000, this, &SyncthingProcess::kill); - terminate(); + if (!isRunning()) { + return; } + m_manuallyStopped = true; + // give Syncthing 5 seconds to terminate, otherwise kill it + QTimer::singleShot(5000, this, &SyncthingProcess::kill); + terminate(); +} + +void SyncthingProcess::handleStarted() +{ + m_activeSince = DateTime::gmtNow(); } void SyncthingProcess::handleFinished(int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitCode) Q_UNUSED(exitStatus) + m_activeSince = DateTime(); if (!m_cmd.isEmpty()) { startSyncthing(m_cmd); m_cmd.clear(); diff --git a/connector/syncthingprocess.h b/connector/syncthingprocess.h index e136fec..2ed2d89 100644 --- a/connector/syncthingprocess.h +++ b/connector/syncthingprocess.h @@ -3,14 +3,24 @@ #include "./global.h" +#include + #include namespace Data { class LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingProcess : public QProcess { Q_OBJECT + Q_PROPERTY(bool running READ isRunning) + Q_PROPERTY(ChronoUtilities::DateTime activeSince READ activeSince) + Q_PROPERTY(bool manuallyStopped READ isManuallyStopped) + public: explicit SyncthingProcess(QObject *parent = nullptr); + bool isRunning() const; + ChronoUtilities::DateTime activeSince() const; + bool isActiveFor(unsigned int atLeastSeconds) const; + bool isManuallyStopped() const; public Q_SLOTS: void restartSyncthing(const QString &cmd); @@ -18,13 +28,36 @@ public Q_SLOTS: void stopSyncthing(); private Q_SLOTS: + void handleStarted(); void handleFinished(int exitCode, QProcess::ExitStatus exitStatus); void killToRestart(); private: QString m_cmd; + ChronoUtilities::DateTime m_activeSince; + bool m_manuallyStopped; }; +inline bool SyncthingProcess::isRunning() const +{ + return state() != QProcess::NotRunning; +} + +inline ChronoUtilities::DateTime SyncthingProcess::activeSince() const +{ + return m_activeSince; +} + +inline bool SyncthingProcess::isActiveFor(unsigned int atLeastSeconds) const +{ + return !m_activeSince.isNull() && (ChronoUtilities::DateTime::gmtNow() - m_activeSince).totalSeconds() > atLeastSeconds; +} + +inline bool SyncthingProcess::isManuallyStopped() const +{ + return m_manuallyStopped; +} + SyncthingProcess LIB_SYNCTHING_CONNECTOR_EXPORT &syncthingProcess(); } // namespace Data diff --git a/connector/syncthingservice.cpp b/connector/syncthingservice.cpp index 2e4174e..944a90e 100644 --- a/connector/syncthingservice.cpp +++ b/connector/syncthingservice.cpp @@ -109,18 +109,17 @@ bool SyncthingService::isUnitAvailable() const return m_unit && m_unit->isValid(); } -bool SyncthingService::isActiveWithoutSleepFor(unsigned int atLeastSeconds) const +bool SyncthingService::isActiveWithoutSleepFor(DateTime activeSince, unsigned int atLeastSeconds) { if (!atLeastSeconds) { return true; } - if (m_activeSince.isNull() || s_fallingAsleep) { + if (activeSince.isNull() || s_fallingAsleep) { return false; } const DateTime now(DateTime::gmtNow()); - return ((now - m_activeSince).totalSeconds() > atLeastSeconds) - && (s_lastWakeUp.isNull() || ((now - s_lastWakeUp).totalSeconds() > atLeastSeconds)); + return ((now - activeSince).totalSeconds() > atLeastSeconds) && (s_lastWakeUp.isNull() || ((now - s_lastWakeUp).totalSeconds() > atLeastSeconds)); } void SyncthingService::setRunning(bool running) diff --git a/connector/syncthingservice.h b/connector/syncthingservice.h index 1502455..fae4da3 100644 --- a/connector/syncthingservice.h +++ b/connector/syncthingservice.h @@ -56,6 +56,7 @@ public: ChronoUtilities::DateTime activeSince() const; bool isActiveFor(unsigned int atLeastSeconds) const; bool isActiveWithoutSleepFor(unsigned int atLeastSeconds) const; + static bool isActiveWithoutSleepFor(ChronoUtilities::DateTime activeSince, unsigned int atLeastSeconds); static ChronoUtilities::DateTime lastWakeUp(); const QString &unitFileState() const; const QString &description() const; @@ -188,6 +189,11 @@ inline bool SyncthingService::isActiveFor(unsigned int atLeastSeconds) const return !m_activeSince.isNull() && (ChronoUtilities::DateTime::gmtNow() - m_activeSince).totalSeconds() > atLeastSeconds; } +inline bool SyncthingService::isActiveWithoutSleepFor(unsigned int atLeastSeconds) const +{ + return isActiveWithoutSleepFor(m_activeSince, atLeastSeconds); +} + inline ChronoUtilities::DateTime SyncthingService::lastWakeUp() { return s_lastWakeUp; diff --git a/plasmoid/lib/syncthingapplet.cpp b/plasmoid/lib/syncthingapplet.cpp index 00f9d55..64e8a1c 100644 --- a/plasmoid/lib/syncthingapplet.cpp +++ b/plasmoid/lib/syncthingapplet.cpp @@ -101,6 +101,7 @@ void SyncthingApplet::init() #ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD SyncthingService &service = syncthingService(); service.setUnitName(Settings::values().systemd.syncthingUnit); + connect(&service, &SyncthingService::systemdAvailableChanged, this, &SyncthingApplet::handleSystemdStatusChanged); connect(&service, &SyncthingService::errorOccurred, this, &SyncthingApplet::handleSystemdServiceError); #endif @@ -156,10 +157,11 @@ Data::SyncthingConnectionSettings *SyncthingApplet::connectionConfig(int index) void SyncthingApplet::setCurrentConnectionConfigIndex(int index) { - auto &settings = Settings::values().connection; - if (index != m_currentConnectionConfig && index >= 0 && static_cast(index) <= settings.secondary.size()) { - auto &selectedConfig = index == 0 ? settings.primary : settings.secondary[static_cast(index) - 1]; - m_connection.connect(selectedConfig); + auto &settings = Settings::values(); + bool reconnectRequired = false; + if (index != m_currentConnectionConfig && index >= 0 && static_cast(index) <= settings.connection.secondary.size()) { + auto &selectedConfig = index == 0 ? settings.connection.primary : settings.connection.secondary[static_cast(index) - 1]; + reconnectRequired = m_connection.applySettings(selectedConfig); #ifndef SYNCTHINGWIDGETS_NO_WEBVIEW if (m_webViewDlg) { m_webViewDlg->applySettings(selectedConfig); @@ -169,9 +171,18 @@ void SyncthingApplet::setCurrentConnectionConfigIndex(int index) emit currentConnectionConfigIndexChanged(m_currentConnectionConfig = index); emit localChanged(); } + + // apply systemd settings, reconnect if required and possible +#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD + settings.systemd.apply(m_connection, currentConnectionConfig(), reconnectRequired); +#else + if (reconnectRequired || !m_connection.isConnected()) { + m_connection.reconnect(); + } +#endif } -bool SyncthingApplet::isStartStopForServiceEnabled() const +bool SyncthingApplet::isStartStopEnabled() const { return Settings::values().systemd.showButton; } @@ -311,7 +322,7 @@ void SyncthingApplet::handleSettingsChanged() const auto &settings(Settings::values()); // apply notifiction settings - settings.notifyOn.apply(m_notifier); + settings.apply(m_notifier); // apply appearance settings setSize(config.readEntry("size", QSize(25, 25))); @@ -349,11 +360,12 @@ void SyncthingApplet::handleDevicesChanged() void SyncthingApplet::handleInternalError( const QString &errorMsg, SyncthingErrorCategory category, int networkError, const QNetworkRequest &request, const QByteArray &response) { - if (InternalError::isRelevant(m_connection, category, networkError)) { - InternalError error(errorMsg, request.url(), response); - m_dbusNotifier.showInternalError(error); - ErrorViewDialog::addError(move(error)); + if (!InternalError::isRelevant(m_connection, category, networkError)) { + return; } + InternalError error(errorMsg, request.url(), response); + m_dbusNotifier.showInternalError(error); + ErrorViewDialog::addError(move(error)); } void SyncthingApplet::handleErrorsCleared() @@ -388,6 +400,14 @@ void SyncthingApplet::handleSystemdServiceError(const QString &context, const QS handleInternalError(tr("D-Bus error - unable to ") % context % QChar('\n') % name % QChar(':') % message, SyncthingErrorCategory::SpecificRequest, QNetworkReply::NoError, QNetworkRequest(), QByteArray()); } + +#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD +void SyncthingApplet::handleSystemdStatusChanged() +{ + Settings::values().systemd.apply(m_connection, currentConnectionConfig()); +} +#endif + } // namespace Plasmoid K_EXPORT_PLASMA_APPLET_WITH_JSON(syncthing, Plasmoid::SyncthingApplet, "metadata.json") diff --git a/plasmoid/lib/syncthingapplet.h b/plasmoid/lib/syncthingapplet.h index 5e6199b..d853a6d 100644 --- a/plasmoid/lib/syncthingapplet.h +++ b/plasmoid/lib/syncthingapplet.h @@ -56,7 +56,7 @@ class SyncthingApplet : public Plasma::Applet { Q_PROPERTY(QString currentConnectionConfigName READ currentConnectionConfigName NOTIFY currentConnectionConfigIndexChanged) Q_PROPERTY(int currentConnectionConfigIndex READ currentConnectionConfigIndex WRITE setCurrentConnectionConfigIndex NOTIFY currentConnectionConfigIndexChanged) - Q_PROPERTY(bool startStopForServiceEnabled READ isStartStopForServiceEnabled NOTIFY settingsChanged) + Q_PROPERTY(bool startStopEnabled READ isStartStopEnabled NOTIFY settingsChanged) Q_PROPERTY(QSize size READ size WRITE setSize NOTIFY sizeChanged) Q_PROPERTY(bool notificationsAvailable READ areNotificationsAvailable NOTIFY notificationsAvailableChanged) @@ -82,7 +82,7 @@ public: Data::SyncthingConnectionSettings *currentConnectionConfig(); Data::SyncthingConnectionSettings *connectionConfig(int index); void setCurrentConnectionConfigIndex(int index); - bool isStartStopForServiceEnabled() const; + bool isStartStopEnabled() const; QSize size() const; void setSize(const QSize &size); bool areNotificationsAvailable() const; @@ -133,6 +133,9 @@ private Q_SLOTS: #endif void handleNewNotification(ChronoUtilities::DateTime when, const QString &msg); void handleSystemdServiceError(const QString &context, const QString &name, const QString &message); +#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD + void handleSystemdStatusChanged(); +#endif private: Dialogs::AboutDialog *m_aboutDlg; diff --git a/plasmoid/package/contents/ui/FullRepresentation.qml b/plasmoid/package/contents/ui/FullRepresentation.qml index 696d23e..9f54a6b 100644 --- a/plasmoid/package/contents/ui/FullRepresentation.qml +++ b/plasmoid/package/contents/ui/FullRepresentation.qml @@ -280,7 +280,7 @@ ColumnLayout { var nativeInterface = plasmoid.nativeInterface // the systemd unit status is only relevant when connected to the local instance if (!nativeInterface.local - || !nativeInterface.startStopForServiceEnabled) { + || !nativeInterface.startStopEnabled) { return "irrelevant" } // show start/stop button only when the configured unit is available diff --git a/tray/application/main.cpp b/tray/application/main.cpp index add61b2..45a387d 100644 --- a/tray/application/main.cpp +++ b/tray/application/main.cpp @@ -48,68 +48,72 @@ void handleSystemdServiceError(const QString &context, const QString &name, cons int initSyncthingTray(bool windowed, bool waitForTray, const char *connectionConfig) { - auto &v = Settings::values(); -#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD - SyncthingService &service = syncthingService(); - service.setUnitName(v.systemd.syncthingUnit); - QObject::connect(&service, &SyncthingService::errorOccurred, &handleSystemdServiceError); -#endif - const QString connectionConfigQStr = connectionConfig ? QString::fromLocal8Bit(connectionConfig) : QString(); + // get settings + auto &settings = Settings::values(); + const auto connectionConfigQStr(connectionConfig ? QString::fromLocal8Bit(connectionConfig) : QString()); + + // handle "windowed" case if (windowed) { - v.launcher.autostart(); - auto *trayWidget = new TrayWidget(connectionConfigQStr); + settings.launcher.autostart(); + auto *const trayWidget = new TrayWidget(connectionConfigQStr); trayWidget->setAttribute(Qt::WA_DeleteOnClose); trayWidget->show(); - } else { -#ifndef QT_NO_SYSTEMTRAYICON - if (QSystemTrayIcon::isSystemTrayAvailable() || waitForTray) { - v.launcher.autostart(); - auto *trayIcon = new TrayIcon(connectionConfigQStr); - trayIcon->show(); - if (v.firstLaunch) { - QMessageBox msgBox; - msgBox.setIcon(QMessageBox::Information); - msgBox.setText( - QCoreApplication::translate("main", "You must configure how to connect to Syncthing when using Syncthing Tray the first time.")); - msgBox.setInformativeText(QCoreApplication::translate( - "main", "Note that the settings dialog allows importing URL, credentials and API-key from the local Syncthing configuration.")); - msgBox.exec(); - trayIcon->trayMenu().widget()->showSettingsDialog(); - } - } else { - QMessageBox::critical(nullptr, QApplication::applicationName(), - QApplication::translate("main", - "The system tray is (currently) not available. You could open the tray menu as a regular window using the -w flag, though.")); - return -1; - } -#else - QMessageBox::critical(nullptr, QApplication::applicationName(), - QApplication::translate("main", - "The Qt libraries have not been built with tray icon support. You could open the tray menu as a regular " - "window using the -w flag, though.")); - return -2; -#endif + return 0; } + +#ifndef QT_NO_SYSTEMTRAYICON + // check whether system tray is available + if (!QSystemTrayIcon::isSystemTrayAvailable() && !waitForTray) { + QMessageBox::critical(nullptr, QApplication::applicationName(), + QApplication::translate( + "main", "The system tray is (currently) not available. You could open the tray menu as a regular window using the -w flag, though.")); + return -1; + } + + // show tray icon + settings.launcher.autostart(); + auto *const trayIcon = new TrayIcon(connectionConfigQStr, QApplication::instance()); + trayIcon->show(); + if (!settings.firstLaunch) { + return 0; + } + + // show "first launch" message box + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Information); + msgBox.setText(QCoreApplication::translate("main", "You must configure how to connect to Syncthing when using Syncthing Tray the first time.")); + msgBox.setInformativeText(QCoreApplication::translate( + "main", "Note that the settings dialog allows importing URL, credentials and API-key from the local Syncthing configuration.")); + msgBox.exec(); + trayIcon->trayMenu().widget().showSettingsDialog(); return 0; + +#else + // show error if system tray is not supported by Qt + QMessageBox::critical(nullptr, QApplication::applicationName(), + QApplication::translate("main", + "The Qt libraries have not been built with tray icon support. You could open the tray menu as a regular " + "window using the -w flag, though.")); + return -2; +#endif } void trigger(bool tray, bool webUi) { - if (!TrayWidget::instances().empty() && (tray || webUi)) { - TrayWidget *trayWidget = TrayWidget::instances().front(); - if (webUi) { - trayWidget->showWebUi(); - } - if (tray) { - trayWidget->showAtCursor(); - } + if (TrayWidget::instances().empty() || !(tray || webUi)) { + return; + } + auto *const trayWidget = TrayWidget::instances().front(); + if (webUi) { + trayWidget->showWebUi(); + } + if (tray) { + trayWidget->showAtCursor(); } } int runApplication(int argc, const char *const *argv) { - static bool firstRun = true; - // setup argument parser SET_APPLICATION_INFO; CMD_UTILS_CONVERT_ARGS_TO_UTF8; @@ -140,44 +144,54 @@ int runApplication(int argc, const char *const *argv) if (!qtConfigArgs.qtWidgetsGuiArg().isPresent()) { return 0; } + + // check whether runApplication() has been called for the first time + static auto firstRun = true; if (firstRun) { firstRun = false; + // do first-time initializations SET_QT_APPLICATION_INFO; QApplication application(argc, const_cast(argv)); QGuiApplication::setQuitOnLastWindowClosed(false); SingleInstance singleInstance(argc, argv); networkAccessManager().setParent(&singleInstance); QObject::connect(&singleInstance, &SingleInstance::newInstance, &runApplication); - Settings::restore(); Settings::values().qt.apply(); qtConfigArgs.applySettings(true); LOAD_QT_TRANSLATIONS; +#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD + SyncthingService &service = syncthingService(); + service.setUnitName(Settings::values().systemd.syncthingUnit); + QObject::connect(&service, &SyncthingService::errorOccurred, &handleSystemdServiceError); +#endif - int res = initSyncthingTray(windowedArg.isPresent(), waitForTrayArg.isPresent(), connectionArg.firstValue()); + // show (first) tray icon and enter main event loop + auto res = initSyncthingTray(windowedArg.isPresent(), waitForTrayArg.isPresent(), connectionArg.firstValue()); if (!res) { trigger(triggerArg.isPresent(), showWebUiArg.isPresent()); res = application.exec(); } + // perform cleanup, then terminate Settings::Launcher::terminate(); Settings::save(); return res; - } else { - if (!TrayWidget::instances().empty() && (showWebUiArg.isPresent() || triggerArg.isPresent())) { - // if --webui or --trigger is present don't create a new tray icon, just trigger actions - trigger(triggerArg.isPresent(), showWebUiArg.isPresent()); - } else { - const int res = initSyncthingTray(windowedArg.isPresent(), waitForTrayArg.isPresent(), connectionArg.firstValue()); - if (!res) { - trigger(triggerArg.isPresent(), showWebUiArg.isPresent()); - } - return res; - } } - return 0; + // trigger actions if --webui or --trigger is present but don't create a new tray icon + if (!TrayWidget::instances().empty() && (showWebUiArg.isPresent() || triggerArg.isPresent())) { + trigger(triggerArg.isPresent(), showWebUiArg.isPresent()); + return 0; + } + + // create new/additional tray icon + const auto res = initSyncthingTray(windowedArg.isPresent(), waitForTrayArg.isPresent(), connectionArg.firstValue()); + if (!res) { + trigger(triggerArg.isPresent(), showWebUiArg.isPresent()); + } + return res; } int main(int argc, char *argv[]) diff --git a/tray/gui/trayicon.cpp b/tray/gui/trayicon.cpp index 553e87d..254554e 100644 --- a/tray/gui/trayicon.cpp +++ b/tray/gui/trayicon.cpp @@ -45,28 +45,28 @@ TrayIcon::TrayIcon(const QString &connectionConfig, QObject *parent) connect(m_contextMenu.addAction(QIcon::fromTheme(QStringLiteral("internet-web-browser"), QIcon(QStringLiteral(":/icons/hicolor/scalable/apps/internet-web-browser.svg"))), tr("Web UI")), - &QAction::triggered, m_trayMenu.widget(), &TrayWidget::showWebUi); + &QAction::triggered, &m_trayMenu.widget(), &TrayWidget::showWebUi); connect(m_contextMenu.addAction( QIcon::fromTheme(QStringLiteral("preferences-other"), QIcon(QStringLiteral(":/icons/hicolor/scalable/apps/preferences-other.svg"))), tr("Settings")), - &QAction::triggered, m_trayMenu.widget(), &TrayWidget::showSettingsDialog); + &QAction::triggered, &m_trayMenu.widget(), &TrayWidget::showSettingsDialog); connect(m_contextMenu.addAction( QIcon::fromTheme(QStringLiteral("folder-sync"), QIcon(QStringLiteral(":/icons/hicolor/scalable/actions/folder-sync.svg"))), tr("Rescan all")), - &QAction::triggered, &m_trayMenu.widget()->connection(), &SyncthingConnection::rescanAllDirs); + &QAction::triggered, &m_trayMenu.widget().connection(), &SyncthingConnection::rescanAllDirs); connect(m_contextMenu.addAction( QIcon::fromTheme(QStringLiteral("text-x-generic"), QIcon(QStringLiteral(":/icons/hicolor/scalable/mimetypes/text-x-generic.svg"))), tr("Log")), - &QAction::triggered, m_trayMenu.widget(), &TrayWidget::showLog); + &QAction::triggered, &m_trayMenu.widget(), &TrayWidget::showLog); m_errorsAction = m_contextMenu.addAction( QIcon::fromTheme(QStringLiteral("emblem-error"), QIcon(QStringLiteral(":/icons/hicolor/scalable/emblems/8/emblem-error.svg"))), tr("Show internal errors")); m_errorsAction->setVisible(false); connect(m_errorsAction, &QAction::triggered, this, &TrayIcon::showInternalErrorsDialog); - m_contextMenu.addMenu(m_trayMenu.widget()->connectionsMenu()); + m_contextMenu.addMenu(m_trayMenu.widget().connectionsMenu()); connect(m_contextMenu.addAction( QIcon::fromTheme(QStringLiteral("help-about"), QIcon(QStringLiteral(":/icons/hicolor/scalable/apps/help-about.svg"))), tr("About")), - &QAction::triggered, m_trayMenu.widget(), &TrayWidget::showAboutDialog); + &QAction::triggered, &m_trayMenu.widget(), &TrayWidget::showAboutDialog); m_contextMenu.addSeparator(); connect(m_contextMenu.addAction( QIcon::fromTheme(QStringLiteral("window-close"), QIcon(QStringLiteral(":/icons/hicolor/scalable/actions/window-close.svg"))), @@ -75,8 +75,8 @@ TrayIcon::TrayIcon(const QString &connectionConfig, QObject *parent) setContextMenu(&m_contextMenu); // connect signals and slots - const SyncthingConnection &connection = m_trayMenu.widget()->connection(); - const SyncthingNotifier ¬ifier = m_trayMenu.widget()->notifier(); + const SyncthingConnection &connection = m_trayMenu.widget().connection(); + const SyncthingNotifier ¬ifier = m_trayMenu.widget().notifier(); connect(this, &TrayIcon::activated, this, &TrayIcon::handleActivated); connect(this, &TrayIcon::messageClicked, this, &TrayIcon::handleMessageClicked); connect(&connection, &SyncthingConnection::error, this, &TrayIcon::showInternalError); @@ -89,8 +89,8 @@ TrayIcon::TrayIcon(const QString &connectionConfig, QObject *parent) #ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS connect(&m_dbusNotifier, &DBusStatusNotifier::connectRequested, &connection, static_cast(&SyncthingConnection::connect)); - connect(&m_dbusNotifier, &DBusStatusNotifier::dismissNotificationsRequested, m_trayMenu.widget(), &TrayWidget::dismissNotifications); - connect(&m_dbusNotifier, &DBusStatusNotifier::showNotificationsRequested, m_trayMenu.widget(), &TrayWidget::showNotifications); + connect(&m_dbusNotifier, &DBusStatusNotifier::dismissNotificationsRequested, &m_trayMenu.widget(), &TrayWidget::dismissNotifications); + connect(&m_dbusNotifier, &DBusStatusNotifier::showNotificationsRequested, &m_trayMenu.widget(), &TrayWidget::showNotifications); connect(&m_dbusNotifier, &DBusStatusNotifier::errorDetailsRequested, this, &TrayIcon::showInternalErrorsDialog); connect(¬ifier, &SyncthingNotifier::connected, &m_dbusNotifier, &DBusStatusNotifier::hideDisconnect); #endif @@ -120,7 +120,7 @@ void TrayIcon::handleActivated(QSystemTrayIcon::ActivationReason reason) // can't catch that event on Plasma 5 anyways break; case QSystemTrayIcon::MiddleClick: - m_trayMenu.widget()->showWebUi(); + m_trayMenu.widget().showWebUi(); break; case QSystemTrayIcon::Trigger: { m_trayMenu.showAtCursor(); @@ -136,7 +136,7 @@ void TrayIcon::handleMessageClicked() case TrayIconMessageClickedAction::None: return; case TrayIconMessageClickedAction::DismissNotification: - m_trayMenu.widget()->dismissNotifications(); + m_trayMenu.widget().dismissNotifications(); break; case TrayIconMessageClickedAction::ShowInternalErrors: showInternalErrorsDialog(); @@ -178,20 +178,21 @@ void TrayIcon::handleErrorsCleared() void TrayIcon::showInternalError( const QString &errorMsg, SyncthingErrorCategory category, int networkError, const QNetworkRequest &request, const QByteArray &response) { - if (InternalError::isRelevant(m_trayMenu.widget()->connection(), category, networkError)) { - InternalError error(errorMsg, request.url(), response); -#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS - if (Settings::values().dbusNotifications) { - m_dbusNotifier.showInternalError(error); - } else -#endif - { - m_messageClickedAction = TrayIconMessageClickedAction::ShowInternalErrors; - showMessage(tr("Error"), errorMsg, QSystemTrayIcon::Critical); - } - ErrorViewDialog::addError(move(error)); - m_errorsAction->setVisible(true); + if (!InternalError::isRelevant(m_trayMenu.widget().connection(), category, networkError)) { + return; } + InternalError error(errorMsg, request.url(), response); +#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS + if (Settings::values().dbusNotifications) { + m_dbusNotifier.showInternalError(error); + } else +#endif + { + m_messageClickedAction = TrayIconMessageClickedAction::ShowInternalErrors; + showMessage(tr("Error"), errorMsg, QSystemTrayIcon::Critical); + } + ErrorViewDialog::addError(move(error)); + m_errorsAction->setVisible(true); } void TrayIcon::showSyncthingNotification(ChronoUtilities::DateTime when, const QString &message) @@ -215,7 +216,7 @@ void TrayIcon::showSyncthingNotification(ChronoUtilities::DateTime when, const Q void TrayIcon::updateStatusIconAndText() { - const StatusInfo statusInfo(trayMenu().widget()->connection()); + const StatusInfo statusInfo(trayMenu().widget().connection()); if (statusInfo.additionalStatusText().isEmpty()) { setToolTip(statusInfo.statusText()); } else { diff --git a/tray/gui/traymenu.cpp b/tray/gui/traymenu.cpp index 6afb25e..179ba9a 100644 --- a/tray/gui/traymenu.cpp +++ b/tray/gui/traymenu.cpp @@ -15,8 +15,9 @@ TrayMenu::TrayMenu(const QString &connectionConfig, TrayIcon *trayIcon, QWidget : QMenu(parent) , m_trayIcon(trayIcon) { - auto *menuLayout = new QHBoxLayout; - menuLayout->setMargin(0), menuLayout->setSpacing(0); + auto *const menuLayout = new QHBoxLayout; + menuLayout->setMargin(0); + menuLayout->setSpacing(0); menuLayout->addWidget(m_trayWidget = new TrayWidget(connectionConfig, this)); setLayout(menuLayout); setPlatformMenu(nullptr); diff --git a/tray/gui/traymenu.h b/tray/gui/traymenu.h index 5cd48a5..0817eb8 100644 --- a/tray/gui/traymenu.h +++ b/tray/gui/traymenu.h @@ -15,7 +15,7 @@ public: TrayMenu(const QString &connectionConfig = QString(), TrayIcon *trayIcon = nullptr, QWidget *parent = nullptr); QSize sizeHint() const; - TrayWidget *widget(); + TrayWidget &widget(); TrayIcon *icon(); public slots: @@ -26,9 +26,9 @@ private: TrayIcon *m_trayIcon; }; -inline TrayWidget *TrayMenu::widget() +inline TrayWidget &TrayMenu::widget() { - return m_trayWidget; + return *m_trayWidget; } inline TrayIcon *TrayMenu::icon() diff --git a/tray/gui/traywidget.cpp b/tray/gui/traywidget.cpp index d5fb51d..6f4b85c 100644 --- a/tray/gui/traywidget.cpp +++ b/tray/gui/traywidget.cpp @@ -365,15 +365,13 @@ void TrayWidget::applySettings(const QString &connectionConfig) const bool reconnectRequired = m_connection.applySettings(*m_selectedConnection); // apply notifiction settings - settings.notifyOn.apply(m_notifier); + settings.apply(m_notifier); #ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD - // reconnect to apply settings considering systemd - const bool couldReconnect = handleSystemdStatusChanged(); - if (reconnectRequired && couldReconnect) { - m_connection.reconnect(); - } + // apply systemd settings, also enforce reconnect if required and possible + applySystemdSettings(reconnectRequired); #else + // reconnect if required, not checking whether possible if (reconnectRequired) { m_connection.reconnect(); } @@ -500,56 +498,43 @@ void TrayWidget::updateTraffic() #ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD bool TrayWidget::handleSystemdStatusChanged() { - const SyncthingService &service = syncthingService(); - const Settings::Systemd &settings = Settings::values().systemd; - const bool serviceRelevant = service.isSystemdAvailable() && isLocal(QUrl(m_connection.syncthingUrl())); - bool couldConnectNow = true; + return applySystemdSettings(); +} - if (serviceRelevant) { - const bool isRunning = service.isRunning(); - if (settings.showButton) { +bool TrayWidget::applySystemdSettings(bool reconnectRequired) +{ + // update connection + const Settings::Systemd &systemdSettings(Settings::values().systemd); + bool isServiceRelevant, isServiceRunning; + tie(isServiceRelevant, isServiceRunning) = systemdSettings.apply(m_connection, m_selectedConnection, reconnectRequired); + + if (isServiceRelevant) { + // update start/stop button + if (systemdSettings.showButton) { m_ui->startStopPushButton->setVisible(true); - if (isRunning) { + const auto &unitName(syncthingService().unitName()); + if (isServiceRunning) { m_ui->startStopPushButton->setText(tr("Stop")); - m_ui->startStopPushButton->setToolTip(QStringLiteral("systemctl --user stop ") + service.unitName()); + m_ui->startStopPushButton->setToolTip(QStringLiteral("systemctl --user stop ") + unitName); 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 ") + service.unitName()); + m_ui->startStopPushButton->setToolTip(QStringLiteral("systemctl --user start ") + unitName); m_ui->startStopPushButton->setIcon( QIcon::fromTheme(QStringLiteral("system-run"), QIcon(QStringLiteral(":/icons/hicolor/scalable/apps/system-run.svg")))); } } - if (settings.considerForReconnect) { - if (isRunning && m_selectedConnection) { - // auto-reconnect might have been disabled when unit was inactive before, so re-enable it according current connection settings - m_connection.setAutoReconnectInterval(m_selectedConnection->reconnectInterval); - if (!m_connection.isConnected()) { - // FIXME: This will fail if Syncthing has just been started and isn't ready yet - m_connection.connect(); - } - } else { - // disable auto-reconnect if unit isn't running - m_connection.setAutoReconnectInterval(0); - couldConnectNow = false; - } - } } - - if (!settings.showButton || !serviceRelevant) { + if (!systemdSettings.showButton || !isServiceRelevant) { m_ui->startStopPushButton->setVisible(false); } - if ((!settings.considerForReconnect || !serviceRelevant) && m_selectedConnection) { - m_connection.setAutoReconnectInterval(m_selectedConnection->reconnectInterval); - } - - return couldConnectNow; + return isServiceRelevant && isServiceRunning; } void TrayWidget::connectIfServiceRunning() { - if (Settings::values().systemd.considerForReconnect && isLocal(QUrl(m_connection.syncthingUrl())) && syncthingService().isRunning()) { + if (Settings::values().systemd.considerForReconnect && m_connection.isLocal() && syncthingService().isRunning()) { m_connection.connect(); } } diff --git a/tray/gui/traywidget.h b/tray/gui/traywidget.h index 47edc19..1c5230f 100644 --- a/tray/gui/traywidget.h +++ b/tray/gui/traywidget.h @@ -75,6 +75,7 @@ private slots: void updateTraffic(); #ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD bool handleSystemdStatusChanged(); + bool applySystemdSettings(bool reconnectRequired = false); void connectIfServiceRunning(); #endif #ifndef SYNCTHINGWIDGETS_NO_WEBVIEW diff --git a/widgets/misc/internalerror.cpp b/widgets/misc/internalerror.cpp index 2f44879..5a7d320 100644 --- a/widgets/misc/internalerror.cpp +++ b/widgets/misc/internalerror.cpp @@ -3,8 +3,8 @@ #include "../settings/settings.h" #include "../../connector/syncthingconnection.h" +#include "../../connector/syncthingprocess.h" #include "../../connector/syncthingservice.h" -#include "../../connector/utils.h" #include @@ -12,21 +12,51 @@ using namespace Data; namespace QtGui { +/*! + * \brief Returns whether the error is relevant. Only in this case a notification for the error should be shown. + */ bool InternalError::isRelevant(const SyncthingConnection &connection, SyncthingErrorCategory category, int networkError) { + // ignore overall connection errors when auto reconnect tries >= 1 + if (category != SyncthingErrorCategory::OverallConnection && connection.autoReconnectTries() >= 1) { + return false; + } + + // ignore errors when disabled in settings const auto &settings = Settings::values(); + if (!settings.notifyOn.internalErrors) { + return false; + } + + // skip further considerations if connection is remote + if (!connection.isLocal()) { + return true; + } + + // consider process/launcher or systemd unit status + const auto remoteHostClosed(networkError == QNetworkReply::RemoteHostClosedError); + // ignore "remote host closed" error if we've just stopped Syncthing ourselves + const SyncthingProcess &process(syncthingProcess()); + if (settings.launcher.considerForReconnect && remoteHostClosed && process.isManuallyStopped()) { + return false; + } #ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD - const SyncthingService &service = syncthingService(); - const bool serviceRelevant = service.isSystemdAvailable() && isLocal(QUrl(connection.syncthingUrl())); + const SyncthingService &service(syncthingService()); + if (settings.systemd.considerForReconnect && remoteHostClosed && service.isManuallyStopped()) { + return false; + } + + // ignore inavailability after start or standby-wakeup + if (settings.ignoreInavailabilityAfterStart && networkError == QNetworkReply::ConnectionRefusedError) { + if (process.isRunning() && !service.isActiveWithoutSleepFor(process.activeSince(), settings.ignoreInavailabilityAfterStart)) { + return false; + } + if (service.isRunning() && !service.isActiveWithoutSleepFor(settings.ignoreInavailabilityAfterStart)) { + return false; + } + } #endif - return settings.notifyOn.internalErrors && (connection.autoReconnectTries() < 1 || category != SyncthingErrorCategory::OverallConnection) -#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD - && (!settings.systemd.considerForReconnect || !serviceRelevant - || !(networkError == QNetworkReply::RemoteHostClosedError && service.isManuallyStopped())) - && (settings.ignoreInavailabilityAfterStart == 0 - || !(networkError == QNetworkReply::ConnectionRefusedError && service.isRunning() - && !service.isActiveWithoutSleepFor(settings.ignoreInavailabilityAfterStart))) -#endif - ; + + return true; } } // namespace QtGui diff --git a/widgets/settings/settings.cpp b/widgets/settings/settings.cpp index ccf0883..4f0096c 100644 --- a/widgets/settings/settings.cpp +++ b/widgets/settings/settings.cpp @@ -1,6 +1,10 @@ #include "./settings.h" #include "../../connector/syncthingnotifier.h" #include "../../connector/syncthingprocess.h" +#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD +#include "../../connector/syncthingconnection.h" +#include "../../connector/syncthingservice.h" +#endif // use meta-data of syncthingtray application here #include "resources/../../tray/resources/config.h" @@ -295,19 +299,70 @@ void save() /*! * \brief Applies the notification settings on the specified \a notifier. */ -void NotifyOn::apply(SyncthingNotifier ¬ifier) const +void Settings::apply(SyncthingNotifier ¬ifier) const { auto notifications(SyncthingHighLevelNotification::None); - if (disconnect) { + if (notifyOn.disconnect) { notifications |= SyncthingHighLevelNotification::ConnectedDisconnected; } - if (localSyncComplete) { + if (notifyOn.localSyncComplete) { notifications |= SyncthingHighLevelNotification::LocalSyncComplete; } - if (remoteSyncComplete) { + if (notifyOn.remoteSyncComplete) { notifications |= SyncthingHighLevelNotification::RemoteSyncComplete; } notifier.setEnabledNotifications(notifications); + notifier.setIgnoreInavailabilityAfterStart(ignoreInavailabilityAfterStart); } +#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD +/*! + * \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. + * - \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. + */ +std::tuple Systemd::apply( + Data::SyncthingConnection &connection, const SyncthingConnectionSettings *currentConnectionSettings, bool reconnectRequired) const +{ + const SyncthingService &service(syncthingService()); + const auto isRelevant = service.isSystemdAvailable() && connection.isLocal(); + const auto isRunning = service.isRunning(); + + 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 (considerForReconnect && isRelevant) { + constexpr auto minActiveTimeInSeconds(5); + if (reconnectRequired) { + if (service.isActiveWithoutSleepFor(minActiveTimeInSeconds)) { + connection.reconnect(); + } else { + // give the service (which has just started) a few seconds to initialize + connection.reconnectLater(minActiveTimeInSeconds * 1000); + } + } else if (isRunning && !connection.isConnected()) { + if (service.isActiveWithoutSleepFor(minActiveTimeInSeconds)) { + connection.connect(); + } else { + // give the service (which has just started) a few seconds to initialize + connection.connectLater(minActiveTimeInSeconds * 1000); + } + } + } else if (reconnectRequired) { + connection.reconnect(); + } + + return make_tuple(isRelevant, isRunning); +} +#endif + } // namespace Settings diff --git a/widgets/settings/settings.h b/widgets/settings/settings.h index 11ff4d3..2639eb2 100644 --- a/widgets/settings/settings.h +++ b/widgets/settings/settings.h @@ -15,6 +15,7 @@ #include #include +#include #include namespace Dialogs { @@ -24,6 +25,7 @@ class QtSettings; namespace Data { class SyncthingProcess; class SyncthingNotifier; +class SyncthingConnection; } // namespace Data namespace Settings { @@ -39,8 +41,6 @@ struct SYNCTHINGWIDGETS_EXPORT NotifyOn { bool localSyncComplete = false; bool remoteSyncComplete = false; bool syncthingErrors = true; - - void apply(Data::SyncthingNotifier ¬ifier) const; }; struct SYNCTHINGWIDGETS_EXPORT Appearance { @@ -67,6 +67,7 @@ struct SYNCTHINGWIDGETS_EXPORT Launcher { #endif QString syncthingArgs; QHash tools; + bool considerForReconnect = false; QString syncthingCmd() const; QString toolCmd(const QString &tool) const; static Data::SyncthingProcess &toolProcess(const QString &tool); @@ -79,6 +80,11 @@ struct SYNCTHINGWIDGETS_EXPORT Systemd { QString syncthingUnit = QStringLiteral("syncthing.service"); bool showButton = false; bool considerForReconnect = false; + +#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD + std::tuple apply(Data::SyncthingConnection &connection, const Data::SyncthingConnectionSettings *currentConnectionSettings, + bool preventReconnect = false) const; +#endif }; #endif @@ -108,6 +114,8 @@ struct SYNCTHINGWIDGETS_EXPORT Settings { WebView webView; #endif Dialogs::QtSettings qt; + + void apply(Data::SyncthingNotifier ¬ifier) const; }; Settings SYNCTHINGWIDGETS_EXPORT &values();