Mixing ForkAwesome icons too much with the regular icon theme doesn't look good (depending on the theme) so let's prefer ForkAwesome icons within the tray widget which already uses ForkAwesome in some places like certain buttons and models. This makes it also look more in-line with Syncthing's web UI. For context menus and dialogs let's prefer system icons because there are hardly any ForkAwesome icons used/required so far and it is maybe nicer to be in-line with the system here.
303 lines
12 KiB
C++
303 lines
12 KiB
C++
#include "./trayicon.h"
|
|
#include "./traywidget.h"
|
|
|
|
#include <syncthingwidgets/misc/internalerrorsdialog.h>
|
|
#include <syncthingwidgets/misc/statusinfo.h>
|
|
#include <syncthingwidgets/misc/textviewdialog.h>
|
|
#include <syncthingwidgets/settings/settings.h>
|
|
|
|
#include <syncthingmodel/syncthingicons.h>
|
|
|
|
#include <syncthingconnector/syncthingconnection.h>
|
|
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
|
#include <syncthingconnector/syncthingservice.h>
|
|
#endif
|
|
#include <syncthingconnector/utils.h>
|
|
|
|
#include <qtutilities/misc/dialogutils.h>
|
|
|
|
#include <QCoreApplication>
|
|
#include <QPainter>
|
|
#include <QPixmap>
|
|
#include <QStringBuilder>
|
|
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
|
#include <QNetworkReply>
|
|
#endif
|
|
|
|
using namespace std;
|
|
using namespace QtUtilities;
|
|
using namespace Data;
|
|
|
|
namespace QtGui {
|
|
|
|
/*!
|
|
* \brief Instantiates a new tray icon.
|
|
*/
|
|
TrayIcon::TrayIcon(const QString &connectionConfig, QObject *parent)
|
|
: QSystemTrayIcon(parent)
|
|
, m_trayMenu(new TrayMenu(this, &m_parentWidget))
|
|
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
|
|
, m_dbusNotificationsEnabled(Settings::values().dbusNotifications)
|
|
#endif
|
|
, m_notifyOnSyncthingErrors(Settings::values().notifyOn.syncthingErrors)
|
|
, m_messageClickedAction(TrayIconMessageClickedAction::None)
|
|
{
|
|
// get widget, connection and notifier
|
|
const auto &widget(trayMenu().widget());
|
|
const auto &connection(widget.connection());
|
|
const auto ¬ifier(widget.notifier());
|
|
|
|
// set context menu
|
|
#ifndef SYNCTHINGTRAY_UNIFY_TRAY_MENUS
|
|
connect(m_contextMenu.addAction(QIcon(QStringLiteral("syncthing.fa")), tr("Open Syncthing")),
|
|
&QAction::triggered, &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, &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, &widget.connection(), &SyncthingConnection::rescanAllDirs);
|
|
connect(m_contextMenu.addAction(QIcon(QStringLiteral("file-code-o.fa")),
|
|
tr("Log")),
|
|
&QAction::triggered, &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(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, &widget, &TrayWidget::showAboutDialog);
|
|
m_contextMenu.addSeparator();
|
|
connect(m_contextMenu.addAction(
|
|
QIcon::fromTheme(QStringLiteral("window-close"), QIcon(QStringLiteral(":/icons/hicolor/scalable/actions/window-close.svg"))),
|
|
tr("Close")),
|
|
&QAction::triggered, this, &TrayIcon::deleteLater);
|
|
setContextMenu(&m_contextMenu);
|
|
#endif
|
|
|
|
// connect signals and slots
|
|
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(¬ifier, &SyncthingNotifier::syncthingProcessError, this, &TrayIcon::showLauncherError);
|
|
connect(¬ifier, &SyncthingNotifier::disconnected, this, &TrayIcon::showDisconnected);
|
|
connect(¬ifier, &SyncthingNotifier::syncComplete, this, &TrayIcon::showSyncComplete);
|
|
connect(¬ifier, &SyncthingNotifier::newDevice, this, &TrayIcon::showNewDev);
|
|
connect(¬ifier, &SyncthingNotifier::newDir, this, &TrayIcon::showNewDir);
|
|
connect(&connection, &SyncthingConnection::statusChanged, this, &TrayIcon::updateStatusIconAndText);
|
|
connect(&connection, &SyncthingConnection::newDevices, this, &TrayIcon::updateStatusIconAndText);
|
|
connect(&connection, &SyncthingConnection::devStatusChanged, this, &TrayIcon::updateStatusIconAndText);
|
|
connect(&IconManager::instance(), &IconManager::statusIconsChanged, this, &TrayIcon::updateStatusIconAndText);
|
|
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
|
|
connect(&m_dbusNotifier, &DBusStatusNotifier::connectRequested, &connection,
|
|
static_cast<void (SyncthingConnection::*)(void)>(&SyncthingConnection::connect));
|
|
connect(&m_dbusNotifier, &DBusStatusNotifier::dismissNotificationsRequested, &widget, &TrayWidget::dismissNotifications);
|
|
connect(&m_dbusNotifier, &DBusStatusNotifier::showNotificationsRequested, &widget, &TrayWidget::showNotifications);
|
|
connect(&m_dbusNotifier, &DBusStatusNotifier::errorDetailsRequested, this, &TrayIcon::showInternalErrorsDialog);
|
|
connect(&m_dbusNotifier, &DBusStatusNotifier::webUiRequested, &widget, &TrayWidget::showWebUi);
|
|
connect(¬ifier, &SyncthingNotifier::connected, &m_dbusNotifier, &DBusStatusNotifier::hideDisconnect);
|
|
#endif
|
|
|
|
// apply settings, this also establishes the connection to Syncthing (according to settings)
|
|
// note: It is important to apply settings only after all Signals & Slots have been connected (e.g. to handle SyncthingConnection::error()).
|
|
// note: This weirdly calls updateStatusIconAndText(). So there is not need to call it again within this constructor.
|
|
trayMenu().widget().applySettings(connectionConfig);
|
|
}
|
|
|
|
/*!
|
|
* \brief Moves the specified \a point in the specified \a rect.
|
|
*/
|
|
void moveInside(QPoint &point, const QRect &rect)
|
|
{
|
|
if (point.y() < rect.top()) {
|
|
point.setY(rect.top());
|
|
} else if (point.y() > rect.bottom()) {
|
|
point.setY(rect.bottom());
|
|
}
|
|
if (point.x() < rect.left()) {
|
|
point.setX(rect.left());
|
|
} else if (point.x() > rect.right()) {
|
|
point.setX(rect.right());
|
|
}
|
|
}
|
|
|
|
void TrayIcon::handleActivated(QSystemTrayIcon::ActivationReason reason)
|
|
{
|
|
switch (reason) {
|
|
case QSystemTrayIcon::Context:
|
|
// can't catch that event on Plasma 5 anyways
|
|
break;
|
|
case QSystemTrayIcon::MiddleClick:
|
|
trayMenu().widget().showWebUi();
|
|
break;
|
|
case QSystemTrayIcon::Trigger: {
|
|
trayMenu().showUsingPositioningSettings();
|
|
break;
|
|
}
|
|
default:;
|
|
}
|
|
}
|
|
|
|
void TrayIcon::handleMessageClicked()
|
|
{
|
|
switch (m_messageClickedAction) {
|
|
case TrayIconMessageClickedAction::None:
|
|
return;
|
|
case TrayIconMessageClickedAction::DismissNotification:
|
|
trayMenu().widget().dismissNotifications();
|
|
break;
|
|
case TrayIconMessageClickedAction::ShowInternalErrors:
|
|
showInternalErrorsDialog();
|
|
break;
|
|
case TrayIconMessageClickedAction::ShowWebUi:
|
|
trayMenu().widget().showWebUi();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TrayIcon::showDisconnected()
|
|
{
|
|
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
|
|
if (m_dbusNotificationsEnabled) {
|
|
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 (m_dbusNotificationsEnabled) {
|
|
m_dbusNotifier.showSyncComplete(message);
|
|
} else
|
|
#endif
|
|
{
|
|
m_messageClickedAction = TrayIconMessageClickedAction::None;
|
|
showMessage(QCoreApplication::applicationName(), message, QSystemTrayIcon::Information);
|
|
}
|
|
}
|
|
|
|
void TrayIcon::handleErrorsCleared()
|
|
{
|
|
#ifndef SYNCTHINGTRAY_UNIFY_TRAY_MENUS
|
|
m_errorsAction->setVisible(false);
|
|
#endif
|
|
}
|
|
|
|
void TrayIcon::showInternalError(
|
|
const QString &errorMessage, SyncthingErrorCategory category, int networkError, const QNetworkRequest &request, const QByteArray &response)
|
|
{
|
|
if (!InternalError::isRelevant(trayMenu().widget().connection(), category, networkError)) {
|
|
return;
|
|
}
|
|
InternalError error(errorMessage, request.url(), response);
|
|
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
|
|
if (m_dbusNotificationsEnabled) {
|
|
m_dbusNotifier.showInternalError(error);
|
|
} else
|
|
#endif
|
|
{
|
|
m_messageClickedAction = TrayIconMessageClickedAction::ShowInternalErrors;
|
|
showMessage(tr("Error"), errorMessage, QSystemTrayIcon::Critical);
|
|
}
|
|
InternalErrorsDialog::addError(move(error));
|
|
#ifdef SYNCTHINGTRAY_UNIFY_TRAY_MENUS
|
|
trayMenu().widget().showInternalErrorsButton();
|
|
#else
|
|
m_errorsAction->setVisible(true);
|
|
#endif
|
|
}
|
|
|
|
void TrayIcon::showLauncherError(const QString &errorMessage, const QString &additionalInfo)
|
|
{
|
|
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
|
|
if (m_dbusNotificationsEnabled) {
|
|
m_dbusNotifier.showLauncherError(errorMessage, additionalInfo);
|
|
} else
|
|
#endif
|
|
{
|
|
m_messageClickedAction = TrayIconMessageClickedAction::None;
|
|
showMessage(tr("Launcher error"), QStringList({ errorMessage, additionalInfo }).join(QChar('\n')), QSystemTrayIcon::Critical);
|
|
}
|
|
}
|
|
|
|
void TrayIcon::showSyncthingNotification(CppUtilities::DateTime when, const QString &message)
|
|
{
|
|
if (m_notifyOnSyncthingErrors) {
|
|
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
|
|
if (m_dbusNotificationsEnabled) {
|
|
m_dbusNotifier.showSyncthingNotification(when, message);
|
|
} else
|
|
#else
|
|
Q_UNUSED(when)
|
|
#endif
|
|
{
|
|
m_messageClickedAction = TrayIconMessageClickedAction::DismissNotification;
|
|
showMessage(tr("Syncthing notification - click to dismiss"), message, QSystemTrayIcon::Warning);
|
|
}
|
|
}
|
|
updateStatusIconAndText();
|
|
}
|
|
|
|
void TrayIcon::updateStatusIconAndText()
|
|
{
|
|
auto &trayWidget = trayMenu().widget();
|
|
const auto statusInfo = StatusInfo(trayMenu().widget().connection(),
|
|
TrayWidget::instances().size() > 1 && trayWidget.selectedConnection() ? trayWidget.selectedConnection()->label : QString());
|
|
if (statusInfo.additionalStatusText().isEmpty()) {
|
|
setToolTip(statusInfo.statusText());
|
|
} else {
|
|
setToolTip(statusInfo.statusText() % QChar('\n') % statusInfo.additionalStatusText());
|
|
}
|
|
setIcon(statusInfo.statusIcon());
|
|
}
|
|
|
|
void TrayIcon::showNewDev(const QString &devId, const QString &message)
|
|
{
|
|
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
|
|
if (m_dbusNotificationsEnabled) {
|
|
m_dbusNotifier.showNewDev(devId, message);
|
|
} else
|
|
#else
|
|
Q_UNUSED(devId)
|
|
#endif
|
|
{
|
|
m_messageClickedAction = TrayIconMessageClickedAction::ShowWebUi;
|
|
showMessage(tr("Syncthing device wants to connect - click for web UI"), message, QSystemTrayIcon::Information);
|
|
}
|
|
}
|
|
|
|
void TrayIcon::showNewDir(const QString &devId, const QString &dirId, const QString &message)
|
|
{
|
|
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
|
|
if (m_dbusNotificationsEnabled) {
|
|
m_dbusNotifier.showNewDir(devId, dirId, message);
|
|
} else
|
|
#else
|
|
Q_UNUSED(devId)
|
|
Q_UNUSED(dirId)
|
|
#endif
|
|
{
|
|
m_messageClickedAction = TrayIconMessageClickedAction::ShowWebUi;
|
|
showMessage(tr("New Syncthing directory - click for web UI"), message, QSystemTrayIcon::Information);
|
|
}
|
|
}
|
|
|
|
void TrayIcon::showInternalErrorsDialog()
|
|
{
|
|
if (!InternalErrorsDialog::hasInstance()) {
|
|
connect(InternalErrorsDialog::instance(), &InternalErrorsDialog::errorsCleared, this, &TrayIcon::handleErrorsCleared);
|
|
}
|
|
trayMenu().widget().showInternalErrorsDialog();
|
|
}
|
|
} // namespace QtGui
|