mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 13:47:05 +02:00
Get rid of custom SNI implementation
XDG is inventing new tray specification, so SNI will be outdated soon and it's better to just use QSystemTrayIcon. I believe all the major drawbacks of QSystemTrayIcon are solved and we can live with minor ones. Given the planned MainWindow refactoring, it seems it's the best time to do that.
This commit is contained in:
parent
aed49b9289
commit
b65d40a22b
9 changed files with 33 additions and 1139 deletions
|
@ -1334,7 +1334,6 @@ else()
|
|||
if (NOT DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
desktop-app::external_statusnotifieritem
|
||||
desktop-app::external_dbusmenu_qt
|
||||
desktop-app::external_glibmm
|
||||
desktop-app::external_glib
|
||||
|
|
|
@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "window/window_session_controller.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "base/unique_qptr.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/ui_utility.h"
|
||||
|
@ -41,7 +42,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
|
||||
#include <QtCore/QSize>
|
||||
#include <QtCore/QTemporaryFile>
|
||||
#include <QtCore/QMimeData>
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
|
@ -49,9 +49,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include <QtDBus/QDBusConnection>
|
||||
#include <QtDBus/QDBusMessage>
|
||||
#include <QtDBus/QDBusObjectPath>
|
||||
#include <QtDBus/QDBusMetaType>
|
||||
|
||||
#include <statusnotifieritem.h>
|
||||
#include <dbusmenuexporter.h>
|
||||
|
||||
#include <glibmm.h>
|
||||
|
@ -69,13 +67,6 @@ constexpr auto kMutePanelTrayIconName = "telegram-mute-panel"_cs;
|
|||
constexpr auto kAttentionPanelTrayIconName = "telegram-attention-panel"_cs;
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs;
|
||||
constexpr auto kTrayIconFilename = "tdesktop-trayicon-XXXXXX.png"_cs;
|
||||
|
||||
constexpr auto kSNIWatcherService = "org.kde.StatusNotifierWatcher"_cs;
|
||||
constexpr auto kSNIWatcherObjectPath = "/StatusNotifierWatcher"_cs;
|
||||
constexpr auto kSNIWatcherInterface = kSNIWatcherService;
|
||||
|
||||
constexpr auto kAppMenuService = "com.canonical.AppMenu.Registrar"_cs;
|
||||
constexpr auto kAppMenuObjectPath = "/com/canonical/AppMenu/Registrar"_cs;
|
||||
constexpr auto kAppMenuInterface = kAppMenuService;
|
||||
|
@ -390,88 +381,6 @@ void ForceDisabled(QAction *action, bool disabled) {
|
|||
}
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
bool IsIndicatorApplication() {
|
||||
// Hack for indicator-application,
|
||||
// which doesn't handle icons sent across D-Bus:
|
||||
// save the icon to a temp file
|
||||
// and set the icon name to that filename.
|
||||
static const auto Result = [] {
|
||||
try {
|
||||
const auto connection = Gio::DBus::Connection::get_sync(
|
||||
Gio::DBus::BusType::BUS_TYPE_SESSION);
|
||||
|
||||
const auto ubuntuIndicator = base::Platform::DBus::NameHasOwner(
|
||||
connection,
|
||||
"com.canonical.indicator.application");
|
||||
|
||||
const auto ayatanaIndicator = base::Platform::DBus::NameHasOwner(
|
||||
connection,
|
||||
"org.ayatana.indicator.application");
|
||||
|
||||
return ubuntuIndicator || ayatanaIndicator;
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
std::unique_ptr<QTemporaryFile> TrayIconFile(
|
||||
const QIcon &icon,
|
||||
QObject *parent = nullptr) {
|
||||
static const auto templateName = AppRuntimeDirectory()
|
||||
+ kTrayIconFilename.utf16();
|
||||
|
||||
static const auto dprSize = [](const QPixmap &pixmap) {
|
||||
return pixmap.size() / pixmap.devicePixelRatio();
|
||||
};
|
||||
|
||||
static const auto desiredSize = QSize(22, 22);
|
||||
|
||||
static const auto scalePixmap = [=](const QPixmap &pixmap) {
|
||||
if (dprSize(pixmap) != desiredSize) {
|
||||
return pixmap.scaled(
|
||||
desiredSize * pixmap.devicePixelRatio(),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
} else {
|
||||
return pixmap;
|
||||
}
|
||||
};
|
||||
|
||||
auto ret = std::make_unique<QTemporaryFile>(
|
||||
templateName,
|
||||
parent);
|
||||
|
||||
ret->open();
|
||||
|
||||
const auto firstAttempt = icon.pixmap(desiredSize);
|
||||
const auto firstAttemptSize = dprSize(firstAttempt);
|
||||
|
||||
if (firstAttemptSize.width() < desiredSize.width()) {
|
||||
const auto availableSizes = icon.availableSizes();
|
||||
|
||||
const auto biggestSize = ranges::max_element(
|
||||
availableSizes,
|
||||
std::less<>(),
|
||||
&QSize::width);
|
||||
|
||||
if (biggestSize->width() > firstAttemptSize.width()) {
|
||||
scalePixmap(icon.pixmap(*biggestSize)).save(ret.get());
|
||||
} else {
|
||||
scalePixmap(firstAttempt).save(ret.get());
|
||||
}
|
||||
} else {
|
||||
scalePixmap(firstAttempt).save(ret.get());
|
||||
}
|
||||
|
||||
ret->close();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool UseUnityCounter() {
|
||||
static const auto Result = [&] {
|
||||
try {
|
||||
|
@ -490,54 +399,6 @@ bool UseUnityCounter() {
|
|||
return Result;
|
||||
}
|
||||
|
||||
bool IsSNIAvailable() {
|
||||
try {
|
||||
const auto connection = [] {
|
||||
try {
|
||||
return Gio::DBus::Connection::get_sync(
|
||||
Gio::DBus::BusType::BUS_TYPE_SESSION);
|
||||
} catch (...) {
|
||||
return Glib::RefPtr<Gio::DBus::Connection>();
|
||||
}
|
||||
}();
|
||||
|
||||
if (!connection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto reply = connection->call_sync(
|
||||
std::string(kSNIWatcherObjectPath),
|
||||
std::string(kPropertiesInterface),
|
||||
"Get",
|
||||
base::Platform::MakeGlibVariant(std::tuple{
|
||||
Glib::ustring(std::string(kSNIWatcherInterface)),
|
||||
Glib::ustring("IsStatusNotifierHostRegistered"),
|
||||
}),
|
||||
std::string(kSNIWatcherService));
|
||||
|
||||
return base::Platform::GlibVariantCast<bool>(
|
||||
base::Platform::GlibVariantCast<Glib::VariantBase>(
|
||||
reply.get_child(0)));
|
||||
} catch (const Glib::Error &e) {
|
||||
static const auto NotSupportedErrors = {
|
||||
"org.freedesktop.DBus.Error.ServiceUnknown",
|
||||
};
|
||||
|
||||
const auto errorName = Gio::DBus::ErrorUtils::get_remote_error(e);
|
||||
if (ranges::contains(NotSupportedErrors, errorName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(("SNI Error: %1")
|
||||
.arg(QString::fromStdString(e.what())));
|
||||
} catch (const std::exception &e) {
|
||||
LOG(("SNI Error: %1")
|
||||
.arg(QString::fromStdString(e.what())));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint djbStringHash(const std::string &string) {
|
||||
uint hash = 5381;
|
||||
for (const auto &curChar : string) {
|
||||
|
@ -608,162 +469,50 @@ void UnregisterAppMenu(QWindow *window) {
|
|||
|
||||
} // namespace
|
||||
|
||||
class MainWindow::Private {
|
||||
class MainWindow::Private : public QObject {
|
||||
public:
|
||||
explicit Private(not_null<MainWindow*> window)
|
||||
: _public(window) {
|
||||
QCoreApplication::instance()->installEventFilter(this);
|
||||
}
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> trayIconMenuXEmbed;
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
Glib::RefPtr<Gio::DBus::Connection> dbusConnection;
|
||||
|
||||
StatusNotifierItem *sniTrayIcon = nullptr;
|
||||
uint sniRegisteredSignalId = 0;
|
||||
uint sniWatcherId = 0;
|
||||
std::unique_ptr<QTemporaryFile> trayIconFile;
|
||||
|
||||
bool appMenuSupported = false;
|
||||
uint appMenuWatcherId = 0;
|
||||
DBusMenuExporter *mainMenuExporter = nullptr;
|
||||
|
||||
void setSNITrayIcon(int counter, bool muted);
|
||||
void attachToSNITrayIcon();
|
||||
void handleSNIHostRegistered();
|
||||
|
||||
void handleSNIOwnerChanged(
|
||||
const QString &service,
|
||||
const QString &oldOwner,
|
||||
const QString &newOwner);
|
||||
|
||||
void handleAppMenuOwnerChanged(
|
||||
const QString &service,
|
||||
const QString &oldOwner,
|
||||
const QString &newOwner);
|
||||
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *e) override;
|
||||
|
||||
private:
|
||||
not_null<MainWindow*> _public;
|
||||
};
|
||||
|
||||
bool MainWindow::Private::eventFilter(QObject *obj, QEvent *e) {
|
||||
if (obj->objectName() == qstr("QSystemTrayIconSys")
|
||||
&& e->type() == QEvent::MouseButtonPress) {
|
||||
const auto ee = static_cast<QMouseEvent*>(e);
|
||||
if (ee->button() == Qt::RightButton) {
|
||||
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
||||
_public->handleTrayIconActication(QSystemTrayIcon::Context);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return QObject::eventFilter(obj, e);
|
||||
}
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
void MainWindow::Private::setSNITrayIcon(int counter, bool muted) {
|
||||
const auto iconName = GetTrayIconName(counter, muted);
|
||||
const auto panelIconName = GetPanelIconName(counter, muted);
|
||||
|
||||
if (iconName == panelIconName) {
|
||||
if (sniTrayIcon->iconName() == iconName) {
|
||||
return;
|
||||
}
|
||||
|
||||
sniTrayIcon->setIconByName(iconName);
|
||||
sniTrayIcon->setToolTipIconByName(iconName);
|
||||
} else if (IsIndicatorApplication()) {
|
||||
if (!IsIconRegenerationNeeded(counter, muted)
|
||||
&& trayIconFile
|
||||
&& sniTrayIcon->iconName() == trayIconFile->fileName()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto icon = TrayIconGen(counter, muted);
|
||||
trayIconFile = TrayIconFile(icon, _public);
|
||||
|
||||
if (trayIconFile) {
|
||||
// indicator-application doesn't support tooltips
|
||||
sniTrayIcon->setIconByName(trayIconFile->fileName());
|
||||
}
|
||||
} else {
|
||||
if (!IsIconRegenerationNeeded(counter, muted)
|
||||
&& !sniTrayIcon->iconPixmap().isEmpty()
|
||||
&& sniTrayIcon->iconName().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto icon = TrayIconGen(counter, muted);
|
||||
sniTrayIcon->setIconByPixmap(icon);
|
||||
sniTrayIcon->setToolTipIconByPixmap(icon);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::Private::attachToSNITrayIcon() {
|
||||
sniTrayIcon->setToolTipTitle(AppName.utf16());
|
||||
connect(sniTrayIcon,
|
||||
&StatusNotifierItem::activateRequested,
|
||||
[=](const QPoint &) {
|
||||
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
||||
_public->handleTrayIconActication(QSystemTrayIcon::Trigger);
|
||||
});
|
||||
});
|
||||
connect(sniTrayIcon,
|
||||
&StatusNotifierItem::secondaryActivateRequested,
|
||||
[=](const QPoint &) {
|
||||
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
||||
_public->handleTrayIconActication(QSystemTrayIcon::MiddleClick);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::Private::handleSNIHostRegistered() {
|
||||
if (_public->_sniAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
_public->_sniAvailable = true;
|
||||
|
||||
if (Core::App().settings().workMode() == WorkMode::WindowOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(("Switching to SNI tray icon..."));
|
||||
|
||||
if (_public->trayIcon) {
|
||||
_public->trayIcon->setContextMenu(nullptr);
|
||||
_public->trayIcon->deleteLater();
|
||||
}
|
||||
_public->trayIcon = nullptr;
|
||||
|
||||
_public->psSetupTrayIcon();
|
||||
|
||||
SkipTaskbar(
|
||||
_public->windowHandle(),
|
||||
Core::App().settings().workMode() == WorkMode::TrayOnly);
|
||||
}
|
||||
|
||||
void MainWindow::Private::handleSNIOwnerChanged(
|
||||
const QString &service,
|
||||
const QString &oldOwner,
|
||||
const QString &newOwner) {
|
||||
_public->_sniAvailable = IsSNIAvailable();
|
||||
|
||||
if (Core::App().settings().workMode() == WorkMode::WindowOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldOwner.isEmpty() && !newOwner.isEmpty() && _public->_sniAvailable) {
|
||||
LOG(("Switching to SNI tray icon..."));
|
||||
} else if (!oldOwner.isEmpty() && newOwner.isEmpty()) {
|
||||
LOG(("Switching to Qt tray icon..."));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_public->trayIcon) {
|
||||
_public->trayIcon->setContextMenu(0);
|
||||
_public->trayIcon->deleteLater();
|
||||
}
|
||||
_public->trayIcon = nullptr;
|
||||
|
||||
if (_public->trayAvailable()) {
|
||||
_public->psSetupTrayIcon();
|
||||
} else {
|
||||
LOG(("System tray is not available."));
|
||||
}
|
||||
|
||||
SkipTaskbar(
|
||||
_public->windowHandle(),
|
||||
(Core::App().settings().workMode() == WorkMode::TrayOnly)
|
||||
&& _public->trayAvailable());
|
||||
}
|
||||
|
||||
void MainWindow::Private::handleAppMenuOwnerChanged(
|
||||
const QString &service,
|
||||
const QString &oldOwner,
|
||||
|
@ -787,11 +536,6 @@ void MainWindow::Private::handleAppMenuOwnerChanged(
|
|||
MainWindow::MainWindow(not_null<Window::Controller*> controller)
|
||||
: Window::MainWindow(controller)
|
||||
, _private(std::make_unique<Private>(this)) {
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
qDBusRegisterMetaType<ToolTip>();
|
||||
qDBusRegisterMetaType<IconPixmap>();
|
||||
qDBusRegisterMetaType<IconPixmapList>();
|
||||
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
}
|
||||
|
||||
void MainWindow::initHook() {
|
||||
|
@ -817,47 +561,12 @@ void MainWindow::initHook() {
|
|||
});
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
_sniAvailable = IsSNIAvailable();
|
||||
_private->appMenuSupported = IsAppMenuSupported();
|
||||
|
||||
try {
|
||||
_private->dbusConnection = Gio::DBus::Connection::get_sync(
|
||||
Gio::DBus::BusType::BUS_TYPE_SESSION);
|
||||
|
||||
_private->sniRegisteredSignalId = _private->dbusConnection->signal_subscribe(
|
||||
[](
|
||||
const Glib::RefPtr<Gio::DBus::Connection> &connection,
|
||||
const Glib::ustring &sender_name,
|
||||
const Glib::ustring &object_path,
|
||||
const Glib::ustring &interface_name,
|
||||
const Glib::ustring &signal_name,
|
||||
const Glib::VariantContainerBase ¶meters) {
|
||||
if (signal_name == "StatusNotifierHostRegistered") {
|
||||
crl::on_main([] {
|
||||
if (const auto window = App::wnd()) {
|
||||
window->_private->handleSNIHostRegistered();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
std::string(kSNIWatcherService),
|
||||
std::string(kSNIWatcherInterface),
|
||||
"StatusNotifierHostRegistered",
|
||||
std::string(kSNIWatcherObjectPath));
|
||||
|
||||
_private->sniWatcherId = base::Platform::DBus::RegisterServiceWatcher(
|
||||
_private->dbusConnection,
|
||||
std::string(kSNIWatcherService),
|
||||
[=](
|
||||
const Glib::ustring &service,
|
||||
const Glib::ustring &oldOwner,
|
||||
const Glib::ustring &newOwner) {
|
||||
_private->handleSNIOwnerChanged(
|
||||
QString::fromStdString(service),
|
||||
QString::fromStdString(oldOwner),
|
||||
QString::fromStdString(newOwner));
|
||||
});
|
||||
|
||||
_private->appMenuWatcherId = base::Platform::DBus::RegisterServiceWatcher(
|
||||
_private->dbusConnection,
|
||||
std::string(kAppMenuService),
|
||||
|
@ -890,15 +599,11 @@ void MainWindow::initHook() {
|
|||
XCBSetDesktopFileName(windowHandle());
|
||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
|
||||
LOG(("System tray available: %1").arg(Logs::b(trayAvailable())));
|
||||
LOG(("System tray available: %1").arg(Logs::b(TrayIconSupported())));
|
||||
}
|
||||
|
||||
bool MainWindow::hasTrayIcon() const {
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
return trayIcon || (_sniAvailable && _private->sniTrayIcon);
|
||||
#else
|
||||
return trayIcon;
|
||||
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
}
|
||||
|
||||
bool MainWindow::isActiveForTrayMenu() {
|
||||
|
@ -907,44 +612,19 @@ bool MainWindow::isActiveForTrayMenu() {
|
|||
}
|
||||
|
||||
void MainWindow::psShowTrayMenu() {
|
||||
_trayIconMenuXEmbed->popup(QCursor::pos());
|
||||
_private->trayIconMenuXEmbed->popup(QCursor::pos());
|
||||
}
|
||||
|
||||
void MainWindow::psTrayMenuUpdated() {
|
||||
}
|
||||
|
||||
void MainWindow::psSetupTrayIcon() {
|
||||
const auto counter = Core::App().unreadBadge();
|
||||
const auto muted = Core::App().unreadBadgeMuted();
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
if (_sniAvailable) {
|
||||
LOG(("Using SNI tray icon."));
|
||||
if (!_private->sniTrayIcon) {
|
||||
_private->sniTrayIcon = new StatusNotifierItem(
|
||||
QCoreApplication::applicationName(),
|
||||
this);
|
||||
|
||||
_private->sniTrayIcon->setTitle(AppName.utf16());
|
||||
_private->sniTrayIcon->setCategory(qsl("Communications"));
|
||||
_private->sniTrayIcon->setContextMenu(trayIconMenu);
|
||||
_private->setSNITrayIcon(counter, muted);
|
||||
|
||||
_private->attachToSNITrayIcon();
|
||||
}
|
||||
updateIconCounters();
|
||||
|
||||
return;
|
||||
}
|
||||
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
|
||||
LOG(("Using Qt tray icon."));
|
||||
if (!trayIcon) {
|
||||
trayIcon = new QSystemTrayIcon(this);
|
||||
if (_sniAvailable) {
|
||||
trayIcon->setContextMenu(trayIconMenu);
|
||||
}
|
||||
trayIcon->setIcon(TrayIconGen(counter, muted));
|
||||
trayIcon->setContextMenu(trayIconMenu);
|
||||
trayIcon->setIcon(TrayIconGen(
|
||||
Core::App().unreadBadge(),
|
||||
Core::App().unreadBadgeMuted()));
|
||||
|
||||
attachToTrayIcon(trayIcon);
|
||||
}
|
||||
|
@ -954,17 +634,9 @@ void MainWindow::psSetupTrayIcon() {
|
|||
}
|
||||
|
||||
void MainWindow::workmodeUpdated(Core::Settings::WorkMode mode) {
|
||||
if (!trayAvailable()) {
|
||||
if (!TrayIconSupported()) {
|
||||
return;
|
||||
} else if (mode == WorkMode::WindowOnly) {
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
if (_private->sniTrayIcon) {
|
||||
_private->sniTrayIcon->setContextMenu(0);
|
||||
_private->sniTrayIcon->deleteLater();
|
||||
}
|
||||
_private->sniTrayIcon = nullptr;
|
||||
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
|
||||
if (trayIcon) {
|
||||
trayIcon->setContextMenu(0);
|
||||
trayIcon->deleteLater();
|
||||
|
@ -1024,10 +696,6 @@ void MainWindow::updateIconCounters() {
|
|||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
if (_private->sniTrayIcon) {
|
||||
_private->setSNITrayIcon(counter, muted);
|
||||
}
|
||||
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
|
||||
if (trayIcon && IsIconRegenerationNeeded(counter, muted)) {
|
||||
|
@ -1036,8 +704,8 @@ void MainWindow::updateIconCounters() {
|
|||
}
|
||||
|
||||
void MainWindow::initTrayMenuHook() {
|
||||
_trayIconMenuXEmbed.emplace(nullptr, trayIconMenu);
|
||||
_trayIconMenuXEmbed->deleteOnHide(false);
|
||||
_private->trayIconMenuXEmbed.emplace(nullptr, trayIconMenu);
|
||||
_private->trayIconMenuXEmbed->deleteOnHide(false);
|
||||
}
|
||||
|
||||
void MainWindow::createGlobalMenu() {
|
||||
|
@ -1310,7 +978,7 @@ void MainWindow::handleNativeSurfaceChanged(bool exist) {
|
|||
SkipTaskbar(
|
||||
windowHandle(),
|
||||
(Core::App().settings().workMode() == WorkMode::TrayOnly)
|
||||
&& trayAvailable());
|
||||
&& TrayIconSupported());
|
||||
}
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
|
@ -1327,16 +995,6 @@ void MainWindow::handleNativeSurfaceChanged(bool exist) {
|
|||
MainWindow::~MainWindow() {
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
if (_private->dbusConnection) {
|
||||
if (_private->sniRegisteredSignalId != 0) {
|
||||
_private->dbusConnection->signal_unsubscribe(
|
||||
_private->sniRegisteredSignalId);
|
||||
}
|
||||
|
||||
if (_private->sniWatcherId != 0) {
|
||||
_private->dbusConnection->signal_unsubscribe(
|
||||
_private->sniWatcherId);
|
||||
}
|
||||
|
||||
if (_private->appMenuWatcherId != 0) {
|
||||
_private->dbusConnection->signal_unsubscribe(
|
||||
_private->appMenuWatcherId);
|
||||
|
|
|
@ -8,11 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#pragma once
|
||||
|
||||
#include "platform/platform_main_window.h"
|
||||
#include "base/unique_qptr.h"
|
||||
|
||||
namespace Ui {
|
||||
class PopupMenu;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Platform {
|
||||
|
||||
|
@ -22,10 +17,6 @@ public:
|
|||
|
||||
void psShowTrayMenu();
|
||||
|
||||
bool trayAvailable() {
|
||||
return _sniAvailable || QSystemTrayIcon::isSystemTrayAvailable();
|
||||
}
|
||||
|
||||
bool isActiveForTrayMenu() override;
|
||||
|
||||
~MainWindow();
|
||||
|
@ -52,9 +43,6 @@ private:
|
|||
friend class Private;
|
||||
const std::unique_ptr<Private> _private;
|
||||
|
||||
bool _sniAvailable = false;
|
||||
base::unique_qptr<Ui::PopupMenu> _trayIconMenuXEmbed;
|
||||
|
||||
QMenu *psMainMenu = nullptr;
|
||||
QAction *psLogout = nullptr;
|
||||
QAction *psUndo = nullptr;
|
||||
|
|
|
@ -592,9 +592,7 @@ bool AutostartSkip() {
|
|||
}
|
||||
|
||||
bool TrayIconSupported() {
|
||||
return App::wnd()
|
||||
? App::wnd()->trayAvailable()
|
||||
: false;
|
||||
return QSystemTrayIcon::isSystemTrayAvailable();
|
||||
}
|
||||
|
||||
bool SkipTaskbarSupported() {
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||
* (c)LGPL2+
|
||||
*
|
||||
* LXQt - a lightweight, Qt based, desktop toolset
|
||||
* https://lxqt.org
|
||||
*
|
||||
* Copyright: 2015 LXQt team
|
||||
* Authors:
|
||||
* Balázs Béla <balazsbela[at]gmail.com>
|
||||
* Paulo Lieuthier <paulolieuthier@gmail.com>
|
||||
*
|
||||
* This program or library is free software; you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*
|
||||
* END_COMMON_COPYRIGHT_HEADER */
|
||||
|
||||
#include "dbustypes.h"
|
||||
|
||||
// Marshall the IconPixmap data into a D-Bus argument
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const IconPixmap &icon)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << icon.width;
|
||||
argument << icon.height;
|
||||
argument << icon.bytes;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
// Retrieve the ImageStruct data from the D-Bus argument
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, IconPixmap &icon)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument >> icon.width;
|
||||
argument >> icon.height;
|
||||
argument >> icon.bytes;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
// Marshall the ToolTip data into a D-Bus argument
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const ToolTip &toolTip)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << toolTip.iconName;
|
||||
argument << toolTip.iconPixmap;
|
||||
argument << toolTip.title;
|
||||
argument << toolTip.description;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
// Retrieve the ToolTip data from the D-Bus argument
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, ToolTip &toolTip)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument >> toolTip.iconName;
|
||||
argument >> toolTip.iconPixmap;
|
||||
argument >> toolTip.title;
|
||||
argument >> toolTip.description;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||
* (c)LGPL2+
|
||||
*
|
||||
* LXQt - a lightweight, Qt based, desktop toolset
|
||||
* https://lxqt.org
|
||||
*
|
||||
* Copyright: 2015 LXQt team
|
||||
* Authors:
|
||||
* Balázs Béla <balazsbela[at]gmail.com>
|
||||
* Paulo Lieuthier <paulolieuthier@gmail.com>
|
||||
*
|
||||
* This program or library is free software; you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*
|
||||
* END_COMMON_COPYRIGHT_HEADER */
|
||||
|
||||
#include <QDBusArgument>
|
||||
|
||||
#ifndef DBUSTYPES_H
|
||||
#define DBUSTYPES_H
|
||||
|
||||
struct IconPixmap {
|
||||
int width;
|
||||
int height;
|
||||
QByteArray bytes;
|
||||
};
|
||||
|
||||
typedef QList<IconPixmap> IconPixmapList;
|
||||
|
||||
Q_DECLARE_METATYPE(IconPixmap)
|
||||
Q_DECLARE_METATYPE(IconPixmapList)
|
||||
|
||||
struct ToolTip {
|
||||
QString iconName;
|
||||
QList<IconPixmap> iconPixmap;
|
||||
QString title;
|
||||
QString description;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ToolTip)
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const IconPixmap &icon);
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, IconPixmap &icon);
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const ToolTip &toolTip);
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, ToolTip &toolTip);
|
||||
|
||||
#endif // DBUSTYPES_H
|
|
@ -1,69 +0,0 @@
|
|||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.kde.StatusNotifierItem">
|
||||
|
||||
<property name="Category" type="s" access="read"/>
|
||||
<property name="Id" type="s" access="read"/>
|
||||
<property name="Title" type="s" access="read"/>
|
||||
<property name="Status" type="s" access="read"/>
|
||||
<property name="WindowId" type="i" access="read"/>
|
||||
<property name="IconThemePath" type="s" access="read"/>
|
||||
<property name="Menu" type="o" access="read"/>
|
||||
<property name="ItemIsMenu" type="b" access="read"/>
|
||||
<property name="IconName" type="s" access="read"/>
|
||||
<property name="IconPixmap" type="a(iiay)" access="read">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName" value="IconPixmapList"/>
|
||||
</property>
|
||||
<property name="OverlayIconName" type="s" access="read"/>
|
||||
<property name="OverlayIconPixmap" type="a(iiay)" access="read">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName" value="IconPixmapList"/>
|
||||
</property>
|
||||
<property name="AttentionIconName" type="s" access="read"/>
|
||||
<property name="AttentionIconPixmap" type="a(iiay)" access="read">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName" value="IconPixmapList"/>
|
||||
</property>
|
||||
<property name="AttentionMovieName" type="s" access="read"/>
|
||||
<property name="ToolTip" type="(sa(iiay)ss)" access="read">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName" value="ToolTip"/>
|
||||
</property>
|
||||
<method name="ContextMenu">
|
||||
<arg name="x" type="i" direction="in"/>
|
||||
<arg name="y" type="i" direction="in"/>
|
||||
</method>
|
||||
|
||||
<method name="Activate">
|
||||
<arg name="x" type="i" direction="in"/>
|
||||
<arg name="y" type="i" direction="in"/>
|
||||
</method>
|
||||
|
||||
<method name="SecondaryActivate">
|
||||
<arg name="x" type="i" direction="in"/>
|
||||
<arg name="y" type="i" direction="in"/>
|
||||
</method>
|
||||
|
||||
<method name="Scroll">
|
||||
<arg name="delta" type="i" direction="in"/>
|
||||
<arg name="orientation" type="s" direction="in"/>
|
||||
</method>
|
||||
|
||||
<signal name="NewTitle">
|
||||
</signal>
|
||||
|
||||
<signal name="NewIcon">
|
||||
</signal>
|
||||
|
||||
<signal name="NewAttentionIcon">
|
||||
</signal>
|
||||
|
||||
<signal name="NewOverlayIcon">
|
||||
</signal>
|
||||
|
||||
<signal name="NewToolTip">
|
||||
</signal>
|
||||
|
||||
<signal name="NewStatus">
|
||||
<arg name="status" type="s"/>
|
||||
</signal>
|
||||
|
||||
</interface>
|
||||
</node>
|
|
@ -1,354 +0,0 @@
|
|||
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||
* (c)LGPL2+
|
||||
*
|
||||
* LXQt - a lightweight, Qt based, desktop toolset
|
||||
* https://lxqt.org/
|
||||
*
|
||||
* Copyright: 2015 LXQt team
|
||||
* Authors:
|
||||
* Paulo Lieuthier <paulolieuthier@gmail.com>
|
||||
*
|
||||
* This program or library is free software; you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*
|
||||
* END_COMMON_COPYRIGHT_HEADER */
|
||||
|
||||
#include "statusnotifieritem.h"
|
||||
#include "statusnotifieritemadaptor.h"
|
||||
#include <QDBusServiceWatcher>
|
||||
#include <QDBusMessage>
|
||||
#include <utility>
|
||||
#include <dbusmenuexporter.h>
|
||||
|
||||
int StatusNotifierItem::mServiceCounter = 0;
|
||||
|
||||
StatusNotifierItem::StatusNotifierItem(QString id, QObject *parent)
|
||||
: QObject(parent),
|
||||
mAdaptor(new StatusNotifierItemAdaptor(this)),
|
||||
mService(QString::fromLatin1("org.freedesktop.StatusNotifierItem-%1-%2")
|
||||
.arg(QCoreApplication::applicationPid())
|
||||
.arg(++mServiceCounter)),
|
||||
mId(std::move(id)),
|
||||
mTitle(QLatin1String("Test")),
|
||||
mStatus(QLatin1String("Active")),
|
||||
mCategory(QLatin1String("ApplicationStatus")),
|
||||
mMenu(nullptr),
|
||||
mMenuPath(QLatin1String("/NO_DBUSMENU")),
|
||||
mMenuExporter(nullptr),
|
||||
mSessionBus(QDBusConnection::connectToBus(QDBusConnection::SessionBus, mService))
|
||||
{
|
||||
// Separate DBus connection to the session bus is created, because QDbus does not provide
|
||||
// a way to register different objects for different services with the same paths.
|
||||
// For status notifiers we need different /StatusNotifierItem for each service.
|
||||
|
||||
// register service
|
||||
|
||||
mSessionBus.registerObject(QLatin1String("/StatusNotifierItem"), this);
|
||||
|
||||
registerToHost();
|
||||
|
||||
// monitor the watcher service in case the host restarts
|
||||
QDBusServiceWatcher *watcher = new QDBusServiceWatcher(QLatin1String("org.kde.StatusNotifierWatcher"),
|
||||
mSessionBus,
|
||||
QDBusServiceWatcher::WatchForOwnerChange,
|
||||
this);
|
||||
connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged,
|
||||
this, &StatusNotifierItem::onServiceOwnerChanged);
|
||||
}
|
||||
|
||||
StatusNotifierItem::~StatusNotifierItem()
|
||||
{
|
||||
mSessionBus.unregisterObject(QLatin1String("/StatusNotifierItem"));
|
||||
QDBusConnection::disconnectFromBus(mService);
|
||||
}
|
||||
|
||||
void StatusNotifierItem::registerToHost()
|
||||
{
|
||||
QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.kde.StatusNotifierWatcher"),
|
||||
QLatin1String("/StatusNotifierWatcher"),
|
||||
QLatin1String("org.kde.StatusNotifierWatcher"),
|
||||
QLatin1String("RegisterStatusNotifierItem"));
|
||||
|
||||
message.setArguments({
|
||||
mSessionBus.baseService()
|
||||
});
|
||||
|
||||
mSessionBus.send(message);
|
||||
}
|
||||
|
||||
void StatusNotifierItem::onServiceOwnerChanged(const QString& service, const QString& oldOwner,
|
||||
const QString& newOwner)
|
||||
{
|
||||
Q_UNUSED(service);
|
||||
Q_UNUSED(oldOwner);
|
||||
|
||||
if (!newOwner.isEmpty())
|
||||
registerToHost();
|
||||
}
|
||||
|
||||
void StatusNotifierItem::onMenuDestroyed()
|
||||
{
|
||||
mMenu = nullptr;
|
||||
setMenuPath(QLatin1String("/NO_DBUSMENU"));
|
||||
mMenuExporter = nullptr; //mMenu is a QObject parent of the mMenuExporter
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setTitle(const QString &title)
|
||||
{
|
||||
if (mTitle == title)
|
||||
return;
|
||||
|
||||
mTitle = title;
|
||||
Q_EMIT mAdaptor->NewTitle();
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setStatus(const QString &status)
|
||||
{
|
||||
if (mStatus == status)
|
||||
return;
|
||||
|
||||
mStatus = status;
|
||||
Q_EMIT mAdaptor->NewStatus(mStatus);
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setCategory(const QString &category)
|
||||
{
|
||||
if (mCategory == category)
|
||||
return;
|
||||
|
||||
mCategory = category;
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setMenuPath(const QString& path)
|
||||
{
|
||||
mMenuPath.setPath(path);
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setIconByName(const QString &name)
|
||||
{
|
||||
if (mIconName == name)
|
||||
return;
|
||||
|
||||
mIconName = name;
|
||||
Q_EMIT mAdaptor->NewIcon();
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setIconByPixmap(const QIcon &icon)
|
||||
{
|
||||
if (mIconCacheKey == icon.cacheKey())
|
||||
return;
|
||||
|
||||
mIconCacheKey = icon.cacheKey();
|
||||
mIcon = iconToPixmapList(icon);
|
||||
mIconName.clear();
|
||||
Q_EMIT mAdaptor->NewIcon();
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setOverlayIconByName(const QString &name)
|
||||
{
|
||||
if (mOverlayIconName == name)
|
||||
return;
|
||||
|
||||
mOverlayIconName = name;
|
||||
Q_EMIT mAdaptor->NewOverlayIcon();
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setOverlayIconByPixmap(const QIcon &icon)
|
||||
{
|
||||
if (mOverlayIconCacheKey == icon.cacheKey())
|
||||
return;
|
||||
|
||||
mOverlayIconCacheKey = icon.cacheKey();
|
||||
mOverlayIcon = iconToPixmapList(icon);
|
||||
mOverlayIconName.clear();
|
||||
Q_EMIT mAdaptor->NewOverlayIcon();
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setAttentionIconByName(const QString &name)
|
||||
{
|
||||
if (mAttentionIconName == name)
|
||||
return;
|
||||
|
||||
mAttentionIconName = name;
|
||||
Q_EMIT mAdaptor->NewAttentionIcon();
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setAttentionIconByPixmap(const QIcon &icon)
|
||||
{
|
||||
if (mAttentionIconCacheKey == icon.cacheKey())
|
||||
return;
|
||||
|
||||
mAttentionIconCacheKey = icon.cacheKey();
|
||||
mAttentionIcon = iconToPixmapList(icon);
|
||||
mAttentionIconName.clear();
|
||||
Q_EMIT mAdaptor->NewAttentionIcon();
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setToolTipTitle(const QString &title)
|
||||
{
|
||||
if (mTooltipTitle == title)
|
||||
return;
|
||||
|
||||
mTooltipTitle = title;
|
||||
Q_EMIT mAdaptor->NewToolTip();
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setToolTipSubTitle(const QString &subTitle)
|
||||
{
|
||||
if (mTooltipSubtitle == subTitle)
|
||||
return;
|
||||
|
||||
mTooltipSubtitle = subTitle;
|
||||
Q_EMIT mAdaptor->NewToolTip();
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setToolTipIconByName(const QString &name)
|
||||
{
|
||||
if (mTooltipIconName == name)
|
||||
return;
|
||||
|
||||
mTooltipIconName = name;
|
||||
Q_EMIT mAdaptor->NewToolTip();
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setToolTipIconByPixmap(const QIcon &icon)
|
||||
{
|
||||
if (mTooltipIconCacheKey == icon.cacheKey())
|
||||
return;
|
||||
|
||||
mTooltipIconCacheKey = icon.cacheKey();
|
||||
mTooltipIcon = iconToPixmapList(icon);
|
||||
mTooltipIconName.clear();
|
||||
Q_EMIT mAdaptor->NewToolTip();
|
||||
}
|
||||
|
||||
void StatusNotifierItem::setContextMenu(QMenu* menu)
|
||||
{
|
||||
if (mMenu == menu)
|
||||
return;
|
||||
|
||||
if (nullptr != mMenu)
|
||||
{
|
||||
disconnect(mMenu, &QObject::destroyed, this, &StatusNotifierItem::onMenuDestroyed);
|
||||
}
|
||||
mMenu = menu;
|
||||
|
||||
if (nullptr != mMenu)
|
||||
setMenuPath(QLatin1String("/MenuBar"));
|
||||
else
|
||||
setMenuPath(QLatin1String("/NO_DBUSMENU"));
|
||||
|
||||
//Note: we need to destroy menu exporter before creating new one -> to free the DBus object path for new menu
|
||||
delete mMenuExporter;
|
||||
if (nullptr != mMenu)
|
||||
{
|
||||
connect(mMenu, &QObject::destroyed, this, &StatusNotifierItem::onMenuDestroyed);
|
||||
mMenuExporter = new DBusMenuExporter{this->menu().path(), mMenu, mSessionBus};
|
||||
}
|
||||
}
|
||||
|
||||
void StatusNotifierItem::Activate(int x, int y)
|
||||
{
|
||||
if (mStatus == QLatin1String("NeedsAttention"))
|
||||
mStatus = QLatin1String("Active");
|
||||
|
||||
Q_EMIT activateRequested(QPoint(x, y));
|
||||
}
|
||||
|
||||
void StatusNotifierItem::SecondaryActivate(int x, int y)
|
||||
{
|
||||
if (mStatus == QLatin1String("NeedsAttention"))
|
||||
mStatus = QLatin1String("Active");
|
||||
|
||||
Q_EMIT secondaryActivateRequested(QPoint(x, y));
|
||||
}
|
||||
|
||||
void StatusNotifierItem::ContextMenu(int x, int y)
|
||||
{
|
||||
if (mMenu != nullptr)
|
||||
{
|
||||
if (mMenu->isVisible())
|
||||
mMenu->popup(QPoint(x, y));
|
||||
else
|
||||
mMenu->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void StatusNotifierItem::Scroll(int delta, const QString &orientation)
|
||||
{
|
||||
Qt::Orientation orient = Qt::Vertical;
|
||||
if (orientation.toLower() == QLatin1String("horizontal"))
|
||||
orient = Qt::Horizontal;
|
||||
|
||||
Q_EMIT scrollRequested(delta, orient);
|
||||
}
|
||||
|
||||
void StatusNotifierItem::showMessage(const QString& title, const QString& msg,
|
||||
const QString& iconName, int secs)
|
||||
{
|
||||
QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.Notifications"),
|
||||
QLatin1String("/org/freedesktop/Notifications"),
|
||||
QLatin1String("org.freedesktop.Notifications"),
|
||||
QLatin1String("Notify"));
|
||||
|
||||
message.setArguments({
|
||||
mTitle,
|
||||
(uint) 0,
|
||||
iconName,
|
||||
title,
|
||||
msg,
|
||||
QStringList(),
|
||||
QVariantMap(),
|
||||
secs
|
||||
});
|
||||
|
||||
mSessionBus.send(message);
|
||||
}
|
||||
|
||||
IconPixmapList StatusNotifierItem::iconToPixmapList(const QIcon& icon)
|
||||
{
|
||||
IconPixmapList pixmapList;
|
||||
|
||||
// long live KDE!
|
||||
const QList<QSize> sizes = icon.availableSizes();
|
||||
for (const QSize &size : sizes)
|
||||
{
|
||||
QImage image = icon.pixmap(size).toImage();
|
||||
|
||||
IconPixmap pix;
|
||||
pix.height = image.height();
|
||||
pix.width = image.width();
|
||||
|
||||
if (image.format() != QImage::Format_ARGB32)
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
|
||||
pix.bytes = QByteArray((char *) image.bits(), image.sizeInBytes());
|
||||
|
||||
// swap to network byte order if we are little endian
|
||||
if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
|
||||
{
|
||||
quint32 *uintBuf = (quint32 *) pix.bytes.data();
|
||||
for (uint i = 0; i < pix.bytes.size() / sizeof(quint32); ++i)
|
||||
{
|
||||
*uintBuf = qToBigEndian(*uintBuf);
|
||||
++uintBuf;
|
||||
}
|
||||
}
|
||||
|
||||
pixmapList.append(pix);
|
||||
}
|
||||
|
||||
return pixmapList;
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||
* (c)LGPL2+
|
||||
*
|
||||
* LXQt - a lightweight, Qt based, desktop toolset
|
||||
* https://lxqt.org/
|
||||
*
|
||||
* Copyright: 2015 LXQt team
|
||||
* Authors:
|
||||
* Paulo Lieuthier <paulolieuthier@gmail.com>
|
||||
*
|
||||
* This program or library is free software; you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*
|
||||
* END_COMMON_COPYRIGHT_HEADER */
|
||||
|
||||
|
||||
#ifndef STATUS_NOTIFIER_ITEM_H
|
||||
#define STATUS_NOTIFIER_ITEM_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QIcon>
|
||||
#include <QMenu>
|
||||
#include <QDBusConnection>
|
||||
|
||||
#include "dbustypes.h"
|
||||
|
||||
class StatusNotifierItemAdaptor;
|
||||
class DBusMenuExporter;
|
||||
|
||||
class StatusNotifierItem : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString Category READ category)
|
||||
Q_PROPERTY(QString Title READ title)
|
||||
Q_PROPERTY(QString Id READ id)
|
||||
Q_PROPERTY(QString Status READ status)
|
||||
Q_PROPERTY(QDBusObjectPath Menu READ menu)
|
||||
|
||||
Q_PROPERTY(QString IconName READ iconName)
|
||||
Q_PROPERTY(IconPixmapList IconPixmap READ iconPixmap)
|
||||
|
||||
Q_PROPERTY(QString OverlayIconName READ overlayIconName)
|
||||
Q_PROPERTY(IconPixmapList OverlayIconPixmap READ overlayIconPixmap)
|
||||
|
||||
Q_PROPERTY(QString AttentionIconName READ attentionIconName)
|
||||
Q_PROPERTY(IconPixmapList AttentionIconPixmap READ attentionIconPixmap)
|
||||
|
||||
Q_PROPERTY(ToolTip ToolTip READ toolTip)
|
||||
|
||||
public:
|
||||
StatusNotifierItem(QString id, QObject *parent = nullptr);
|
||||
~StatusNotifierItem() override;
|
||||
|
||||
QString id() const
|
||||
{ return mId; }
|
||||
|
||||
QString title() const
|
||||
{ return mTitle; }
|
||||
void setTitle(const QString &title);
|
||||
|
||||
QString status() const
|
||||
{ return mStatus; }
|
||||
void setStatus(const QString &status);
|
||||
|
||||
QString category() const
|
||||
{ return mCategory; }
|
||||
void setCategory(const QString &category);
|
||||
|
||||
QDBusObjectPath menu() const
|
||||
{ return mMenuPath; }
|
||||
void setMenuPath(const QString &path);
|
||||
|
||||
QString iconName() const
|
||||
{ return mIconName; }
|
||||
void setIconByName(const QString &name);
|
||||
|
||||
IconPixmapList iconPixmap() const
|
||||
{ return mIcon; }
|
||||
void setIconByPixmap(const QIcon &icon);
|
||||
|
||||
QString overlayIconName() const
|
||||
{ return mOverlayIconName; }
|
||||
void setOverlayIconByName(const QString &name);
|
||||
|
||||
IconPixmapList overlayIconPixmap() const
|
||||
{ return mOverlayIcon; }
|
||||
void setOverlayIconByPixmap(const QIcon &icon);
|
||||
|
||||
QString attentionIconName() const
|
||||
{ return mAttentionIconName; }
|
||||
void setAttentionIconByName(const QString &name);
|
||||
|
||||
IconPixmapList attentionIconPixmap() const
|
||||
{ return mAttentionIcon; }
|
||||
void setAttentionIconByPixmap(const QIcon &icon);
|
||||
|
||||
QString toolTipTitle() const
|
||||
{ return mTooltipTitle; }
|
||||
void setToolTipTitle(const QString &title);
|
||||
|
||||
QString toolTipSubTitle() const
|
||||
{ return mTooltipSubtitle; }
|
||||
void setToolTipSubTitle(const QString &subTitle);
|
||||
|
||||
QString toolTipIconName() const
|
||||
{ return mTooltipIconName; }
|
||||
void setToolTipIconByName(const QString &name);
|
||||
|
||||
IconPixmapList toolTipIconPixmap() const
|
||||
{ return mTooltipIcon; }
|
||||
void setToolTipIconByPixmap(const QIcon &icon);
|
||||
|
||||
ToolTip toolTip() const
|
||||
{
|
||||
ToolTip tt;
|
||||
tt.title = mTooltipTitle;
|
||||
tt.description = mTooltipSubtitle;
|
||||
tt.iconName = mTooltipIconName;
|
||||
tt.iconPixmap = mTooltipIcon;
|
||||
return tt;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \Note: we don't take ownership for the \param menu
|
||||
*/
|
||||
void setContextMenu(QMenu *menu);
|
||||
|
||||
public Q_SLOTS:
|
||||
void Activate(int x, int y);
|
||||
void SecondaryActivate(int x, int y);
|
||||
void ContextMenu(int x, int y);
|
||||
void Scroll(int delta, const QString &orientation);
|
||||
|
||||
void showMessage(const QString &title, const QString &msg, const QString &iconName, int secs);
|
||||
|
||||
private:
|
||||
void registerToHost();
|
||||
IconPixmapList iconToPixmapList(const QIcon &icon);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onServiceOwnerChanged(const QString &service, const QString &oldOwner,
|
||||
const QString &newOwner);
|
||||
void onMenuDestroyed();
|
||||
|
||||
Q_SIGNALS:
|
||||
void activateRequested(const QPoint &pos);
|
||||
void secondaryActivateRequested(const QPoint &pos);
|
||||
void scrollRequested(int delta, Qt::Orientation orientation);
|
||||
|
||||
private:
|
||||
StatusNotifierItemAdaptor *mAdaptor;
|
||||
|
||||
QString mService;
|
||||
QString mId;
|
||||
QString mTitle;
|
||||
QString mStatus;
|
||||
QString mCategory;
|
||||
|
||||
// icons
|
||||
QString mIconName, mOverlayIconName, mAttentionIconName;
|
||||
IconPixmapList mIcon, mOverlayIcon, mAttentionIcon;
|
||||
qint64 mIconCacheKey, mOverlayIconCacheKey, mAttentionIconCacheKey;
|
||||
|
||||
// tooltip
|
||||
QString mTooltipTitle, mTooltipSubtitle, mTooltipIconName;
|
||||
IconPixmapList mTooltipIcon;
|
||||
qint64 mTooltipIconCacheKey;
|
||||
|
||||
// menu
|
||||
QMenu *mMenu;
|
||||
QDBusObjectPath mMenuPath;
|
||||
DBusMenuExporter *mMenuExporter;
|
||||
QDBusConnection mSessionBus;
|
||||
|
||||
static int mServiceCounter;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Add table
Reference in a new issue