Refactor notifications

* Use SyncthingNotifier class
* Show 'Sync complete' on FolderCompletion event
* Add extra structure for stats
This commit is contained in:
Martchus 2018-01-27 23:27:50 +01:00
parent 2f26543acf
commit 79fe97d952
18 changed files with 202 additions and 213 deletions

View File

@ -479,8 +479,8 @@ void Application::printDir(const RelevantDir &relevantDir) const
printProperty("Label", dir->label);
printProperty("Path", dir->path);
printProperty("Status", dir->statusString());
printProperty("Global", directoryStatusString(dir->globalFiles, dir->globalDirs, dir->globalBytes), nullptr, 6);
printProperty("Local", directoryStatusString(dir->localFiles, dir->localDirs, dir->localBytes), nullptr, 6);
printProperty("Global", directoryStatusString(dir->globalStats), nullptr, 6);
printProperty("Local", directoryStatusString(dir->localStats), nullptr, 6);
printProperty("Last scan time", dir->lastScanTime);
printProperty("Last file time", dir->lastFileTime);
printProperty("Last file name", dir->lastFileName);

View File

@ -640,7 +640,7 @@ QString SyncthingConnection::deviceNameOrId(const QString &deviceId) const
* \brief Returns the number of devices Syncthing is currently connected to.
* \remarks Computed by looping devInfo().
*/
size_t SyncthingConnection::connectedDevices() const
std::size_t SyncthingConnection::connectedDevices() const
{
size_t connectedDevs = 0;
for (const SyncthingDev &dev : devInfo()) {
@ -1028,8 +1028,10 @@ void SyncthingConnection::readConfig()
*/
void SyncthingConnection::readDirs(const QJsonArray &dirs)
{
// store the new dirs in a temporary list which is assigned to m_dirs later
std::vector<SyncthingDir> newDirs;
newDirs.reserve(static_cast<size_t>(dirs.size()));
int dummy;
for (const QJsonValue &dirVal : dirs) {
const QJsonObject dirObj(dirVal.toObject());
@ -1058,8 +1060,8 @@ void SyncthingConnection::readDirs(const QJsonArray &dirs)
dirItem->minDiskFreePercentage = dirObj.value(QStringLiteral("minDiskFreePct")).toInt(-1);
dirItem->paused = dirObj.value(QStringLiteral("paused")).toBool(dirItem->paused);
}
m_dirs.swap(newDirs);
m_syncedDirs.reserve(m_dirs.size());
emit this->newDirs(m_dirs);
}
@ -1068,8 +1070,10 @@ void SyncthingConnection::readDirs(const QJsonArray &dirs)
*/
void SyncthingConnection::readDevs(const QJsonArray &devs)
{
// store the new devs in a temporary list which is assigned to m_devs later
vector<SyncthingDev> newDevs;
newDevs.reserve(static_cast<size_t>(devs.size()));
for (const QJsonValue &devVal : devs) {
const QJsonObject devObj(devVal.toObject());
SyncthingDev *const devItem = addDevInfo(newDevs, devObj.value(QStringLiteral("deviceID")).toString());
@ -1088,6 +1092,7 @@ void SyncthingConnection::readDevs(const QJsonArray &devs)
devItem->status = devItem->id == m_myId ? SyncthingDevStatus::OwnDevice : SyncthingDevStatus::Unknown;
devItem->paused = devObj.value(QStringLiteral("paused")).toBool(devItem->paused);
}
m_devs.swap(newDevs);
emit this->newDevices(m_devs);
}
@ -1640,11 +1645,14 @@ void SyncthingConnection::readDirEvent(DateTime eventTime, const QString &eventT
readDirSummary(eventTime, eventData.value(QStringLiteral("summary")).toObject(), *dirInfo, index);
} else if (eventType == QLatin1String("FolderCompletion")) {
const int percentage = jsonValueToInt<int>(eventData.value(QStringLiteral("completion")));
dirInfo->globalBytes = jsonValueToInt(eventData.value(QStringLiteral("globalBytes")), dirInfo->globalBytes);
dirInfo->neededBytes = jsonValueToInt(eventData.value(QStringLiteral("neededBytes")), dirInfo->neededBytes);
if (percentage > 0 && percentage < 100) {
dirInfo->globalStats.bytes = jsonValueToInt(eventData.value(QStringLiteral("globalBytes")), dirInfo->globalStats.bytes);
dirInfo->neededStats.bytes = jsonValueToInt(eventData.value(QStringLiteral("needBytes")), dirInfo->neededStats.bytes);
if (percentage >= 0 && percentage <= 100) {
dirInfo->completionPercentage = percentage;
emit dirStatusChanged(*dirInfo, index);
if (percentage == 100) {
emit dirCompleted(*dirInfo, index);
}
}
} else if (eventType == QLatin1String("FolderScanProgress")) {
const double current = eventData.value(QStringLiteral("current")).toDouble(0);
@ -1737,27 +1745,34 @@ void SyncthingConnection::readItemFinished(DateTime eventTime, const QJsonObject
if (dir.isEmpty()) {
return;
}
int index;
if (SyncthingDir *dirInfo = findDirInfo(dir, index)) {
const QString error(eventData.value(QStringLiteral("error")).toString()), item(eventData.value(QStringLiteral("item")).toString());
if (error.isEmpty()) {
if (dirInfo->lastFileTime.isNull() || eventTime < dirInfo->lastFileTime) {
dirInfo->lastFileTime = eventTime, dirInfo->lastFileName = item,
dirInfo->lastFileDeleted = (eventData.value(QStringLiteral("action")) != QLatin1String("delete"));
if (eventTime > m_lastFileTime) {
m_lastFileTime = dirInfo->lastFileTime, m_lastFileName = dirInfo->lastFileName, m_lastFileDeleted = dirInfo->lastFileDeleted;
}
emit dirStatusChanged(*dirInfo, index);
}
} else if (dirInfo->status == SyncthingDirStatus::OutOfSync) {
auto *const dirInfo = findDirInfo(dir, index);
if (!dirInfo) {
return;
}
// handle unsuccessfull operation
const auto error(eventData.value(QStringLiteral("error")).toString()), item(eventData.value(QStringLiteral("item")).toString());
if (!error.isEmpty()) {
if (dirInfo->status == SyncthingDirStatus::OutOfSync) {
// FIXME: find better way to check whether the event is still relevant
dirInfo->itemErrors.emplace_back(error, item);
dirInfo->status = SyncthingDirStatus::OutOfSync;
// emitNotification will trigger status update, so no need to call setStatus(status())
emit dirStatusChanged(*dirInfo, index);
emitNotification(eventTime, error);
}
return;
}
// update last file
if (dirInfo->lastFileTime.isNull() || eventTime < dirInfo->lastFileTime) {
dirInfo->lastFileTime = eventTime;
dirInfo->lastFileName = item;
dirInfo->lastFileDeleted = (eventData.value(QStringLiteral("action")) != QLatin1String("delete"));
if (eventTime > m_lastFileTime) {
m_lastFileTime = dirInfo->lastFileTime, m_lastFileName = dirInfo->lastFileName, m_lastFileDeleted = dirInfo->lastFileDeleted;
}
emit dirStatusChanged(*dirInfo, index);
}
}
@ -1935,16 +1950,25 @@ bool SyncthingConnection::readDirSummary(DateTime eventTime, const QJsonObject &
}
// update statistics
dir.globalBytes = jsonValueToInt(summary.value(QStringLiteral("globalBytes")));
dir.globalDeleted = jsonValueToInt(summary.value(QStringLiteral("globalDeleted")));
dir.globalFiles = jsonValueToInt(summary.value(QStringLiteral("globalFiles")));
dir.globalDirs = jsonValueToInt(summary.value(QStringLiteral("globalDirectories")));
dir.localBytes = jsonValueToInt(summary.value(QStringLiteral("localBytes")));
dir.localDeleted = jsonValueToInt(summary.value(QStringLiteral("localDeleted")));
dir.localFiles = jsonValueToInt(summary.value(QStringLiteral("localFiles")));
dir.localDirs = jsonValueToInt(summary.value(QStringLiteral("localDirectories")));
dir.neededBytes = jsonValueToInt(summary.value(QStringLiteral("needByted")));
dir.neededFiles = jsonValueToInt(summary.value(QStringLiteral("needFiles")));
auto &globalStats(dir.globalStats);
globalStats.bytes = jsonValueToInt(summary.value(QStringLiteral("globalBytes")));
globalStats.deletes = jsonValueToInt(summary.value(QStringLiteral("globalDeleted")));
globalStats.files = jsonValueToInt(summary.value(QStringLiteral("globalFiles")));
globalStats.dirs = jsonValueToInt(summary.value(QStringLiteral("globalDirectories")));
globalStats.symlinks = jsonValueToInt(summary.value(QStringLiteral("globalSymlinks")));
auto &localStats(dir.localStats);
localStats.bytes = jsonValueToInt(summary.value(QStringLiteral("localBytes")));
localStats.deletes = jsonValueToInt(summary.value(QStringLiteral("localDeleted")));
localStats.files = jsonValueToInt(summary.value(QStringLiteral("localFiles")));
localStats.dirs = jsonValueToInt(summary.value(QStringLiteral("localDirectories")));
localStats.symlinks = jsonValueToInt(summary.value(QStringLiteral("localSymlinks")));
auto &neededStats(dir.neededStats);
neededStats.bytes = jsonValueToInt(summary.value(QStringLiteral("needBytes")));
neededStats.deletes = jsonValueToInt(summary.value(QStringLiteral("needDeletes")));
neededStats.files = jsonValueToInt(summary.value(QStringLiteral("needFiles")));
neededStats.dirs = jsonValueToInt(summary.value(QStringLiteral("needDirectories")));
neededStats.symlinks = jsonValueToInt(summary.value(QStringLiteral("needSymlinks")));
dir.ignorePatterns = summary.value(QStringLiteral("ignorePatterns")).toBool();
dir.lastStatisticsUpdate = eventTime;
@ -2027,7 +2051,6 @@ void SyncthingConnection::setStatus(SyncthingStatus status)
m_devStatsPollTimer.stop();
m_trafficPollTimer.stop();
m_errorsPollTimer.stop();
m_syncedDirs.clear();
break;
default:
// reset reconnect tries
@ -2036,16 +2059,18 @@ void SyncthingConnection::setStatus(SyncthingStatus status)
// check whether at least one directory is scanning or synchronizing
bool scanning = false;
bool synchronizing = false;
for (SyncthingDir &dir : m_dirs) {
if (dir.status == SyncthingDirStatus::Synchronizing) {
if (find(m_syncedDirs.cbegin(), m_syncedDirs.cend(), &dir) == m_syncedDirs.cend()) {
m_syncedDirs.push_back(&dir);
}
for (const SyncthingDir &dir : m_dirs) {
switch (dir.status) {
case SyncthingDirStatus::Synchronizing:
synchronizing = true;
} else if (dir.status == SyncthingDirStatus::Scanning) {
break;
case SyncthingDirStatus::Scanning:
scanning = true;
break;
default:;
}
}
if (synchronizing) {
status = SyncthingStatus::Synchronizing;
} else if (scanning) {
@ -2061,16 +2086,10 @@ void SyncthingConnection::setStatus(SyncthingStatus status)
}
if (paused) {
status = SyncthingStatus::Paused;
// don't consider synchronization finished in this this case
m_syncedDirs.clear();
} else {
status = SyncthingStatus::Idle;
}
}
if (status != SyncthingStatus::Synchronizing) {
m_completedDirs.clear();
m_completedDirs.swap(m_syncedDirs);
}
}
if (m_status != status) {
emit statusChanged(m_status = status);

View File

@ -78,7 +78,7 @@ class LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingConnection : public QObject {
Q_PROPERTY(int totalOutgoingTraffic READ totalOutgoingTraffic NOTIFY trafficChanged)
Q_PROPERTY(double totalIncomingRate READ totalIncomingRate NOTIFY trafficChanged)
Q_PROPERTY(double totalOutgoingRate READ totalOutgoingRate NOTIFY trafficChanged)
Q_PROPERTY(bool connectedDevices READ connectedDevices)
Q_PROPERTY(std::size_t connectedDevices READ connectedDevices)
public:
explicit SyncthingConnection(
@ -125,11 +125,10 @@ public:
SyncthingDir *findDirInfoByPath(const QString &path, QString &relativePath, int &row);
SyncthingDev *findDevInfo(const QString &devId, int &row);
SyncthingDev *findDevInfoByName(const QString &devName, int &row);
const std::vector<SyncthingDir *> &completedDirs() const;
QStringList directoryIds() const;
QStringList deviceIds() const;
QString deviceNameOrId(const QString &deviceId) const;
size_t connectedDevices() const;
std::size_t connectedDevices() const;
public Q_SLOTS:
bool loadSelfSignedCertificate();
@ -161,6 +160,7 @@ Q_SIGNALS:
void dirStatusChanged(const SyncthingDir &dir, int index);
void devStatusChanged(const SyncthingDev &dev, int index);
void downloadProgressChanged();
void dirCompleted(const SyncthingDir &dir, int index, const SyncthingDev *remoteDev = nullptr);
void newNotification(ChronoUtilities::DateTime when, const QString &message);
void error(const QString &errorMessage, SyncthingErrorCategory category, int networkError, const QNetworkRequest &request = QNetworkRequest(),
const QByteArray &response = QByteArray());
@ -267,8 +267,6 @@ private:
bool m_hasConfig;
bool m_hasStatus;
std::vector<SyncthingDir> m_dirs;
std::vector<SyncthingDir *> m_syncedDirs;
std::vector<SyncthingDir *> m_completedDirs;
std::vector<SyncthingDev> m_devs;
ChronoUtilities::DateTime m_lastConnectionsUpdate;
ChronoUtilities::DateTime m_lastFileTime;
@ -555,14 +553,6 @@ inline const QList<QSslError> &SyncthingConnection::expectedSslErrors()
{
return m_expectedSslErrors;
}
/*!
* \brief Returns the directories which have been synchronized during the last synchronizing status().
*/
inline const std::vector<SyncthingDir *> &SyncthingConnection::completedDirs() const
{
return m_completedDirs;
}
}
Q_DECLARE_METATYPE(Data::SyncthingLogEntry)

View File

@ -60,6 +60,21 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingCompletion {
quint64 neededDeletes = 0;
};
struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingStatistics {
quint64 bytes = 0;
quint64 deletes = 0;
quint64 dirs = 0;
quint64 files = 0;
quint64 symlinks = 0;
constexpr bool isNull() const;
};
constexpr bool SyncthingStatistics::isNull() const
{
return bytes == 0 && deletes == 0 && dirs == 0 && files == 0 && symlinks == 0;
}
struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingDir {
SyncthingDir(const QString &id = QString(), const QString &label = QString(), const QString &path = QString());
bool assignStatus(const QString &statusStr, ChronoUtilities::DateTime time);
@ -67,6 +82,7 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingDir {
const QString &displayName() const;
QString statusString() const;
QStringRef pathWithoutTrailingSlash() const;
bool isLocallyUpToDate() const;
bool areRemotesUpToDate() const;
QString id;
@ -89,9 +105,7 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingDir {
QString globalError;
std::vector<SyncthingItemError> itemErrors;
std::vector<SyncthingItemError> previousItemErrors;
quint64 globalBytes = 0, globalDeleted = 0, globalFiles = 0, globalDirs = 0;
quint64 localBytes = 0, localDeleted = 0, localFiles = 0, localDirs = 0;
quint64 neededBytes = 0, neededFiles = 0, neededDirs = 0;
SyncthingStatistics globalStats, localStats, neededStats;
ChronoUtilities::DateTime lastStatisticsUpdate;
ChronoUtilities::DateTime lastScanTime;
ChronoUtilities::DateTime lastFileTime;
@ -121,6 +135,11 @@ inline const QString &SyncthingDir::displayName() const
return label.isEmpty() ? id : label;
}
inline bool SyncthingDir::isLocallyUpToDate() const
{
return neededStats.isNull();
}
inline bool SyncthingDir::assignStatus(SyncthingDirStatus newStatus, ChronoUtilities::DateTime time)
{
return checkWhetherStatusUpdateRelevant(time) && finalizeStatusUpdate(newStatus);

View File

@ -1,5 +1,6 @@
#include "./syncthingnotifier.h"
#include "./syncthingconnection.h"
#include "./utils.h"
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
#include "./syncthingservice.h"
@ -33,6 +34,8 @@ SyncthingNotifier::SyncthingNotifier(const SyncthingConnection &connection, QObj
, m_previousStatus(SyncthingStatus::Disconnected)
, m_initialized(false)
{
connect(&connection, &SyncthingConnection::statusChanged, this, &SyncthingNotifier::handleStatusChangedEvent);
connect(&connection, &SyncthingConnection::dirCompleted, this, &SyncthingNotifier::emitSyncComplete);
}
void SyncthingNotifier::handleStatusChangedEvent(SyncthingStatus newStatus)
@ -45,7 +48,6 @@ void SyncthingNotifier::handleStatusChangedEvent(SyncthingStatus newStatus)
// emit signals
emit statusChanged(m_previousStatus, newStatus);
emitConnectedAndDisconnected(newStatus);
emitSyncComplete(newStatus);
// update status variables
m_initialized = true;
@ -85,24 +87,18 @@ void SyncthingNotifier::emitConnectedAndDisconnected(SyncthingStatus newStatus)
/*!
* \brief Emits the syncComplete() signal.
*/
void SyncthingNotifier::emitSyncComplete(SyncthingStatus newStatus)
void SyncthingNotifier::emitSyncComplete(const SyncthingDir &dir, int index, const SyncthingDev *remoteDev)
{
if (!(m_enabledNotifications & SyncthingHighLevelNotification::SyncComplete)) {
VAR_UNUSED(index)
VAR_UNUSED(remoteDev)
if ((m_enabledNotifications & SyncthingHighLevelNotification::SyncComplete) == 0 || !m_initialized) {
return;
}
switch (newStatus) {
case SyncthingStatus::Disconnected:
case SyncthingStatus::Reconnecting:
case SyncthingStatus::Synchronizing:
break;
default:
if (m_previousStatus == SyncthingStatus::Synchronizing) {
const auto &completedDirs = m_connection.completedDirs();
if (!completedDirs.empty()) {
emit syncComplete(completedDirs);
}
}
const auto message(syncCompleteString(std::vector<const SyncthingDir *>{ &dir }));
if (!message.isEmpty()) {
emit syncComplete(message);
}
}

View File

@ -11,6 +11,7 @@ enum class SyncthingStatus;
class SyncthingConnection;
class SyncthingService;
struct SyncthingDir;
struct SyncthingDev;
/*!
* \brief The SyncthingHighLevelNotification enum specifies the high-level notifications provided by the SyncthingNotifier class.
@ -28,6 +29,11 @@ constexpr SyncthingHighLevelNotification operator|(SyncthingHighLevelNotificatio
return static_cast<SyncthingHighLevelNotification>(static_cast<unsigned char>(lhs) | static_cast<unsigned char>(rhs));
}
constexpr SyncthingHighLevelNotification &operator|=(SyncthingHighLevelNotification &lhs, SyncthingHighLevelNotification rhs)
{
return lhs = static_cast<SyncthingHighLevelNotification>(static_cast<unsigned char>(lhs) | static_cast<unsigned char>(rhs));
}
constexpr bool operator&(SyncthingHighLevelNotification lhs, SyncthingHighLevelNotification rhs)
{
return static_cast<bool>(static_cast<unsigned char>(lhs) & static_cast<unsigned char>(rhs));
@ -47,21 +53,21 @@ public Q_SLOTS:
void setEnabledNotifications(SyncthingHighLevelNotification enabledNotifications);
Q_SIGNALS:
///! \brief Emitted when the connection status changes. Also provided the previous status.
///! \brief Emitted when the connection status changes. Also provides the previous status.
void statusChanged(SyncthingStatus previousStatus, SyncthingStatus newStatus);
///! \brief Emitted when the connection to Syncthing has been established.
void connected();
///! \brief Emitted when the connection to Syncthing has been interrupted.
void disconnected();
///! \brief Emitted when the specified \a dirs have been completed synchronization.
void syncComplete(const std::vector<SyncthingDir *> &dirs);
void syncComplete(const QString &message);
private Q_SLOTS:
void handleStatusChangedEvent(SyncthingStatus newStatus);
private:
void emitConnectedAndDisconnected(SyncthingStatus newStatus);
void emitSyncComplete(SyncthingStatus newStatus);
void emitSyncComplete(const SyncthingDir &dir, int index, const SyncthingDev *remoteDev);
const SyncthingConnection &m_connection;
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD

View File

@ -54,17 +54,17 @@ QString trafficString(uint64 total, double rate)
/*!
* \brief Returns the string for global/local directory status, eg. "5 files, 1 directory, 23.7 MiB".
*/
QString directoryStatusString(quint64 files, quint64 dirs, quint64 size)
QString directoryStatusString(const SyncthingStatistics &stats)
{
return QCoreApplication::translate("Data::Utils", "%1 file(s)", nullptr, trQuandity(files)).arg(files) % QChar(',') % QChar(' ')
% QCoreApplication::translate("Data::Utils", "%1 dir(s)", nullptr, trQuandity(dirs)).arg(dirs) % QChar(',') % QChar(' ')
% QString::fromUtf8(dataSizeToString(size).data());
return QCoreApplication::translate("Data::Utils", "%1 file(s)", nullptr, trQuandity(stats.files)).arg(stats.files) % QChar(',') % QChar(' ')
% QCoreApplication::translate("Data::Utils", "%1 dir(s)", nullptr, trQuandity(stats.dirs)).arg(stats.dirs) % QChar(',') % QChar(' ')
% QString::fromUtf8(dataSizeToString(stats.bytes).data());
}
/*!
* \brief Returns the "sync complete" notication message for the specified directories.
*/
QString syncCompleteString(const std::vector<SyncthingDir *> &completedDirs)
QString syncCompleteString(const std::vector<const SyncthingDir *> &completedDirs)
{
switch (completedDirs.size()) {
case 0:

View File

@ -21,12 +21,13 @@ class DateTime;
namespace Data {
struct SyncthingStatistics;
struct SyncthingDir;
QString LIB_SYNCTHING_CONNECTOR_EXPORT agoString(ChronoUtilities::DateTime dateTime);
QString LIB_SYNCTHING_CONNECTOR_EXPORT trafficString(uint64 total, double rate);
QString LIB_SYNCTHING_CONNECTOR_EXPORT directoryStatusString(quint64 files, quint64 dirs, quint64 size);
QString LIB_SYNCTHING_CONNECTOR_EXPORT syncCompleteString(const std::vector<SyncthingDir *> &completedDirs);
QString LIB_SYNCTHING_CONNECTOR_EXPORT directoryStatusString(const Data::SyncthingStatistics &stats);
QString LIB_SYNCTHING_CONNECTOR_EXPORT syncCompleteString(const std::vector<const SyncthingDir *> &completedDirs);
bool LIB_SYNCTHING_CONNECTOR_EXPORT isLocal(const QUrl &url);
bool LIB_SYNCTHING_CONNECTOR_EXPORT setDirectoriesPaused(QJsonObject &syncthingConfig, const QStringList &dirIds, bool paused);
bool LIB_SYNCTHING_CONNECTOR_EXPORT setDevicesPaused(QJsonObject &syncthingConfig, const QStringList &dirs, bool paused);

View File

@ -161,8 +161,8 @@ bool SyncthingDirActions::updateStatus(const SyncthingDir &dir)
break;
}
}
m_globalStatusAction.setText(tr("Global: ") + directoryStatusString(dir.globalFiles, dir.globalDirs, dir.globalBytes));
m_localStatusAction.setText(tr("Local: ") + directoryStatusString(dir.localFiles, dir.localDirs, dir.localBytes));
m_globalStatusAction.setText(tr("Global: ") + directoryStatusString(dir.globalStats));
m_localStatusAction.setText(tr("Local: ") + directoryStatusString(dir.localStats));
m_lastScanAction.setText(tr("Last scan time: ") + agoString(dir.lastScanTime));
m_lastScanAction.setIcon(QIcon::fromTheme(QStringLiteral("accept_time_event")));
m_rescanIntervalAction.setText(tr("Rescan interval: %1 seconds").arg(dir.rescanInterval));

View File

@ -148,9 +148,9 @@ QVariant SyncthingDirectoryModel::data(const QModelIndex &index, int role) const
case 1:
return dir.path;
case 2:
return directoryStatusString(dir.globalFiles, dir.globalDirs, dir.globalBytes);
return directoryStatusString(dir.globalStats);
case 3:
return directoryStatusString(dir.localFiles, dir.localDirs, dir.localBytes);
return directoryStatusString(dir.localStats);
case 4:
if (!dir.deviceNames.isEmpty()) {
return dir.deviceNames.join(QStringLiteral(", "));

View File

@ -45,6 +45,7 @@ SyncthingApplet::SyncthingApplet(QObject *parent, const QVariantList &data)
: Applet(parent, data)
, m_aboutDlg(nullptr)
, m_connection()
, m_notifier(m_connection)
, m_dirModel(m_connection)
, m_devModel(m_connection)
, m_downloadModel(m_connection)
@ -53,7 +54,6 @@ SyncthingApplet::SyncthingApplet(QObject *parent, const QVariantList &data)
, m_webViewDlg(nullptr)
#endif
, m_currentConnectionConfig(-1)
, m_status(SyncthingStatus::Disconnected)
, m_initialized(false)
{
qmlRegisterUncreatableMetaObject(Data::staticMetaObject, "martchus.syncthingplasmoid", 0, 6, "Data", QStringLiteral("only enums"));
@ -74,7 +74,9 @@ void SyncthingApplet::init()
Applet::init();
// connect signals and slots
connect(&m_connection, &SyncthingConnection::statusChanged, this, &SyncthingApplet::handleConnectionStatusChanged);
connect(&m_notifier, &SyncthingNotifier::statusChanged, this, &SyncthingApplet::handleConnectionStatusChanged);
connect(&m_notifier, &SyncthingNotifier::syncComplete, &m_dbusNotifier, &DBusStatusNotifier::showSyncComplete);
connect(&m_notifier, &SyncthingNotifier::disconnected, &m_dbusNotifier, &DBusStatusNotifier::showDisconnect);
connect(&m_connection, &SyncthingConnection::newDevices, this, &SyncthingApplet::handleDevicesChanged);
connect(&m_connection, &SyncthingConnection::devStatusChanged, this, &SyncthingApplet::handleDevicesChanged);
connect(&m_connection, &SyncthingConnection::error, this, &SyncthingApplet::handleInternalError);
@ -304,6 +306,17 @@ void SyncthingApplet::copyToClipboard(const QString &text)
void SyncthingApplet::handleSettingsChanged()
{
const KConfigGroup config(this->config());
const auto &settings(Settings::values());
// apply notifiction settings
auto notifications(SyncthingHighLevelNotification::None);
if (settings.notifyOn.disconnect) {
notifications |= SyncthingHighLevelNotification::ConnectedDisconnected;
}
if (settings.notifyOn.syncComplete) {
notifications |= SyncthingHighLevelNotification::SyncComplete;
}
m_notifier.setEnabledNotifications(notifications);
// apply appearance settings
setSize(config.readEntry<QSize>("size", QSize(25, 25)));
@ -322,7 +335,7 @@ void SyncthingApplet::handleSettingsChanged()
void SyncthingApplet::handleConnectionStatusChanged(SyncthingStatus status)
{
if (m_initialized && m_status == status) {
if (m_initialized) {
return;
}
@ -330,37 +343,7 @@ void SyncthingApplet::handleConnectionStatusChanged(SyncthingStatus status)
m_statusInfo.updateConnectionStatus(m_connection);
m_statusInfo.updateConnectedDevices(m_connection);
// show notifications (FIXME: reduce C&P from trayicon.cpp)
const auto &settings = Settings::values();
switch (status) {
case SyncthingStatus::Disconnected:
if (m_initialized && settings.notifyOn.disconnect
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
&& !syncthingService().isManuallyStopped()
#endif
) {
m_dbusNotifier.showDisconnect();
}
break;
default:
m_dbusNotifier.hideDisconnect();
}
switch (status) {
case SyncthingStatus::Disconnected:
case SyncthingStatus::Reconnecting:
case SyncthingStatus::Synchronizing:
break;
default:
if (m_status == SyncthingStatus::Synchronizing && settings.notifyOn.syncComplete) {
const auto message(syncCompleteString(m_connection.completedDirs()));
if (!message.isEmpty()) {
m_dbusNotifier.showSyncComplete(message);
}
}
}
// set status and emit signal
m_status = status;
emit connectionStatusChanged();
}

View File

@ -9,6 +9,7 @@
#include "../../model/syncthingdownloadmodel.h"
#include "../../connector/syncthingconnection.h"
#include "../../connector/syncthingnotifier.h"
#include "../../connector/syncthingservice.h"
#include <qtutilities/aboutdialog/aboutdialog.h>
@ -135,6 +136,7 @@ private Q_SLOTS:
private:
Dialogs::AboutDialog *m_aboutDlg;
Data::SyncthingConnection m_connection;
Data::SyncthingNotifier m_notifier;
QtGui::StatusInfo m_statusInfo;
Data::SyncthingDirectoryModel m_dirModel;
Data::SyncthingDeviceModel m_devModel;
@ -146,7 +148,6 @@ private:
QtGui::WebViewDialog *m_webViewDlg;
#endif
int m_currentConnectionConfig;
Data::SyncthingStatus m_status;
bool m_initialized;
QSize m_size;
};

View File

@ -38,9 +38,7 @@ namespace QtGui {
*/
TrayIcon::TrayIcon(const QString &connectionConfig, QObject *parent)
: QSystemTrayIcon(parent)
, m_initialized(false)
, m_trayMenu(this, connectionConfig)
, m_status(SyncthingStatus::Disconnected)
, m_trayMenu(connectionConfig, this)
, m_messageClickedAction(TrayIconMessageClickedAction::None)
{
// set context menu
@ -76,26 +74,26 @@ TrayIcon::TrayIcon(const QString &connectionConfig, QObject *parent)
&QAction::triggered, this, &TrayIcon::deleteLater);
setContextMenu(&m_contextMenu);
// set initial status
handleConnectionStatusChanged(SyncthingStatus::Disconnected);
// connect signals and slots
SyncthingConnection *connection = &(m_trayMenu.widget()->connection());
const SyncthingConnection &connection = m_trayMenu.widget()->connection();
const SyncthingNotifier &notifier = 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);
connect(connection, &SyncthingConnection::newNotification, this, &TrayIcon::showSyncthingNotification);
connect(connection, &SyncthingConnection::statusChanged, this, &TrayIcon::handleConnectionStatusChanged);
connect(connection, &SyncthingConnection::newDevices, this, &TrayIcon::updateStatusIconAndText);
connect(connection, &SyncthingConnection::devStatusChanged, this, &TrayIcon::updateStatusIconAndText);
connect(&connection, &SyncthingConnection::error, this, &TrayIcon::showInternalError);
connect(&connection, &SyncthingConnection::newNotification, this, &TrayIcon::showSyncthingNotification);
connect(&notifier, &SyncthingNotifier::disconnected, this, &TrayIcon::showDisconnected);
connect(&notifier, &SyncthingNotifier::syncComplete, this, &TrayIcon::showSyncComplete);
connect(&connection, &SyncthingConnection::statusChanged, this, &TrayIcon::updateStatusIconAndText);
connect(&connection, &SyncthingConnection::newDevices, this, &TrayIcon::updateStatusIconAndText);
connect(&connection, &SyncthingConnection::devStatusChanged, this, &TrayIcon::updateStatusIconAndText);
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
connect(&m_dbusNotifier, &DBusStatusNotifier::connectRequested, connection,
connect(&m_dbusNotifier, &DBusStatusNotifier::connectRequested, &connection,
static_cast<void (SyncthingConnection::*)(void)>(&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::errorDetailsRequested, this, &TrayIcon::showInternalErrorsDialog);
connect(&notifier, &SyncthingNotifier::connected, &m_dbusNotifier, &DBusStatusNotifier::hideDisconnect);
#endif
m_initialized = true;
}
/*!
@ -146,14 +144,30 @@ void TrayIcon::handleMessageClicked()
}
}
void TrayIcon::handleConnectionStatusChanged(SyncthingStatus status)
void TrayIcon::showDisconnected()
{
if (m_initialized && m_status == status) {
return;
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
if (Settings::values().dbusNotifications) {
m_dbusNotifier.showDisconnect();
} else
#endif
{
m_messageClickedAction = TrayIconMessageClickedAction::None;
showMessage(QCoreApplication::applicationName(), tr("Disconnected from Syncthing"), QSystemTrayIcon::Warning);
}
}
void TrayIcon::showSyncComplete(const QString &message)
{
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
if (Settings::values().dbusNotifications) {
m_dbusNotifier.showSyncComplete(message);
} else
#endif
{
m_messageClickedAction = TrayIconMessageClickedAction::None;
showMessage(QCoreApplication::applicationName(), message, QSystemTrayIcon::Information);
}
updateStatusIconAndText();
showStatusNotification(status);
m_status = status;
}
void TrayIcon::handleErrorsCleared()
@ -182,7 +196,7 @@ void TrayIcon::showInternalError(
void TrayIcon::showSyncthingNotification(ChronoUtilities::DateTime when, const QString &message)
{
const auto &settings = Settings::values();
const auto &settings(Settings::values());
if (settings.notifyOn.syncthingErrors) {
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
if (settings.dbusNotifications) {
@ -210,57 +224,6 @@ void TrayIcon::updateStatusIconAndText()
setIcon(statusInfo.statusIcon());
}
void TrayIcon::showStatusNotification(SyncthingStatus status)
{
const SyncthingConnection &connection = trayMenu().widget()->connection();
const auto &settings = Settings::values();
switch (status) {
case SyncthingStatus::Disconnected:
if (m_initialized && settings.notifyOn.disconnect
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
&& !syncthingService().isManuallyStopped()
#endif
) {
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
if (settings.dbusNotifications) {
m_dbusNotifier.showDisconnect();
} else
#endif
{
m_messageClickedAction = TrayIconMessageClickedAction::None;
showMessage(QCoreApplication::applicationName(), tr("Disconnected from Syncthing"), QSystemTrayIcon::Warning);
}
}
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
break;
default:
m_dbusNotifier.hideDisconnect();
#endif
}
switch (status) {
case SyncthingStatus::Disconnected:
case SyncthingStatus::Reconnecting:
case SyncthingStatus::Synchronizing:
break;
default:
if (m_status == SyncthingStatus::Synchronizing && settings.notifyOn.syncComplete) {
const auto message(syncCompleteString(connection.completedDirs()));
if (!message.isEmpty()) {
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
if (settings.dbusNotifications) {
m_dbusNotifier.showSyncComplete(message);
} else
#endif
{
m_messageClickedAction = TrayIconMessageClickedAction::None;
showMessage(QCoreApplication::applicationName(), message, QSystemTrayIcon::Information);
}
}
}
}
}
void TrayIcon::showInternalErrorsDialog()
{
auto *const errorViewDlg = ErrorViewDialog::instance();

View File

@ -16,6 +16,8 @@ QT_FORWARD_DECLARE_CLASS(QNetworkRequest)
namespace Data {
enum class SyncthingStatus;
enum class SyncthingErrorCategory;
struct SyncthingDir;
struct SyncthingDev;
} // namespace Data
namespace QtGui {
@ -33,22 +35,20 @@ public slots:
void showInternalError(
const QString &errorMsg, Data::SyncthingErrorCategory category, int networkError, const QNetworkRequest &request, const QByteArray &response);
void showSyncthingNotification(ChronoUtilities::DateTime when, const QString &message);
void showStatusNotification(Data::SyncthingStatus status);
void showInternalErrorsDialog();
void updateStatusIconAndText();
private slots:
void handleActivated(QSystemTrayIcon::ActivationReason reason);
void handleMessageClicked();
void handleConnectionStatusChanged(Data::SyncthingStatus status);
void showDisconnected();
void showSyncComplete(const QString &message);
void handleErrorsCleared();
private:
bool m_initialized;
TrayMenu m_trayMenu;
QMenu m_contextMenu;
QAction *m_errorsAction;
Data::SyncthingStatus m_status;
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
DBusStatusNotifier m_dbusNotifier;
#endif
@ -59,6 +59,7 @@ inline TrayMenu &TrayIcon::trayMenu()
{
return m_trayMenu;
}
} // namespace QtGui
#endif // TRAY_ICON_H

View File

@ -11,15 +11,9 @@
namespace QtGui {
TrayMenu::TrayMenu(TrayIcon *trayIcon, const QString &connectionConfig, QWidget *parent)
: TrayMenu(connectionConfig, parent)
{
m_trayIcon = trayIcon;
}
TrayMenu::TrayMenu(const QString &connectionConfig, QWidget *parent)
TrayMenu::TrayMenu(const QString &connectionConfig, TrayIcon *trayIcon, QWidget *parent)
: QMenu(parent)
, m_trayIcon(nullptr)
, m_trayIcon(trayIcon)
{
auto *menuLayout = new QHBoxLayout;
menuLayout->setMargin(0), menuLayout->setSpacing(0);

View File

@ -12,8 +12,7 @@ class TrayMenu : public QMenu {
Q_OBJECT
public:
TrayMenu(TrayIcon *trayIcon, const QString &connectionConfig = QString(), QWidget *parent = nullptr);
TrayMenu(const QString &connectionConfig = QString(), QWidget *parent = nullptr);
TrayMenu(const QString &connectionConfig = QString(), TrayIcon *trayIcon = nullptr, QWidget *parent = nullptr);
QSize sizeHint() const;
TrayWidget *widget();

View File

@ -59,12 +59,11 @@ TrayWidget::TrayWidget(const QString &connectionConfig, TrayMenu *parent)
: QWidget(parent)
, m_menu(parent)
, m_ui(new Ui::TrayWidget)
,
#ifndef SYNCTHINGWIDGETS_NO_WEBVIEW
m_webViewDlg(nullptr)
,
, m_webViewDlg(nullptr)
#endif
m_dirModel(m_connection)
, m_notifier(m_connection)
, m_dirModel(m_connection)
, m_devModel(m_connection)
, m_dlModel(m_connection)
, m_selectedConnection(nullptr)
@ -363,6 +362,16 @@ void TrayWidget::applySettings(const QString &connectionConfig)
m_ui->connectionsPushButton->setText(m_selectedConnection->label);
const bool reconnectRequired = m_connection.applySettings(*m_selectedConnection);
// apply notifiction settings
auto notifications(SyncthingHighLevelNotification::None);
if (settings.notifyOn.disconnect) {
notifications |= SyncthingHighLevelNotification::ConnectedDisconnected;
}
if (settings.notifyOn.syncComplete) {
notifications |= SyncthingHighLevelNotification::SyncComplete;
}
m_notifier.setEnabledNotifications(notifications);
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
// reconnect to apply settings considering systemd
const bool couldReconnect = handleSystemdStatusChanged();

View File

@ -8,6 +8,7 @@
#include "../../model/syncthingdownloadmodel.h"
#include "../../connector/syncthingconnection.h"
#include "../../connector/syncthingnotifier.h"
#include "../../connector/syncthingprocess.h"
#include <QWidget>
@ -44,6 +45,7 @@ public:
~TrayWidget();
Data::SyncthingConnection &connection();
Data::SyncthingNotifier &notifier();
QMenu *connectionsMenu();
static const std::vector<TrayWidget *> &instances();
@ -91,6 +93,7 @@ private:
#endif
QFrame *m_cornerFrame;
Data::SyncthingConnection m_connection;
Data::SyncthingNotifier m_notifier;
Data::SyncthingDirectoryModel m_dirModel;
Data::SyncthingDeviceModel m_devModel;
Data::SyncthingDownloadModel m_dlModel;
@ -107,6 +110,11 @@ inline Data::SyncthingConnection &TrayWidget::connection()
return m_connection;
}
inline Data::SyncthingNotifier &TrayWidget::notifier()
{
return m_notifier;
}
inline QMenu *TrayWidget::connectionsMenu()
{
return m_connectionsMenu;