diff --git a/connector/CMakeLists.txt b/connector/CMakeLists.txt index dfed547..db19c9a 100644 --- a/connector/CMakeLists.txt +++ b/connector/CMakeLists.txt @@ -36,6 +36,8 @@ set(TS_FILES # find c++utilities find_package(c++utilities 4.0.0 REQUIRED) use_cpp_utilities() +set(META_PUBLIC_SHARED_LIB_DEPENDS c++utilities) +set(META_PUBLIC_STATIC_LIB_DEPENDS c++utilities_static) # find qtutilities (only headers and CMake modules used) find_package(qtutilities 5.0.0 REQUIRED) diff --git a/connector/syncthingservice.cpp b/connector/syncthingservice.cpp index f9aed98..9a1a5bb 100644 --- a/connector/syncthingservice.cpp +++ b/connector/syncthingservice.cpp @@ -17,6 +17,7 @@ using namespace std; using namespace std::placeholders; +using namespace ChronoUtilities; namespace Data { @@ -36,6 +37,11 @@ const QDBusArgument &operator>>(const QDBusArgument &argument, ManagerDBusUnitFi return argument; } +constexpr DateTime dateTimeFromSystemdTimeStamp(qulonglong timeStamp) +{ + return DateTime(621355968000000000 + timeStamp * 10); +} + OrgFreedesktopSystemd1ManagerInterface *SyncthingService::s_manager = nullptr; SyncthingService::SyncthingService(QObject *parent) : @@ -140,10 +146,12 @@ void SyncthingService::handleUnitGet(QDBusPendingCallWatcher *watcher) void SyncthingService::handlePropertiesChanged(const QString &interface, const QVariantMap &changedProperties, const QStringList &invalidatedProperties) { if(interface == m_unit->interface()) { + handlePropertyChanged(m_activeSince, QStringLiteral("ActiveEnterTimestamp"), changedProperties, invalidatedProperties); + const bool wasRunningBefore = isRunning(); if(handlePropertyChanged(m_activeState, &SyncthingService::activeStateChanged, QStringLiteral("ActiveState"), changedProperties, invalidatedProperties) | handlePropertyChanged(m_subState, &SyncthingService::subStateChanged, QStringLiteral("SubState"), changedProperties, invalidatedProperties)) { - emit stateChanged(m_activeState, m_subState); + emit stateChanged(m_activeState, m_subState, m_activeSince); } const bool currentlyRunning = isRunning(); if(currentlyRunning) { @@ -196,6 +204,23 @@ bool SyncthingService::handlePropertyChanged(QString &variable, void (SyncthingS return false; } +bool SyncthingService::handlePropertyChanged(DateTime &variable, const QString &propertyName, const QVariantMap &changedProperties, const QStringList &invalidatedProperties) +{ + const QVariant valueVariant(changedProperties[propertyName]); + if(valueVariant.isValid()) { + bool ok; + const qulonglong valueInt = valueVariant.toULongLong(&ok); + if(ok) { + variable = dateTimeFromSystemdTimeStamp(valueInt); + return true; + } + } else if(invalidatedProperties.contains(propertyName) && !variable.isNull()) { + variable = DateTime(); + return true; + } + return false; +} + void SyncthingService::registerErrorHandler(const QDBusPendingCall &call, const char *context) { connect(new QDBusPendingCallWatcher(call, this), &QDBusPendingCallWatcher::finished, bind(&SyncthingService::handleError, this, context, _1)); @@ -215,6 +240,7 @@ void SyncthingService::setUnit(const QDBusObjectPath &objectPath) // init unit m_unit = new OrgFreedesktopSystemd1UnitInterface(s_manager->service(), path, s_manager->connection()); + m_activeSince = dateTimeFromSystemdTimeStamp(m_unit->activeEnterTimestamp()); setProperties(m_unit->activeState(), m_unit->subState(), m_unit->unitFileState(), m_unit->description()); // init properties @@ -235,7 +261,7 @@ void SyncthingService::setProperties(const QString &activeState, const QString & anyStateChanged = true; } if(anyStateChanged) { - emit stateChanged(m_activeState, m_subState); + emit stateChanged(m_activeState, m_subState, m_activeSince); } if(running != isRunning()) { emit runningChanged(isRunning()); diff --git a/connector/syncthingservice.h b/connector/syncthingservice.h index 4ecd9a1..97ec39e 100644 --- a/connector/syncthingservice.h +++ b/connector/syncthingservice.h @@ -1,6 +1,8 @@ #ifndef DATA_SYNCTHINGSERVICE_H #define DATA_SYNCTHINGSERVICE_H +#include + #include #include @@ -36,6 +38,7 @@ class SyncthingService : public QObject Q_PROPERTY(bool unitAvailable READ isUnitAvailable) Q_PROPERTY(QString activeState READ activeState NOTIFY activeStateChanged) Q_PROPERTY(QString subState READ subState NOTIFY subStateChanged) + Q_PROPERTY(ChronoUtilities::DateTime activeSince READ activeSince NOTIFY activeStateChanged) Q_PROPERTY(QString unitFileState READ unitFileState NOTIFY unitFileStateChanged) Q_PROPERTY(QString description READ description NOTIFY descriptionChanged) Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) @@ -50,6 +53,8 @@ public: bool isUnitAvailable() const; const QString &activeState() const; const QString &subState() const; + ChronoUtilities::DateTime activeSince() const; + bool isActiveFor(unsigned int atleastSeconds) const; const QString &unitFileState() const; const QString &description() const; bool isRunning() const; @@ -68,7 +73,7 @@ public Q_SLOTS: Q_SIGNALS: void systemdAvailableChanged(bool available); - void stateChanged(const QString &activeState, const QString &subState); + void stateChanged(const QString &activeState, const QString &subState, ChronoUtilities::DateTime activeSince); void activeStateChanged(const QString &activeState); void subStateChanged(const QString &subState); void unitFileStateChanged(const QString &unitFileState); @@ -89,6 +94,7 @@ private Q_SLOTS: private: bool handlePropertyChanged(QString &variable, void(SyncthingService::*signal)(const QString &), const QString &propertyName, const QVariantMap &changedProperties, const QStringList &invalidatedProperties); + bool handlePropertyChanged(ChronoUtilities::DateTime &variable, const QString &propertyName, const QVariantMap &changedProperties, const QStringList &invalidatedProperties); void registerErrorHandler(const QDBusPendingCall &call, const char *context); static OrgFreedesktopSystemd1ManagerInterface *s_manager; @@ -102,6 +108,7 @@ private: QString m_subState; QString m_unitFileState; bool m_manuallyStopped; + ChronoUtilities::DateTime m_activeSince; }; inline const QString &SyncthingService::unitName() const @@ -159,6 +166,16 @@ inline bool SyncthingService::isManuallyStopped() const return m_manuallyStopped; } +inline ChronoUtilities::DateTime SyncthingService::activeSince() const +{ + return m_activeSince; +} + +inline bool SyncthingService::isActiveFor(unsigned int atleastSeconds) const +{ + return !m_activeSince.isNull() && (ChronoUtilities::DateTime::now() - m_activeSince).totalSeconds() > atleastSeconds; +} + inline void SyncthingService::enable() { setEnabled(true); diff --git a/connector/translations/syncthingconnector_de_DE.ts b/connector/translations/syncthingconnector_de_DE.ts index bcee533..fd9c49d 100644 --- a/connector/translations/syncthingconnector_de_DE.ts +++ b/connector/translations/syncthingconnector_de_DE.ts @@ -4,153 +4,158 @@ Data::SyncthingConnection - + disconnected Verbindung getrennt - + reconnecting Verbindung wird hergestellt - + connected verbunden - + connected, paused verbunden, pausiert - + connected, synchronizing verbunden, am Synchronisieren - + unknown Verbindungsstatus unbekannt - - + + Connection configuration is insufficient. Verbindungskonfiguration is ungenügend - + Unable to request QR-Code: Fehler beim Abfragen des QR-Codes: - + Unable to parse Syncthing log: Fehler beim Auslesen des Syncthing-Logs: - + Unable to request Syncthing log: Fehler beim Abfragen des Syncthing-Logs: - + Unable to locate certificate used by Syncthing. Das SSL-Zertifikat von Syncthing kann nicht gefunden werden. - + Unable to load certificate used by Syncthing. Das SSL-Zertifikat von Syncthing kann nicht ausgelesen werden. - + Unable to parse Syncthing config: Fehler beim Auslesen der Syncthing-Konfiguration: - + Unable to request Syncthing config: Fehler beim Abfragen der Syncthing-Konfiguration: - + Unable to parse Syncthing status: Fehler beim Auslesen des Syncthing-Status: - + Unable to request Syncthing status: Fehler beim Abfragen des Syncthing-Status: - + Unable to parse connections: Fehler beim Auslesen der Verbindungen: - + Unable to request connections: Fehler beim Abfragen der Verbindungen: - + Unable to parse directory statistics: Fehler beim Auslesen der Verzeichnisstatistiken: - + Unable to request directory statistics: Fehler beim Abfragen der Verzeichnisstatistiken: - + Unable to parse device statistics: Fehler beim Auslesen der Gerätestatistiken: - + Unable to request device statistics: Fehler beim Abfragen der Gerätestatistiken: - + Unable to parse errors: Fehler beim Auslesen der Syncthing-Fehlermeldungen: - + Unable to request errors: Fehler beim Abfragen der Syncthing-Fehlermeldungen: - + + Unable to request clearing errors: + + + + Unable to parse Syncthing events: Fehler beim Auslesen der Syncthing-Ereignisse: - + Unable to request Syncthing events: Fehler beim Abfragen der Syncthing-Ereignisse: - + Unable to request rescan: Fehler beim Anfordern eines Verzeichnis-Rescans: - + Unable to request pause/resume: Fehler beim Anfordern Gerät zu Pausieren/Fortzusetzen: - + Unable to request restart: Fehler beim Anfordern eines Neustarts: - + Unable to request shutdown: Fehler beim Anfordern Syncthing zu beenden: @@ -158,22 +163,22 @@ Data::SyncthingService - + start unit - + stop unit - + enable unit - + disable unit diff --git a/connector/translations/syncthingconnector_en_US.ts b/connector/translations/syncthingconnector_en_US.ts index 0bc5d41..9724ba1 100644 --- a/connector/translations/syncthingconnector_en_US.ts +++ b/connector/translations/syncthingconnector_en_US.ts @@ -4,153 +4,158 @@ Data::SyncthingConnection - + disconnected - + reconnecting - + connected - + connected, paused - + connected, synchronizing - + unknown - - + + Connection configuration is insufficient. - + Unable to request QR-Code: - + Unable to parse Syncthing log: - + Unable to request Syncthing log: - + Unable to locate certificate used by Syncthing. - + Unable to load certificate used by Syncthing. - + Unable to parse Syncthing config: - + Unable to request Syncthing config: - + Unable to parse Syncthing status: - + Unable to request Syncthing status: - + Unable to parse connections: - + Unable to request connections: - + Unable to parse directory statistics: - + Unable to request directory statistics: - + Unable to parse device statistics: - + Unable to request device statistics: - + Unable to parse errors: - + Unable to request errors: - + + Unable to request clearing errors: + + + + Unable to parse Syncthing events: - + Unable to request Syncthing events: - + Unable to request rescan: - + Unable to request pause/resume: - + Unable to request restart: - + Unable to request shutdown: @@ -158,22 +163,22 @@ Data::SyncthingService - + start unit - + stop unit - + enable unit - + disable unit diff --git a/tray/gui/settingsdialog.cpp b/tray/gui/settingsdialog.cpp index fbbac00..1c3e7ca 100644 --- a/tray/gui/settingsdialog.cpp +++ b/tray/gui/settingsdialog.cpp @@ -27,6 +27,7 @@ # include #endif #ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD +# include # include #endif @@ -42,6 +43,7 @@ #include #include #include +#include #include @@ -590,7 +592,7 @@ QWidget *SystemdOptionPage::setupWidget() QObject::connect(ui()->enablePushButton, &QPushButton::clicked, &m_service, &SyncthingService::enable); QObject::connect(ui()->disablePushButton, &QPushButton::clicked, &m_service, &SyncthingService::disable); QObject::connect(&m_service, &SyncthingService::descriptionChanged, bind(&SystemdOptionPage::handleDescriptionChanged, this, _1)); - QObject::connect(&m_service, &SyncthingService::stateChanged, bind(&SystemdOptionPage::handleStatusChanged, this, _1, _2)); + QObject::connect(&m_service, &SyncthingService::stateChanged, bind(&SystemdOptionPage::handleStatusChanged, this, _1, _2, _3)); QObject::connect(&m_service, &SyncthingService::unitFileStateChanged, bind(&SystemdOptionPage::handleEnabledChanged, this, _1)); return widget; } @@ -614,7 +616,7 @@ void SystemdOptionPage::reset() ui()->showButtonCheckBox->setChecked(settings.showButton); ui()->considerForReconnectCheckBox->setChecked(settings.considerForReconnect); handleDescriptionChanged(m_service.description()); - handleStatusChanged(m_service.activeState(), m_service.subState()); + handleStatusChanged(m_service.activeState(), m_service.subState(), m_service.activeSince()); handleEnabledChanged(m_service.unitFileState()); } } @@ -629,7 +631,7 @@ void setIndicatorColor(QWidget *indicator, const QColor &color) indicator->setStyleSheet(QStringLiteral("border-radius:8px;background-color:") + color.name()); } -void SystemdOptionPage::handleStatusChanged(const QString &activeState, const QString &subState) +void SystemdOptionPage::handleStatusChanged(const QString &activeState, const QString &subState, ChronoUtilities::DateTime activeSince) { QStringList status; if(!activeState.isEmpty()) { @@ -638,11 +640,18 @@ void SystemdOptionPage::handleStatusChanged(const QString &activeState, const QS if(!subState.isEmpty()) { status << subState; } + const bool isRunning = m_service.isRunning(); + QString timeStamp; + if(isRunning && !activeSince.isNull()) { + timeStamp = QLatin1Char('\n') + % QCoreApplication::translate("QtGui::SystemdOptionPage", "since ") + % QString::fromUtf8(activeSince.toString(ChronoUtilities::DateTimeOutputFormat::DateAndTime).data()); + } ui()->statusValueLabel->setText(status.isEmpty() ? QCoreApplication::translate("QtGui::SystemdOptionPage", "unknown") - : status.join(QStringLiteral(" - "))); + : status.join(QStringLiteral(" - ")) + timeStamp); setIndicatorColor(ui()->statusIndicator, status.isEmpty() ? Colors::gray(values().appearance.brightTextColors) : (isRunning diff --git a/tray/gui/settingsdialog.h b/tray/gui/settingsdialog.h index f5b8118..029cc2a 100644 --- a/tray/gui/settingsdialog.h +++ b/tray/gui/settingsdialog.h @@ -10,6 +10,10 @@ #include #include +namespace ChronoUtilities { +class DateTime; +} + namespace Data { class SyncthingConnection; class SyncthingService; @@ -58,7 +62,7 @@ BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE(SystemdOptionPage) private: DECLARE_SETUP_WIDGETS void handleDescriptionChanged(const QString &description); - void handleStatusChanged(const QString &activeState, const QString &subState); + void handleStatusChanged(const QString &activeState, const QString &subState, ChronoUtilities::DateTime activeSince); void handleEnabledChanged(const QString &unitFileState); Data::SyncthingService &m_service; END_DECLARE_OPTION_PAGE diff --git a/tray/translations/syncthingtray_de_DE.ts b/tray/translations/syncthingtray_de_DE.ts index 041b312..700aaae 100644 --- a/tray/translations/syncthingtray_de_DE.ts +++ b/tray/translations/syncthingtray_de_DE.ts @@ -127,22 +127,22 @@ Tray-Icon beim Starten der Desktopumgebung automatisch starten - + This is achieved by adding a *.desktop file under <i>~/.config/autostart</i> so the setting only affects the current user. Durch das Hinzufügen einer *.desktop-Datei unter <i>~/.config/autostart</i> realisiert - betrifft also nur den aktuellen Benutzer. - + This is achieved by adding a registry key under <i>HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run</i> so the setting only affects the current user. Note that the startup entry is invalidated when moving <i>syncthingtray.exe</i>. Durch das Hinzufügen eines Registry-Schlüssels unter <i>HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run</i> realisiert - betrifft also nur den aktuellen Benutzer. - + This feature has not been implemented for your platform (yet). Diese Funktion wurde für die aktuelle Plattform nicht nicht implementiert. - + unable to modify startup entry Fehler beim aktualisieren des Auto-Start-Eintrags @@ -263,27 +263,27 @@ nicht neu verbinden - + Auto-detected for local instance Kann für lokale Instanz automatisch ermittelt werden - + Select Syncthing config file Wähle die Syncthing-Konfigurationsdatei - + Unable to parse the Syncthing config file. Fehler beim Auslesen der Syncthing-Konfigurationsdatei. - + Unable to load specified certificate "%1". Fehler beim Auslesen des angegebenen Zertifikats: %1 - + Instance %1 Instanz %1 @@ -390,13 +390,13 @@ Log folgen - + Syncthing exited with exit code %1 Syncthing wurde mit dem Statuscode %1 beendet - + Syncthing crashed with exit code %1 Syncthing ist mit dem Statuscode %1 abgestürzt @@ -450,7 +450,7 @@ Methode die von Qt verwendet wird (kann vom QPA-Plugin überschrieben werden) - + Configured to use D-Bus notifications but D-Bus notification daemon seems unavailabe. Benachrichtigungen via D-Bus wurden eingestellt, aber es scheint kein Daemon zu laufen der den Dienst bereitstellt. @@ -458,22 +458,22 @@ QtGui::SettingsDialog - + Tray - + Web view Weboberfläche - + Startup Starten - + Settings Einstellungen @@ -514,8 +514,8 @@ - - + + unknown unbekannt @@ -549,10 +549,15 @@ Stoppen - + specified unit is either inactive or doesn't exist angegebene Unit ist entweder nicht geladen oder existiert nicht + + + since + seit + QtGui::TrayIcon @@ -904,7 +909,7 @@ For <i>all</i> notifications, checkout the log QtGui::WebViewOptionPage - + General Allgemein @@ -934,7 +939,7 @@ For <i>all</i> notifications, checkout the log Lasse Weboberfläche im Hintgergrund weiter offen, wenn Fenster nicht offen - + Syncthing Tray has not been built with vieb view support utilizing either Qt WebKit or Qt WebEngine. The Web UI will be opened in the default web browser instead. Syncthing Tray wurde nicht mit Unterstützung für die eingebaute Anzeige der Weboberfläche unter Verwendung von Qt WebKit oder Qt WebEngine gebaut. diff --git a/tray/translations/syncthingtray_en_US.ts b/tray/translations/syncthingtray_en_US.ts index 49fc32b..693562f 100644 --- a/tray/translations/syncthingtray_en_US.ts +++ b/tray/translations/syncthingtray_en_US.ts @@ -127,22 +127,22 @@ - + This is achieved by adding a *.desktop file under <i>~/.config/autostart</i> so the setting only affects the current user. - + This is achieved by adding a registry key under <i>HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run</i> so the setting only affects the current user. Note that the startup entry is invalidated when moving <i>syncthingtray.exe</i>. - + This feature has not been implemented for your platform (yet). - + unable to modify startup entry @@ -263,27 +263,27 @@ - + Auto-detected for local instance - + Select Syncthing config file - + Unable to parse the Syncthing config file. - + Unable to load specified certificate "%1". - + Instance %1 @@ -390,13 +390,13 @@ - + Syncthing exited with exit code %1 - + Syncthing crashed with exit code %1 @@ -450,7 +450,7 @@ - + Configured to use D-Bus notifications but D-Bus notification daemon seems unavailabe. @@ -458,22 +458,22 @@ QtGui::SettingsDialog - + Tray - + Web view - + Startup - + Settings @@ -514,8 +514,8 @@ - - + + unknown @@ -547,10 +547,15 @@ - + specified unit is either inactive or doesn't exist + + + since + + QtGui::TrayIcon @@ -900,7 +905,7 @@ For <i>all</i> notifications, checkout the log QtGui::WebViewOptionPage - + General @@ -930,7 +935,7 @@ For <i>all</i> notifications, checkout the log - + Syncthing Tray has not been built with vieb view support utilizing either Qt WebKit or Qt WebEngine. The Web UI will be opened in the default web browser instead.