syncthingtray/tray/gui/trayicon.cpp
Martchus 7b6121cb05 Use ForkAwesome icons more consistently in tray widget
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.
2021-10-15 00:23:22 +02:00

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 &notifier(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(&notifier, &SyncthingNotifier::syncthingProcessError, this, &TrayIcon::showLauncherError);
connect(&notifier, &SyncthingNotifier::disconnected, this, &TrayIcon::showDisconnected);
connect(&notifier, &SyncthingNotifier::syncComplete, this, &TrayIcon::showSyncComplete);
connect(&notifier, &SyncthingNotifier::newDevice, this, &TrayIcon::showNewDev);
connect(&notifier, &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(&notifier, &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