syncthingtray/tray/gui/trayicon.cpp
Martchus 975e86c895 Allow backend libraries to be used from other projects
So far the backend libraries' include paths were relative within this
repository. This means the header files could not be used at their
installed location.

This change replaces them with "<>" includes to fix that problem and adds
a new include directory so building everything at once still works.

With this change it should be easier to actually split some parts into
another repository if this one would become too big.
2021-01-25 19:48:11 +01:00

304 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(":/icons/hicolor/scalable/status/syncthing-default.svg")), 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::fromTheme(QStringLiteral("text-x-generic"), QIcon(QStringLiteral(":/icons/hicolor/scalable/mimetypes/text-x-generic.svg"))),
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()
{
auto *const errorViewDlg = InternalErrorsDialog::instance();
connect(errorViewDlg, &InternalErrorsDialog::errorsCleared, this, &TrayIcon::handleErrorsCleared);
centerWidget(errorViewDlg);
errorViewDlg->show();
}
} // namespace QtGui