2013-03-20 09:03:49 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) by Cédric Bellegarde <gnumdk@gmail.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* for more details.
|
|
|
|
*/
|
|
|
|
|
2019-12-02 13:10:18 +00:00
|
|
|
#include "accountmanager.h"
|
2013-03-20 09:03:49 +00:00
|
|
|
#include "systray.h"
|
2014-07-10 22:31:24 +00:00
|
|
|
#include "theme.h"
|
2017-03-27 09:12:28 +00:00
|
|
|
#include "config.h"
|
2020-04-24 16:34:11 +00:00
|
|
|
#include "common/utility.h"
|
2021-10-29 07:05:51 +00:00
|
|
|
#include "tray/svgimageprovider.h"
|
2021-10-25 09:37:58 +00:00
|
|
|
#include "tray/usermodel.h"
|
2021-11-12 20:02:58 +00:00
|
|
|
#include "wheelhandler.h"
|
2022-01-20 11:54:36 +00:00
|
|
|
#include "tray/trayimageprovider.h"
|
2021-06-21 11:33:22 +00:00
|
|
|
#include "configfile.h"
|
2021-11-17 09:36:05 +00:00
|
|
|
#include "accessmanager.h"
|
2022-06-24 09:49:38 +00:00
|
|
|
#include "callstatechecker.h"
|
2013-03-20 09:03:49 +00:00
|
|
|
|
2020-01-21 08:55:26 +00:00
|
|
|
#include <QCursor>
|
2019-12-30 13:14:28 +00:00
|
|
|
#include <QGuiApplication>
|
2020-03-22 15:14:26 +00:00
|
|
|
#include <QQmlApplicationEngine>
|
2020-05-20 22:28:23 +00:00
|
|
|
#include <QQmlContext>
|
2020-06-23 09:20:30 +00:00
|
|
|
#include <QQuickWindow>
|
2022-04-21 22:32:01 +00:00
|
|
|
#include <QVariantMap>
|
2019-12-30 13:14:28 +00:00
|
|
|
#include <QScreen>
|
2020-06-29 17:41:51 +00:00
|
|
|
#include <QMenu>
|
2022-02-04 02:39:38 +00:00
|
|
|
#include <QGuiApplication>
|
2019-12-02 13:10:18 +00:00
|
|
|
|
2013-03-22 10:34:21 +00:00
|
|
|
#ifdef USE_FDO_NOTIFICATIONS
|
2013-03-20 09:03:49 +00:00
|
|
|
#include <QDBusConnection>
|
2013-03-22 10:58:55 +00:00
|
|
|
#include <QDBusInterface>
|
2013-03-20 09:03:49 +00:00
|
|
|
#include <QDBusMessage>
|
|
|
|
#include <QDBusPendingCall>
|
2013-03-22 10:58:55 +00:00
|
|
|
#define NOTIFICATIONS_SERVICE "org.freedesktop.Notifications"
|
|
|
|
#define NOTIFICATIONS_PATH "/org/freedesktop/Notifications"
|
|
|
|
#define NOTIFICATIONS_IFACE "org.freedesktop.Notifications"
|
2013-03-20 09:03:49 +00:00
|
|
|
#endif
|
|
|
|
|
2014-11-09 21:34:07 +00:00
|
|
|
namespace OCC {
|
2013-10-01 11:51:40 +00:00
|
|
|
|
2020-06-11 17:27:40 +00:00
|
|
|
Q_LOGGING_CATEGORY(lcSystray, "nextcloud.gui.systray")
|
|
|
|
|
2020-01-11 16:34:56 +00:00
|
|
|
Systray *Systray::_instance = nullptr;
|
|
|
|
|
|
|
|
Systray *Systray::instance()
|
|
|
|
{
|
2020-06-10 01:47:49 +00:00
|
|
|
if (!_instance) {
|
2020-01-11 16:34:56 +00:00
|
|
|
_instance = new Systray();
|
|
|
|
}
|
|
|
|
return _instance;
|
|
|
|
}
|
|
|
|
|
2020-06-24 11:50:17 +00:00
|
|
|
void Systray::setTrayEngine(QQmlApplicationEngine *trayEngine)
|
2019-12-02 13:10:18 +00:00
|
|
|
{
|
2020-06-24 11:50:17 +00:00
|
|
|
_trayEngine = trayEngine;
|
|
|
|
|
2021-11-17 09:36:05 +00:00
|
|
|
_trayEngine->setNetworkAccessManagerFactory(&_accessManagerFactory);
|
|
|
|
|
2020-01-19 19:13:12 +00:00
|
|
|
_trayEngine->addImportPath("qrc:/qml/theme");
|
2020-01-03 12:09:29 +00:00
|
|
|
_trayEngine->addImageProvider("avatars", new ImageProvider);
|
2021-10-29 07:05:51 +00:00
|
|
|
_trayEngine->addImageProvider(QLatin1String("svgimage-custom-color"), new OCC::Ui::SvgImageProvider);
|
2022-01-20 11:54:36 +00:00
|
|
|
_trayEngine->addImageProvider(QLatin1String("tray-image-provider"), new TrayImageProvider);
|
2020-06-24 11:50:17 +00:00
|
|
|
}
|
2020-06-15 15:01:39 +00:00
|
|
|
|
2020-06-24 11:50:17 +00:00
|
|
|
Systray::Systray()
|
|
|
|
: QSystemTrayIcon(nullptr)
|
|
|
|
{
|
2022-06-06 17:05:12 +00:00
|
|
|
#if defined(Q_OS_MACOS) && defined(BUILD_OWNCLOUD_OSX_BUNDLE)
|
2022-05-10 14:12:15 +00:00
|
|
|
setUserNotificationCenterDelegate();
|
2022-05-17 17:20:04 +00:00
|
|
|
checkNotificationAuth(MacNotificationAuthorizationOptions::Default); // No provisional auth, ask user explicitly first time
|
2022-05-10 14:12:15 +00:00
|
|
|
registerNotificationCategories(QString(tr("Download")));
|
2022-09-29 18:01:35 +00:00
|
|
|
#elif !defined(Q_OS_MACOS)
|
2022-05-05 10:10:17 +00:00
|
|
|
connect(AccountManager::instance(), &AccountManager::accountAdded,
|
|
|
|
this, &Systray::setupContextMenu);
|
|
|
|
connect(AccountManager::instance(), &AccountManager::accountRemoved,
|
|
|
|
this, &Systray::setupContextMenu);
|
|
|
|
setupContextMenu();
|
2020-06-29 17:41:51 +00:00
|
|
|
#endif
|
|
|
|
|
2022-07-08 14:49:27 +00:00
|
|
|
connect(UserModel::instance(), &UserModel::currentUserChanged,
|
|
|
|
this, &Systray::slotCurrentUserChanged);
|
2020-12-14 14:58:52 +00:00
|
|
|
connect(UserModel::instance(), &UserModel::addAccount,
|
|
|
|
this, &Systray::openAccountWizard);
|
2020-01-11 16:06:42 +00:00
|
|
|
|
2022-06-29 17:57:52 +00:00
|
|
|
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
2020-01-11 16:06:42 +00:00
|
|
|
connect(AccountManager::instance(), &AccountManager::accountAdded,
|
2022-06-30 15:42:39 +00:00
|
|
|
this, [this]{ showWindow(); });
|
2022-06-29 17:57:52 +00:00
|
|
|
#else
|
|
|
|
// Since the positioning of the QSystemTrayIcon is borked on non-Windows and non-macOS desktop environments,
|
|
|
|
// we hardcode the position of the tray to be in the center when we add a new account from somewhere like
|
|
|
|
// the wizard. Otherwise with the conventional method we end up with the tray appearing wherever the cursor
|
|
|
|
// is placed
|
|
|
|
|
|
|
|
connect(AccountManager::instance(), &AccountManager::accountAdded,
|
2022-06-30 15:42:39 +00:00
|
|
|
this, [this]{ showWindow(WindowPosition::Center); });
|
2022-06-29 17:57:52 +00:00
|
|
|
#endif
|
2019-12-02 13:10:18 +00:00
|
|
|
}
|
|
|
|
|
2020-01-11 14:05:37 +00:00
|
|
|
void Systray::create()
|
|
|
|
{
|
2020-06-24 11:50:17 +00:00
|
|
|
if (_trayEngine) {
|
|
|
|
if (!AccountManager::instance()->accounts().isEmpty()) {
|
|
|
|
_trayEngine->rootContext()->setContextProperty("activityModel", UserModel::instance()->currentActivityModel());
|
|
|
|
}
|
2022-06-30 15:42:39 +00:00
|
|
|
|
|
|
|
QQmlComponent trayWindowComponent(_trayEngine, QStringLiteral("qrc:/qml/src/gui/tray/Window.qml"));
|
2022-08-16 10:16:59 +00:00
|
|
|
|
|
|
|
if(trayWindowComponent.isError()) {
|
|
|
|
qCWarning(lcSystray) << trayWindowComponent.errorString();
|
|
|
|
} else {
|
|
|
|
_trayWindow.reset(qobject_cast<QQuickWindow*>(trayWindowComponent.create()));
|
|
|
|
}
|
2020-01-11 14:05:37 +00:00
|
|
|
}
|
2020-03-22 15:14:26 +00:00
|
|
|
hideWindow();
|
2020-04-24 16:34:11 +00:00
|
|
|
emit activated(QSystemTrayIcon::ActivationReason::Unknown);
|
2020-07-21 09:44:52 +00:00
|
|
|
|
|
|
|
const auto folderMap = FolderMan::instance()->map();
|
|
|
|
for (const auto *folder : folderMap) {
|
2020-07-21 10:44:45 +00:00
|
|
|
if (!folder->syncPaused()) {
|
2020-07-17 13:18:03 +00:00
|
|
|
_syncIsPaused = false;
|
2020-07-21 10:44:45 +00:00
|
|
|
break;
|
2020-07-17 13:18:03 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-11 14:05:37 +00:00
|
|
|
}
|
|
|
|
|
2022-06-30 15:42:39 +00:00
|
|
|
void Systray::showWindow(WindowPosition position)
|
|
|
|
{
|
|
|
|
if(isOpen() || !_trayWindow) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(position == WindowPosition::Center) {
|
|
|
|
positionWindowAtScreenCenter(_trayWindow.data());
|
|
|
|
} else {
|
|
|
|
positionWindowAtTray(_trayWindow.data());
|
|
|
|
}
|
|
|
|
_trayWindow->show();
|
|
|
|
_trayWindow->raise();
|
|
|
|
_trayWindow->requestActivate();
|
|
|
|
|
|
|
|
setIsOpen(true);
|
|
|
|
|
|
|
|
UserModel::instance()->fetchCurrentActivityModel();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Systray::hideWindow()
|
|
|
|
{
|
|
|
|
if(!isOpen() || !_trayWindow) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_trayWindow->hide();
|
|
|
|
setIsOpen(false);
|
|
|
|
}
|
|
|
|
|
2022-05-05 10:10:17 +00:00
|
|
|
void Systray::setupContextMenu()
|
|
|
|
{
|
|
|
|
const auto oldContextMenu = _contextMenu.data();
|
|
|
|
// If we delete the old QMenu before setting the new one the client will crash on GNOME.
|
|
|
|
// Let's delete it once this method is over
|
|
|
|
if(oldContextMenu) {
|
|
|
|
oldContextMenu->deleteLater();
|
|
|
|
}
|
|
|
|
|
|
|
|
_contextMenu = new QMenu();
|
2022-06-20 17:13:11 +00:00
|
|
|
// NOTE: for reasons unclear, setting the the new menu after adding all the actions
|
|
|
|
// will not work on GNOME, as the old menu will not be correctly replaced.
|
|
|
|
setContextMenu(_contextMenu);
|
2022-05-05 10:10:17 +00:00
|
|
|
|
|
|
|
if (AccountManager::instance()->accounts().isEmpty()) {
|
|
|
|
_contextMenu->addAction(tr("Add account"), this, &Systray::openAccountWizard);
|
|
|
|
} else {
|
2022-06-30 15:42:39 +00:00
|
|
|
_contextMenu->addAction(tr("Open main dialog"), this, [this]{ showWindow(); });
|
2022-05-05 10:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto pauseAction = _contextMenu->addAction(tr("Pause sync"), this, &Systray::slotPauseAllFolders);
|
|
|
|
auto resumeAction = _contextMenu->addAction(tr("Resume sync"), this, &Systray::slotUnpauseAllFolders);
|
|
|
|
_contextMenu->addAction(tr("Settings"), this, &Systray::openSettings);
|
|
|
|
_contextMenu->addAction(tr("Help"), this, &Systray::openHelp);
|
|
|
|
_contextMenu->addAction(tr("Exit %1").arg(Theme::instance()->appNameGUI()), this, &Systray::shutdown);
|
|
|
|
|
|
|
|
connect(_contextMenu, &QMenu::aboutToShow, [=] {
|
|
|
|
const auto folders = FolderMan::instance()->map();
|
|
|
|
|
|
|
|
const auto allPaused = std::all_of(std::cbegin(folders), std::cend(folders), [](Folder *f) { return f->syncPaused(); });
|
|
|
|
const auto pauseText = folders.size() > 1 ? tr("Pause sync for all") : tr("Pause sync");
|
|
|
|
pauseAction->setText(pauseText);
|
|
|
|
pauseAction->setVisible(!allPaused);
|
|
|
|
pauseAction->setEnabled(!allPaused);
|
|
|
|
|
|
|
|
const auto anyPaused = std::any_of(std::cbegin(folders), std::cend(folders), [](Folder *f) { return f->syncPaused(); });
|
|
|
|
const auto resumeText = folders.size() > 1 ? tr("Resume sync for all") : tr("Resume sync");
|
|
|
|
resumeAction->setText(resumeText);
|
|
|
|
resumeAction->setVisible(anyPaused);
|
|
|
|
resumeAction->setEnabled(anyPaused);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-09-14 10:25:56 +00:00
|
|
|
void Systray::destroyDialog(QQuickWindow *dialog) const
|
|
|
|
{
|
|
|
|
dialog->destroy();
|
|
|
|
dialog->deleteLater();
|
|
|
|
}
|
|
|
|
|
2022-06-24 09:49:38 +00:00
|
|
|
void Systray::createCallDialog(const Activity &callNotification, const AccountStatePtr accountState)
|
2022-04-21 22:32:01 +00:00
|
|
|
{
|
|
|
|
qCDebug(lcSystray) << "Starting a new call dialog for notification with id: " << callNotification._id << "with text: " << callNotification._subject;
|
|
|
|
|
|
|
|
if (_trayEngine && !_callsAlreadyNotified.contains(callNotification._id)) {
|
|
|
|
const QVariantMap talkNotificationData{
|
|
|
|
{"conversationToken", callNotification._talkNotificationData.conversationToken},
|
|
|
|
{"messageId", callNotification._talkNotificationData.messageId},
|
|
|
|
{"messageSent", callNotification._talkNotificationData.messageSent},
|
|
|
|
{"userAvatar", callNotification._talkNotificationData.userAvatar},
|
|
|
|
};
|
|
|
|
|
|
|
|
QVariantList links;
|
|
|
|
for(const auto &link : callNotification._links) {
|
|
|
|
links.append(QVariantMap{
|
|
|
|
{"imageSource", link._imageSource},
|
|
|
|
{"imageSourceHovered", link._imageSourceHovered},
|
|
|
|
{"label", link._label},
|
|
|
|
{"link", link._link},
|
|
|
|
{"primary", link._primary},
|
|
|
|
{"verb", link._verb},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
const QVariantMap initialProperties{
|
2022-06-24 09:49:38 +00:00
|
|
|
{"accountState", QVariant::fromValue(accountState.data())},
|
2022-04-21 22:32:01 +00:00
|
|
|
{"talkNotificationData", talkNotificationData},
|
|
|
|
{"links", links},
|
|
|
|
{"subject", callNotification._subject},
|
|
|
|
{"link", callNotification._link},
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto callDialog = new QQmlComponent(_trayEngine, QStringLiteral("qrc:/qml/src/gui/tray/CallNotificationDialog.qml"));
|
|
|
|
|
2022-08-16 10:16:59 +00:00
|
|
|
if(callDialog->isError()) {
|
|
|
|
qCWarning(lcSystray) << callDialog->errorString();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-09-14 10:25:56 +00:00
|
|
|
// This call dialog gets deallocated on close conditions
|
|
|
|
// by a call from the QML side to the destroyDialog slot
|
2022-08-16 10:16:59 +00:00
|
|
|
callDialog->createWithInitialProperties(initialProperties);
|
2022-04-21 22:32:01 +00:00
|
|
|
_callsAlreadyNotified.insert(callNotification._id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-28 11:26:57 +00:00
|
|
|
void Systray::createEditFileLocallyLoadingDialog(const QString &fileName)
|
|
|
|
{
|
|
|
|
if (_editFileLocallyLoadingDialog) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
qCDebug(lcSystray) << "Opening a file local editing dialog...";
|
|
|
|
|
|
|
|
const auto editFileLocallyLoadingDialog = new QQmlComponent(_trayEngine, QStringLiteral("qrc:/qml/src/gui/tray/EditFileLocallyLoadingDialog.qml"));
|
|
|
|
|
|
|
|
if (editFileLocallyLoadingDialog->isError()) {
|
|
|
|
qCWarning(lcSystray) << editFileLocallyLoadingDialog->errorString();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_editFileLocallyLoadingDialog = editFileLocallyLoadingDialog->createWithInitialProperties(QVariantMap{{QStringLiteral("fileName"), fileName}});
|
|
|
|
}
|
|
|
|
|
|
|
|
void Systray::destroyEditFileLocallyLoadingDialog()
|
|
|
|
{
|
|
|
|
if (!_editFileLocallyLoadingDialog) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
qCDebug(lcSystray) << "Closing a file local editing dialog...";
|
|
|
|
_editFileLocallyLoadingDialog->deleteLater();
|
|
|
|
_editFileLocallyLoadingDialog = nullptr;
|
|
|
|
}
|
|
|
|
|
2023-05-03 16:09:12 +00:00
|
|
|
void Systray::createResolveConflictsDialog(const OCC::ActivityList &allConflicts)
|
2023-04-28 10:43:04 +00:00
|
|
|
{
|
2023-05-16 09:30:45 +00:00
|
|
|
const auto conflictsDialog = std::make_unique<QQmlComponent>(_trayEngine, QStringLiteral("qrc:/qml/src/gui/ResolveConflictsDialog.qml"));
|
2023-05-03 16:09:12 +00:00
|
|
|
const QVariantMap initialProperties{
|
|
|
|
{"allConflicts", QVariant::fromValue(allConflicts)},
|
|
|
|
};
|
2023-04-28 10:43:04 +00:00
|
|
|
|
2023-05-04 08:32:56 +00:00
|
|
|
if(conflictsDialog->isError()) {
|
|
|
|
qCWarning(lcSystray) << conflictsDialog->errorString();
|
2023-04-28 10:43:04 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This call dialog gets deallocated on close conditions
|
|
|
|
// by a call from the QML side to the destroyDialog slot
|
2023-05-16 09:30:45 +00:00
|
|
|
auto dialog = QScopedPointer(conflictsDialog->createWithInitialProperties(initialProperties));
|
2023-05-04 08:32:56 +00:00
|
|
|
if (!dialog) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
dialog->setParent(QGuiApplication::instance());
|
|
|
|
|
2023-05-16 09:30:45 +00:00
|
|
|
auto dialogWindow = qobject_cast<QQuickWindow*>(dialog.data());
|
2023-05-04 08:32:56 +00:00
|
|
|
if (!dialogWindow) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
dialogWindow->show();
|
|
|
|
dialogWindow->raise();
|
|
|
|
dialogWindow->requestActivate();
|
2023-05-16 09:30:45 +00:00
|
|
|
dialog.take();
|
2023-04-28 10:43:04 +00:00
|
|
|
}
|
|
|
|
|
2022-07-25 16:57:18 +00:00
|
|
|
bool Systray::raiseDialogs()
|
|
|
|
{
|
2022-10-01 13:52:53 +00:00
|
|
|
return raiseFileDetailDialogs();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Systray::raiseFileDetailDialogs(const QString &localPath)
|
|
|
|
{
|
|
|
|
if(_fileDetailDialogs.empty()) {
|
2022-07-25 16:57:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-10-01 13:52:53 +00:00
|
|
|
auto it = _fileDetailDialogs.begin();
|
|
|
|
while (it != _fileDetailDialogs.end()) {
|
|
|
|
const auto dialog = *it;
|
2022-10-30 11:56:11 +00:00
|
|
|
auto nullDialog = dialog == nullptr;
|
2022-07-25 16:57:18 +00:00
|
|
|
|
2022-10-30 11:56:11 +00:00
|
|
|
if (!nullDialog && !dialog->isVisible()) {
|
2022-10-01 08:26:06 +00:00
|
|
|
destroyDialog(dialog);
|
2022-10-30 11:56:11 +00:00
|
|
|
nullDialog = true;
|
2022-07-25 16:57:18 +00:00
|
|
|
}
|
|
|
|
|
2022-10-30 11:56:11 +00:00
|
|
|
if (!nullDialog && (localPath.isEmpty() || dialog->property("localPath").toString() == localPath)) {
|
2022-10-01 13:52:53 +00:00
|
|
|
dialog->show();
|
|
|
|
dialog->raise();
|
|
|
|
dialog->requestActivate();
|
2022-07-25 16:57:18 +00:00
|
|
|
|
2022-10-01 13:52:53 +00:00
|
|
|
++it;
|
|
|
|
continue;
|
|
|
|
}
|
2022-07-25 16:57:18 +00:00
|
|
|
|
2022-10-01 13:52:53 +00:00
|
|
|
it = _fileDetailDialogs.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
2022-07-25 16:57:18 +00:00
|
|
|
|
2023-06-02 11:55:08 +00:00
|
|
|
// If it is empty then we have raised no dialogs, so return false (and vice-versa)
|
2022-10-01 13:52:53 +00:00
|
|
|
return !_fileDetailDialogs.empty();
|
2022-07-25 16:57:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Systray::createFileDetailsDialog(const QString &localPath)
|
|
|
|
{
|
2022-10-01 13:52:53 +00:00
|
|
|
if (raiseFileDetailDialogs(localPath)) {
|
|
|
|
qCDebug(lcSystray) << "Reopening an existing file details dialog for " << localPath;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-07-25 16:57:18 +00:00
|
|
|
qCDebug(lcSystray) << "Opening new file details dialog for " << localPath;
|
|
|
|
|
2022-10-01 13:52:53 +00:00
|
|
|
if (!_trayEngine) {
|
2022-07-25 16:57:18 +00:00
|
|
|
qCWarning(lcSystray) << "Could not open file details dialog for" << localPath << "as no tray engine was available";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto folder = FolderMan::instance()->folderForPath(localPath);
|
|
|
|
if (!folder) {
|
|
|
|
qCWarning(lcSystray) << "Could not open file details dialog for" << localPath << "no responsible folder found";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QVariantMap initialProperties{
|
|
|
|
{"accountState", QVariant::fromValue(folder->accountState())},
|
|
|
|
{"localPath", localPath},
|
|
|
|
};
|
|
|
|
|
2022-10-01 08:26:06 +00:00
|
|
|
QQmlComponent fileDetailsDialog(_trayEngine, QStringLiteral("qrc:/qml/src/gui/filedetails/FileDetailsWindow.qml"));
|
2022-07-25 16:57:18 +00:00
|
|
|
|
2022-10-01 08:26:06 +00:00
|
|
|
if (!fileDetailsDialog.isError()) {
|
|
|
|
const auto createdDialog = fileDetailsDialog.createWithInitialProperties(initialProperties);
|
|
|
|
const auto dialog = qobject_cast<QQuickWindow*>(createdDialog);
|
2022-07-25 16:57:18 +00:00
|
|
|
|
2022-10-01 08:26:06 +00:00
|
|
|
if(!dialog) {
|
2022-07-25 16:57:18 +00:00
|
|
|
qCWarning(lcSystray) << "File details dialog window resulted in creation of object that was not a window!";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-01 13:52:53 +00:00
|
|
|
_fileDetailDialogs.append(dialog);
|
2022-07-25 16:57:18 +00:00
|
|
|
|
|
|
|
dialog->show();
|
|
|
|
dialog->raise();
|
|
|
|
dialog->requestActivate();
|
|
|
|
|
|
|
|
} else {
|
2022-10-01 08:26:06 +00:00
|
|
|
qCWarning(lcSystray) << fileDetailsDialog.errorString();
|
2022-07-25 16:57:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Systray::createShareDialog(const QString &localPath)
|
|
|
|
{
|
|
|
|
createFileDetailsDialog(localPath);
|
|
|
|
Q_EMIT showFileDetailsPage(localPath, FileDetailsPage::Sharing);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Systray::createFileActivityDialog(const QString &localPath)
|
|
|
|
{
|
|
|
|
createFileDetailsDialog(localPath);
|
|
|
|
Q_EMIT showFileDetailsPage(localPath, FileDetailsPage::Activity);
|
|
|
|
}
|
|
|
|
|
2022-11-07 13:05:20 +00:00
|
|
|
void Systray::presentShareViewInTray(const QString &localPath)
|
|
|
|
{
|
|
|
|
const auto folder = FolderMan::instance()->folderForPath(localPath);
|
|
|
|
if (!folder) {
|
|
|
|
qCWarning(lcSystray) << "Could not open file details view in tray for" << localPath << "no responsible folder found";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
qCDebug(lcSystray) << "Opening file details view in tray for " << localPath;
|
|
|
|
|
|
|
|
Q_EMIT showFileDetails(folder->accountState(), localPath, FileDetailsPage::Sharing);
|
|
|
|
}
|
|
|
|
|
2022-07-08 14:49:27 +00:00
|
|
|
void Systray::slotCurrentUserChanged()
|
2019-12-02 13:10:18 +00:00
|
|
|
{
|
2020-06-24 11:50:17 +00:00
|
|
|
if (_trayEngine) {
|
|
|
|
// Change ActivityModel
|
|
|
|
_trayEngine->rootContext()->setContextProperty("activityModel", UserModel::instance()->currentActivityModel());
|
|
|
|
}
|
2020-01-15 15:42:06 +00:00
|
|
|
|
|
|
|
// Rebuild App list
|
|
|
|
UserAppsModel::instance()->buildAppList();
|
2019-12-02 13:10:18 +00:00
|
|
|
}
|
|
|
|
|
2020-10-08 14:32:10 +00:00
|
|
|
void Systray::slotUnpauseAllFolders()
|
|
|
|
{
|
|
|
|
setPauseOnAllFoldersHelper(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Systray::slotPauseAllFolders()
|
|
|
|
{
|
|
|
|
setPauseOnAllFoldersHelper(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Systray::setPauseOnAllFoldersHelper(bool pause)
|
|
|
|
{
|
|
|
|
// For some reason we get the raw pointer from Folder::accountState()
|
|
|
|
// that's why we need a list of raw pointers for the call to contains
|
|
|
|
// later on...
|
|
|
|
const auto accounts = [=] {
|
|
|
|
const auto ptrList = AccountManager::instance()->accounts();
|
|
|
|
auto result = QList<AccountState *>();
|
|
|
|
result.reserve(ptrList.size());
|
|
|
|
std::transform(std::cbegin(ptrList), std::cend(ptrList), std::back_inserter(result), [](const AccountStatePtr &account) {
|
|
|
|
return account.data();
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
}();
|
|
|
|
const auto folders = FolderMan::instance()->map();
|
|
|
|
for (auto f : folders) {
|
|
|
|
if (accounts.contains(f->accountState())) {
|
|
|
|
f->setSyncPaused(pause);
|
|
|
|
if (pause) {
|
|
|
|
f->slotTerminateSync();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-21 11:33:22 +00:00
|
|
|
QString Systray::windowTitle() const
|
|
|
|
{
|
|
|
|
return Theme::instance()->appNameGUI();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Systray::useNormalWindow() const
|
|
|
|
{
|
|
|
|
if (!isSystemTrayAvailable()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ConfigFile cfg;
|
|
|
|
return cfg.showMainDialogAsNormalWindow();
|
|
|
|
}
|
|
|
|
|
2022-06-30 14:20:13 +00:00
|
|
|
bool Systray::isOpen() const
|
2020-01-05 15:25:32 +00:00
|
|
|
{
|
2022-06-30 14:20:13 +00:00
|
|
|
return _isOpen;
|
2020-01-05 15:25:32 +00:00
|
|
|
}
|
|
|
|
|
2023-06-06 10:33:34 +00:00
|
|
|
bool Systray::enableAddAccount() const
|
|
|
|
{
|
|
|
|
#if defined ENFORCE_SINGLE_ACCOUNT
|
|
|
|
return AccountManager::instance()->accounts().isEmpty();
|
|
|
|
#else
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-06-30 14:20:13 +00:00
|
|
|
void Systray::setIsOpen(const bool isOpen)
|
2020-01-05 15:25:32 +00:00
|
|
|
{
|
2022-06-30 14:20:13 +00:00
|
|
|
_isOpen = isOpen;
|
|
|
|
Q_EMIT isOpenChanged();
|
2020-01-05 15:25:32 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 23:38:57 +00:00
|
|
|
void Systray::showMessage(const QString &title, const QString &message, MessageIcon icon)
|
2013-03-20 09:03:49 +00:00
|
|
|
{
|
2013-03-22 10:34:21 +00:00
|
|
|
#ifdef USE_FDO_NOTIFICATIONS
|
2019-11-29 16:06:35 +00:00
|
|
|
if (QDBusInterface(NOTIFICATIONS_SERVICE, NOTIFICATIONS_PATH, NOTIFICATIONS_IFACE).isValid()) {
|
2020-04-14 19:37:49 +00:00
|
|
|
const QVariantMap hints = {{QStringLiteral("desktop-entry"), LINUX_APPLICATION_ID}};
|
2017-03-27 09:12:28 +00:00
|
|
|
QList<QVariant> args = QList<QVariant>() << APPLICATION_NAME << quint32(0) << APPLICATION_ICON_NAME
|
2020-03-20 23:20:51 +00:00
|
|
|
<< title << message << QStringList() << hints << qint32(-1);
|
2013-03-22 10:58:55 +00:00
|
|
|
QDBusMessage method = QDBusMessage::createMethodCall(NOTIFICATIONS_SERVICE, NOTIFICATIONS_PATH, NOTIFICATIONS_IFACE, "Notify");
|
|
|
|
method.setArguments(args);
|
|
|
|
QDBusConnection::sessionBus().asyncCall(method);
|
2013-05-22 15:39:49 +00:00
|
|
|
} else
|
2015-02-07 17:23:09 +00:00
|
|
|
#endif
|
2022-06-06 17:05:12 +00:00
|
|
|
#if defined(Q_OS_MACOS) && defined(BUILD_OWNCLOUD_OSX_BUNDLE)
|
2015-02-07 17:23:09 +00:00
|
|
|
if (canOsXSendUserNotification()) {
|
|
|
|
sendOsXUserNotification(title, message);
|
|
|
|
} else
|
2013-05-22 15:39:49 +00:00
|
|
|
#endif
|
|
|
|
{
|
2020-03-20 23:38:57 +00:00
|
|
|
QSystemTrayIcon::showMessage(title, message, icon);
|
2013-03-22 10:58:55 +00:00
|
|
|
}
|
2013-03-22 10:34:21 +00:00
|
|
|
}
|
2013-10-01 11:51:40 +00:00
|
|
|
|
2022-05-10 14:12:15 +00:00
|
|
|
void Systray::showUpdateMessage(const QString &title, const QString &message, const QUrl &webUrl)
|
|
|
|
{
|
2022-06-06 17:05:12 +00:00
|
|
|
#if defined(Q_OS_MACOS) && defined(BUILD_OWNCLOUD_OSX_BUNDLE)
|
2022-05-10 14:12:15 +00:00
|
|
|
sendOsXUpdateNotification(title, message, webUrl);
|
|
|
|
#else // TODO: Implement custom notifications (i.e. actionable) for other OSes
|
|
|
|
Q_UNUSED(webUrl);
|
|
|
|
showMessage(title, message);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-11-08 18:18:41 +00:00
|
|
|
void Systray::showTalkMessage(const QString &title, const QString &message, const QString &token, const QString &replyTo, const AccountStatePtr &accountState)
|
|
|
|
{
|
|
|
|
#if defined(Q_OS_MACOS) && defined(BUILD_OWNCLOUD_OSX_BUNDLE)
|
|
|
|
sendOsXTalkNotification(title, message, token, replyTo, accountState);
|
|
|
|
#else // TODO: Implement custom notifications (i.e. actionable) for other OSes
|
2022-12-09 13:36:05 +00:00
|
|
|
Q_UNUSED(replyTo)
|
|
|
|
Q_UNUSED(token)
|
|
|
|
Q_UNUSED(accountState)
|
2022-11-08 18:18:41 +00:00
|
|
|
showMessage(title, message);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-11-22 23:05:50 +00:00
|
|
|
void Systray::setToolTip(const QString &tip)
|
|
|
|
{
|
|
|
|
QSystemTrayIcon::setToolTip(tr("%1: %2").arg(Theme::instance()->appNameGUI(), tip));
|
|
|
|
}
|
|
|
|
|
2022-06-30 14:20:13 +00:00
|
|
|
bool Systray::syncIsPaused() const
|
2019-12-30 13:14:28 +00:00
|
|
|
{
|
2020-04-24 16:34:11 +00:00
|
|
|
return _syncIsPaused;
|
|
|
|
}
|
2020-01-21 12:54:04 +00:00
|
|
|
|
2022-06-30 14:20:13 +00:00
|
|
|
void Systray::setSyncIsPaused(const bool syncIsPaused)
|
2020-04-24 16:34:11 +00:00
|
|
|
{
|
2022-06-30 14:20:13 +00:00
|
|
|
_syncIsPaused = syncIsPaused;
|
2020-04-24 16:34:11 +00:00
|
|
|
if (_syncIsPaused) {
|
2020-10-08 14:32:10 +00:00
|
|
|
slotPauseAllFolders();
|
2022-06-30 14:20:13 +00:00
|
|
|
} else {
|
|
|
|
slotUnpauseAllFolders();
|
2020-01-21 08:55:26 +00:00
|
|
|
}
|
2020-04-24 16:34:11 +00:00
|
|
|
}
|
2020-01-21 08:55:26 +00:00
|
|
|
|
2020-04-24 16:34:11 +00:00
|
|
|
/********************************************************************************************/
|
|
|
|
/* Helper functions for cross-platform tray icon position and taskbar orientation detection */
|
|
|
|
/********************************************************************************************/
|
2020-01-13 21:11:57 +00:00
|
|
|
|
2022-06-29 17:57:52 +00:00
|
|
|
void Systray::positionWindowAtTray(QQuickWindow *window) const
|
2020-06-23 09:20:30 +00:00
|
|
|
{
|
2021-07-13 08:11:32 +00:00
|
|
|
if (!useNormalWindow()) {
|
|
|
|
window->setScreen(currentScreen());
|
2021-06-21 11:33:22 +00:00
|
|
|
const auto position = computeWindowPosition(window->width(), window->height());
|
|
|
|
window->setPosition(position);
|
|
|
|
}
|
2020-06-23 09:20:30 +00:00
|
|
|
}
|
|
|
|
|
2022-06-29 17:57:52 +00:00
|
|
|
void Systray::positionWindowAtScreenCenter(QQuickWindow *window) const
|
|
|
|
{
|
|
|
|
if(!useNormalWindow()) {
|
|
|
|
window->setScreen(currentScreen());
|
|
|
|
const QPoint windowAdjustment(window->geometry().width() / 2, window->geometry().height() / 2);
|
|
|
|
const auto position = currentScreen()->virtualGeometry().center() - windowAdjustment;
|
|
|
|
window->setPosition(position);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-22 10:39:40 +00:00
|
|
|
void Systray::forceWindowInit(QQuickWindow *window) const
|
|
|
|
{
|
|
|
|
// HACK: At least on Windows, if the systray window is not shown at least once
|
|
|
|
// it can prevent session handling to carry on properly, so we show/hide it here
|
|
|
|
// this shouldn't flicker
|
|
|
|
window->show();
|
|
|
|
window->hide();
|
2020-11-30 18:24:41 +00:00
|
|
|
|
|
|
|
#ifdef Q_OS_MAC
|
|
|
|
// On macOS we need to designate the tray window as visible on all spaces and
|
|
|
|
// at the menu bar level, otherwise showing it can cause the current spaces to
|
|
|
|
// change, or the window could be obscured by another window that shouldn't
|
|
|
|
// normally cover a menu.
|
|
|
|
OCC::setTrayWindowLevelAndVisibleOnAllSpaces(window);
|
|
|
|
#endif
|
2020-10-22 10:39:40 +00:00
|
|
|
}
|
|
|
|
|
2022-04-21 22:32:01 +00:00
|
|
|
void Systray::positionNotificationWindow(QQuickWindow *window) const
|
|
|
|
{
|
|
|
|
if (!useNormalWindow()) {
|
|
|
|
window->setScreen(currentScreen());
|
|
|
|
if(geometry().isValid()) {
|
|
|
|
// On OSes where the QSystemTrayIcon geometry method isn't borked, we can actually figure out where the system tray is located
|
|
|
|
// We can therefore use our normal routines
|
|
|
|
const auto position = computeNotificationPosition(window->width(), window->height());
|
|
|
|
window->setPosition(position);
|
|
|
|
} else if (QProcessEnvironment::systemEnvironment().contains(QStringLiteral("XDG_CURRENT_DESKTOP")) &&
|
|
|
|
(QProcessEnvironment::systemEnvironment().value(QStringLiteral("XDG_CURRENT_DESKTOP")).contains(QStringLiteral("GNOME")))) {
|
|
|
|
// We can safely hardcode the top-right position for the notification when running GNOME
|
|
|
|
const auto position = computeNotificationPosition(window->width(), window->height(), 0, NotificationPosition::TopRight);
|
|
|
|
window->setPosition(position);
|
|
|
|
} else {
|
|
|
|
// For other DEs we play it safe and place the notification in the centre of the screen
|
2022-06-29 17:57:52 +00:00
|
|
|
positionWindowAtScreenCenter(window);
|
2022-04-21 22:32:01 +00:00
|
|
|
}
|
|
|
|
// TODO: Get actual notification positions for the DEs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-20 17:36:57 +00:00
|
|
|
QScreen *Systray::currentScreen() const
|
|
|
|
{
|
2022-02-14 11:12:45 +00:00
|
|
|
const auto screen = QGuiApplication::screenAt(QCursor::pos());
|
2020-05-20 17:36:57 +00:00
|
|
|
|
2022-02-14 11:12:45 +00:00
|
|
|
if(screen) {
|
|
|
|
return screen;
|
2020-05-20 17:36:57 +00:00
|
|
|
}
|
2020-09-14 16:22:56 +00:00
|
|
|
// Didn't find anything matching the cursor position,
|
|
|
|
// falling back to the primary screen
|
|
|
|
return QGuiApplication::primaryScreen();
|
2020-05-20 17:36:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Systray::TaskBarPosition Systray::taskbarOrientation() const
|
2020-04-24 16:34:11 +00:00
|
|
|
{
|
|
|
|
// macOS: Always on top
|
|
|
|
#if defined(Q_OS_MACOS)
|
|
|
|
return TaskBarPosition::Top;
|
|
|
|
// Windows: Check registry for actual taskbar orientation
|
|
|
|
#elif defined(Q_OS_WIN)
|
2020-10-26 19:51:12 +00:00
|
|
|
auto taskbarPositionSubkey = QStringLiteral("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects3");
|
|
|
|
if (!Utility::registryKeyExists(HKEY_CURRENT_USER, taskbarPositionSubkey)) {
|
|
|
|
// Windows 7
|
|
|
|
taskbarPositionSubkey = QStringLiteral("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects2");
|
|
|
|
}
|
|
|
|
if (!Utility::registryKeyExists(HKEY_CURRENT_USER, taskbarPositionSubkey)) {
|
|
|
|
return TaskBarPosition::Bottom;
|
|
|
|
}
|
|
|
|
auto taskbarPosition = Utility::registryGetKeyValue(HKEY_CURRENT_USER, taskbarPositionSubkey, "Settings");
|
2020-04-24 16:34:11 +00:00
|
|
|
switch (taskbarPosition.toInt()) {
|
|
|
|
// Mapping windows binary value (0 = left, 1 = top, 2 = right, 3 = bottom) to qml logic (0 = bottom, 1 = left...)
|
|
|
|
case 0:
|
|
|
|
return TaskBarPosition::Left;
|
|
|
|
case 1:
|
|
|
|
return TaskBarPosition::Top;
|
|
|
|
case 2:
|
|
|
|
return TaskBarPosition::Right;
|
|
|
|
case 3:
|
|
|
|
return TaskBarPosition::Bottom;
|
|
|
|
default:
|
|
|
|
return TaskBarPosition::Bottom;
|
2019-12-30 13:14:28 +00:00
|
|
|
}
|
2020-05-20 08:12:55 +00:00
|
|
|
// Probably Linux
|
2020-04-24 16:34:11 +00:00
|
|
|
#else
|
2020-05-20 18:03:08 +00:00
|
|
|
const auto screenRect = currentScreenRect();
|
|
|
|
const auto trayIconCenter = calcTrayIconCenter();
|
2020-05-20 08:12:55 +00:00
|
|
|
|
2020-05-25 17:02:10 +00:00
|
|
|
const auto distBottom = screenRect.bottom() - trayIconCenter.y();
|
|
|
|
const auto distRight = screenRect.right() - trayIconCenter.x();
|
|
|
|
const auto distLeft = trayIconCenter.x() - screenRect.left();
|
|
|
|
const auto distTop = trayIconCenter.y() - screenRect.top();
|
2020-05-20 08:12:55 +00:00
|
|
|
|
2020-05-25 17:02:10 +00:00
|
|
|
const auto minDist = std::min({distRight, distTop, distBottom});
|
|
|
|
|
|
|
|
if (minDist == distBottom) {
|
2020-05-20 08:12:55 +00:00
|
|
|
return TaskBarPosition::Bottom;
|
2020-05-25 17:02:10 +00:00
|
|
|
} else if (minDist == distLeft) {
|
2020-05-20 08:12:55 +00:00
|
|
|
return TaskBarPosition::Left;
|
2020-05-25 17:02:10 +00:00
|
|
|
} else if (minDist == distTop) {
|
2020-05-20 08:12:55 +00:00
|
|
|
return TaskBarPosition::Top;
|
|
|
|
} else {
|
|
|
|
return TaskBarPosition::Right;
|
|
|
|
}
|
2020-01-04 11:15:01 +00:00
|
|
|
#endif
|
2019-12-30 13:14:28 +00:00
|
|
|
}
|
2020-01-05 20:06:42 +00:00
|
|
|
|
2020-05-20 08:12:55 +00:00
|
|
|
// TODO: Get real taskbar dimensions Linux as well
|
2020-05-20 17:36:57 +00:00
|
|
|
QRect Systray::taskbarGeometry() const
|
2020-01-05 20:06:42 +00:00
|
|
|
{
|
2020-04-24 16:34:11 +00:00
|
|
|
#if defined(Q_OS_WIN)
|
2020-05-20 08:12:55 +00:00
|
|
|
QRect tbRect = Utility::getTaskbarDimensions();
|
|
|
|
//QML side expects effective pixels, convert taskbar dimensions if necessary
|
2020-05-20 18:03:08 +00:00
|
|
|
auto pixelRatio = currentScreen()->devicePixelRatio();
|
2020-05-20 08:12:55 +00:00
|
|
|
if (pixelRatio != 1) {
|
|
|
|
tbRect.setHeight(tbRect.height() / pixelRatio);
|
|
|
|
tbRect.setWidth(tbRect.width() / pixelRatio);
|
|
|
|
}
|
|
|
|
return tbRect;
|
|
|
|
#elif defined(Q_OS_MACOS)
|
2022-02-03 15:37:17 +00:00
|
|
|
const auto screenWidth = currentScreenRect().width();
|
2022-09-09 12:23:39 +00:00
|
|
|
const auto statusBarHeight = static_cast<int>(OCC::menuBarThickness());
|
2022-02-03 15:37:17 +00:00
|
|
|
return {0, 0, screenWidth, statusBarHeight};
|
2020-04-24 16:34:11 +00:00
|
|
|
#else
|
2020-05-20 08:12:55 +00:00
|
|
|
if (taskbarOrientation() == TaskBarPosition::Bottom || taskbarOrientation() == TaskBarPosition::Top) {
|
2020-05-20 18:03:08 +00:00
|
|
|
auto screenWidth = currentScreenRect().width();
|
2020-07-07 07:43:22 +00:00
|
|
|
return {0, 0, screenWidth, 32};
|
2020-05-20 08:12:55 +00:00
|
|
|
} else {
|
2020-05-20 18:03:08 +00:00
|
|
|
auto screenHeight = currentScreenRect().height();
|
2020-07-07 07:43:22 +00:00
|
|
|
return {0, 0, 32, screenHeight};
|
2020-05-20 08:12:55 +00:00
|
|
|
}
|
2020-04-24 16:34:11 +00:00
|
|
|
#endif
|
2020-01-05 20:06:42 +00:00
|
|
|
}
|
|
|
|
|
2020-05-20 17:36:57 +00:00
|
|
|
QRect Systray::currentScreenRect() const
|
|
|
|
{
|
|
|
|
const auto screen = currentScreen();
|
2020-07-15 09:23:11 +00:00
|
|
|
Q_ASSERT(screen);
|
|
|
|
return screen->geometry();
|
2020-05-20 17:36:57 +00:00
|
|
|
}
|
|
|
|
|
2020-05-20 19:36:08 +00:00
|
|
|
QPoint Systray::computeWindowReferencePoint() const
|
2020-05-20 17:36:57 +00:00
|
|
|
{
|
2020-05-20 19:36:08 +00:00
|
|
|
constexpr auto spacing = 4;
|
2020-05-20 17:36:57 +00:00
|
|
|
const auto trayIconCenter = calcTrayIconCenter();
|
|
|
|
const auto taskbarRect = taskbarGeometry();
|
|
|
|
const auto taskbarScreenEdge = taskbarOrientation();
|
|
|
|
const auto screenRect = currentScreenRect();
|
|
|
|
|
2020-06-11 17:27:40 +00:00
|
|
|
qCDebug(lcSystray) << "screenRect:" << screenRect;
|
|
|
|
qCDebug(lcSystray) << "taskbarRect:" << taskbarRect;
|
|
|
|
qCDebug(lcSystray) << "taskbarScreenEdge:" << taskbarScreenEdge;
|
|
|
|
qCDebug(lcSystray) << "trayIconCenter:" << trayIconCenter;
|
|
|
|
|
2020-05-20 17:36:57 +00:00
|
|
|
switch(taskbarScreenEdge) {
|
|
|
|
case TaskBarPosition::Bottom:
|
|
|
|
return {
|
2020-05-20 19:36:08 +00:00
|
|
|
trayIconCenter.x(),
|
|
|
|
screenRect.bottom() - taskbarRect.height() - spacing
|
2020-05-20 17:36:57 +00:00
|
|
|
};
|
|
|
|
case TaskBarPosition::Left:
|
|
|
|
return {
|
2020-05-20 19:36:08 +00:00
|
|
|
screenRect.left() + taskbarRect.width() + spacing,
|
2020-05-20 17:36:57 +00:00
|
|
|
trayIconCenter.y()
|
|
|
|
};
|
|
|
|
case TaskBarPosition::Top:
|
|
|
|
return {
|
2020-05-20 19:36:08 +00:00
|
|
|
trayIconCenter.x(),
|
|
|
|
screenRect.top() + taskbarRect.height() + spacing
|
2020-05-20 17:36:57 +00:00
|
|
|
};
|
|
|
|
case TaskBarPosition::Right:
|
|
|
|
return {
|
2020-05-20 19:36:08 +00:00
|
|
|
screenRect.right() - taskbarRect.width() - spacing,
|
2020-05-20 17:36:57 +00:00
|
|
|
trayIconCenter.y()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
2022-04-21 22:32:01 +00:00
|
|
|
QPoint Systray::computeNotificationReferencePoint(int spacing, NotificationPosition position) const
|
|
|
|
{
|
|
|
|
auto trayIconCenter = calcTrayIconCenter();
|
|
|
|
auto taskbarScreenEdge = taskbarOrientation();
|
|
|
|
auto taskbarRect = taskbarGeometry();
|
|
|
|
const auto screenRect = currentScreenRect();
|
|
|
|
|
|
|
|
if(position == NotificationPosition::TopLeft) {
|
|
|
|
taskbarScreenEdge = TaskBarPosition::Top;
|
|
|
|
trayIconCenter = QPoint(0, 0);
|
|
|
|
taskbarRect = QRect(0, 0, screenRect.width(), 32);
|
|
|
|
} else if(position == NotificationPosition::TopRight) {
|
|
|
|
taskbarScreenEdge = TaskBarPosition::Top;
|
|
|
|
trayIconCenter = QPoint(screenRect.width(), 0);
|
|
|
|
taskbarRect = QRect(0, 0, screenRect.width(), 32);
|
|
|
|
} else if(position == NotificationPosition::BottomLeft) {
|
|
|
|
taskbarScreenEdge = TaskBarPosition::Bottom;
|
|
|
|
trayIconCenter = QPoint(0, screenRect.height());
|
|
|
|
taskbarRect = QRect(0, 0, screenRect.width(), 32);
|
|
|
|
} else if(position == NotificationPosition::BottomRight) {
|
|
|
|
taskbarScreenEdge = TaskBarPosition::Bottom;
|
|
|
|
trayIconCenter = QPoint(screenRect.width(), screenRect.height());
|
|
|
|
taskbarRect = QRect(0, 0, screenRect.width(), 32);
|
|
|
|
}
|
|
|
|
|
|
|
|
qCDebug(lcSystray) << "screenRect:" << screenRect;
|
|
|
|
qCDebug(lcSystray) << "taskbarRect:" << taskbarRect;
|
|
|
|
qCDebug(lcSystray) << "taskbarScreenEdge:" << taskbarScreenEdge;
|
|
|
|
qCDebug(lcSystray) << "trayIconCenter:" << trayIconCenter;
|
|
|
|
|
|
|
|
switch(taskbarScreenEdge) {
|
|
|
|
case TaskBarPosition::Bottom:
|
|
|
|
return {
|
|
|
|
trayIconCenter.x() < screenRect.center().x() ? screenRect.left() + spacing : screenRect.right() - spacing,
|
|
|
|
screenRect.bottom() - taskbarRect.height() - spacing
|
|
|
|
};
|
|
|
|
case TaskBarPosition::Left:
|
|
|
|
return {
|
|
|
|
screenRect.left() + taskbarRect.width() + spacing,
|
|
|
|
trayIconCenter.y() < screenRect.center().y() ? screenRect.top() + spacing : screenRect.bottom() - spacing
|
|
|
|
};
|
|
|
|
case TaskBarPosition::Top:
|
|
|
|
return {
|
|
|
|
trayIconCenter.x() < screenRect.center().x() ? screenRect.left() + spacing : screenRect.right() - spacing,
|
|
|
|
screenRect.top() + taskbarRect.height() + spacing
|
|
|
|
};
|
|
|
|
case TaskBarPosition::Right:
|
|
|
|
return {
|
|
|
|
screenRect.right() - taskbarRect.width() - spacing,
|
|
|
|
trayIconCenter.y() < screenRect.center().y() ? screenRect.top() + spacing : screenRect.bottom() - spacing
|
|
|
|
};
|
|
|
|
}
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect Systray::computeWindowRect(int spacing, const QPoint &topLeft, const QPoint &bottomRight) const
|
|
|
|
{
|
|
|
|
const auto screenRect = currentScreenRect();
|
|
|
|
const auto rect = QRect(topLeft, bottomRight);
|
|
|
|
auto offset = QPoint();
|
|
|
|
|
|
|
|
if (rect.left() < screenRect.left()) {
|
|
|
|
offset.setX(screenRect.left() - rect.left() + spacing);
|
|
|
|
} else if (rect.right() > screenRect.right()) {
|
|
|
|
offset.setX(screenRect.right() - rect.right() - spacing);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rect.top() < screenRect.top()) {
|
|
|
|
offset.setY(screenRect.top() - rect.top() + spacing);
|
|
|
|
} else if (rect.bottom() > screenRect.bottom()) {
|
|
|
|
offset.setY(screenRect.bottom() - rect.bottom() - spacing);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rect.translated(offset);
|
|
|
|
}
|
|
|
|
|
2020-05-20 17:36:57 +00:00
|
|
|
QPoint Systray::computeWindowPosition(int width, int height) const
|
|
|
|
{
|
2022-04-21 22:32:01 +00:00
|
|
|
constexpr auto spacing = 4;
|
2020-05-20 19:36:08 +00:00
|
|
|
const auto referencePoint = computeWindowReferencePoint();
|
2020-05-20 17:36:57 +00:00
|
|
|
|
|
|
|
const auto taskbarScreenEdge = taskbarOrientation();
|
|
|
|
const auto screenRect = currentScreenRect();
|
|
|
|
|
2020-05-20 19:36:08 +00:00
|
|
|
const auto topLeft = [=]() {
|
|
|
|
switch(taskbarScreenEdge) {
|
|
|
|
case TaskBarPosition::Bottom:
|
|
|
|
return referencePoint - QPoint(width / 2, height);
|
|
|
|
case TaskBarPosition::Left:
|
|
|
|
return referencePoint;
|
|
|
|
case TaskBarPosition::Top:
|
|
|
|
return referencePoint - QPoint(width / 2, 0);
|
|
|
|
case TaskBarPosition::Right:
|
|
|
|
return referencePoint - QPoint(width, 0);
|
|
|
|
}
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
}();
|
|
|
|
const auto bottomRight = topLeft + QPoint(width, height);
|
2022-04-21 22:32:01 +00:00
|
|
|
const auto windowRect = computeWindowRect(spacing, topLeft, bottomRight);
|
2020-05-20 17:36:57 +00:00
|
|
|
|
2022-04-21 22:32:01 +00:00
|
|
|
qCDebug(lcSystray) << "taskbarScreenEdge:" << taskbarScreenEdge;
|
|
|
|
qCDebug(lcSystray) << "screenRect:" << screenRect;
|
|
|
|
qCDebug(lcSystray) << "windowRect (reference)" << QRect(topLeft, bottomRight);
|
|
|
|
qCDebug(lcSystray) << "windowRect (adjusted)" << windowRect;
|
|
|
|
|
|
|
|
return windowRect.topLeft();
|
|
|
|
}
|
|
|
|
|
|
|
|
QPoint Systray::computeNotificationPosition(int width, int height, int spacing, NotificationPosition position) const
|
|
|
|
{
|
|
|
|
const auto referencePoint = computeNotificationReferencePoint(spacing, position);
|
2020-05-20 17:36:57 +00:00
|
|
|
|
2022-04-21 22:32:01 +00:00
|
|
|
auto trayIconCenter = calcTrayIconCenter();
|
|
|
|
auto taskbarScreenEdge = taskbarOrientation();
|
|
|
|
const auto screenRect = currentScreenRect();
|
|
|
|
|
|
|
|
if(position == NotificationPosition::TopLeft) {
|
|
|
|
taskbarScreenEdge = TaskBarPosition::Top;
|
|
|
|
trayIconCenter = QPoint(0, 0);
|
|
|
|
} else if(position == NotificationPosition::TopRight) {
|
|
|
|
taskbarScreenEdge = TaskBarPosition::Top;
|
|
|
|
trayIconCenter = QPoint(screenRect.width(), 0);
|
|
|
|
} else if(position == NotificationPosition::BottomLeft) {
|
|
|
|
taskbarScreenEdge = TaskBarPosition::Bottom;
|
|
|
|
trayIconCenter = QPoint(0, screenRect.height());
|
|
|
|
} else if(position == NotificationPosition::BottomRight) {
|
|
|
|
taskbarScreenEdge = TaskBarPosition::Bottom;
|
|
|
|
trayIconCenter = QPoint(screenRect.width(), screenRect.height());
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto topLeft = [=]() {
|
|
|
|
switch(taskbarScreenEdge) {
|
|
|
|
case TaskBarPosition::Bottom:
|
|
|
|
return trayIconCenter.x() < screenRect.center().x() ? referencePoint - QPoint(0, height) : referencePoint - QPoint(width, height);
|
|
|
|
case TaskBarPosition::Left:
|
|
|
|
return trayIconCenter.y() < screenRect.center().y() ? referencePoint : referencePoint - QPoint(0, height);
|
|
|
|
case TaskBarPosition::Top:
|
|
|
|
return trayIconCenter.x() < screenRect.center().x() ? referencePoint : referencePoint - QPoint(width, 0);
|
|
|
|
case TaskBarPosition::Right:
|
|
|
|
return trayIconCenter.y() < screenRect.center().y() ? referencePoint - QPoint(width, 0) : QPoint(width, height);
|
|
|
|
}
|
|
|
|
Q_UNREACHABLE();
|
2020-05-20 19:36:08 +00:00
|
|
|
}();
|
2022-04-21 22:32:01 +00:00
|
|
|
const auto bottomRight = topLeft + QPoint(width, height);
|
|
|
|
const auto windowRect = computeWindowRect(spacing, topLeft, bottomRight);
|
2020-06-11 17:27:40 +00:00
|
|
|
|
|
|
|
qCDebug(lcSystray) << "taskbarScreenEdge:" << taskbarScreenEdge;
|
|
|
|
qCDebug(lcSystray) << "screenRect:" << screenRect;
|
|
|
|
qCDebug(lcSystray) << "windowRect (reference)" << QRect(topLeft, bottomRight);
|
2020-10-26 19:51:12 +00:00
|
|
|
qCDebug(lcSystray) << "windowRect (adjusted)" << windowRect;
|
2022-04-21 22:32:01 +00:00
|
|
|
qCDebug(lcSystray) << "referencePoint" << referencePoint;
|
2020-06-11 17:27:40 +00:00
|
|
|
|
2020-05-20 19:36:08 +00:00
|
|
|
return windowRect.topLeft();
|
2020-05-20 17:36:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QPoint Systray::calcTrayIconCenter() const
|
2020-01-05 20:06:42 +00:00
|
|
|
{
|
2022-04-21 22:32:01 +00:00
|
|
|
if(geometry().isValid()) {
|
|
|
|
// QSystemTrayIcon::geometry() is broken for ages on most Linux DEs (invalid geometry returned)
|
|
|
|
// thus we can use this only for Windows and macOS
|
|
|
|
auto trayIconCenter = geometry().center();
|
|
|
|
return trayIconCenter;
|
|
|
|
}
|
|
|
|
|
2020-05-20 17:36:57 +00:00
|
|
|
// On Linux, fall back to mouse position (assuming tray icon is activated by mouse click)
|
2020-07-15 09:23:11 +00:00
|
|
|
return QCursor::pos(currentScreen());
|
2020-01-05 20:06:42 +00:00
|
|
|
}
|
|
|
|
|
2021-11-17 09:36:05 +00:00
|
|
|
AccessManagerFactory::AccessManagerFactory()
|
|
|
|
: QQmlNetworkAccessManagerFactory()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QNetworkAccessManager* AccessManagerFactory::create(QObject *parent)
|
|
|
|
{
|
2022-01-20 11:54:36 +00:00
|
|
|
const auto am = new AccessManager(parent);
|
|
|
|
const auto diskCache = new QNetworkDiskCache(am);
|
2022-05-05 11:46:39 +00:00
|
|
|
diskCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
2022-01-20 11:54:36 +00:00
|
|
|
am->setCache(diskCache);
|
|
|
|
return am;
|
2021-11-17 09:36:05 +00:00
|
|
|
}
|
|
|
|
|
2014-11-09 21:34:07 +00:00
|
|
|
} // namespace OCC
|