Use LXQt's StatusNotifierItem implementation instead of appindicator

This commit is contained in:
Ilya Fedin 2020-01-31 10:34:37 +04:00 committed by John Preston
parent 1b1f9d9985
commit 3b4dfa1381
22 changed files with 1089 additions and 567 deletions

View File

@ -54,7 +54,7 @@ jobs:
sudo apt-get update
sudo apt-get install software-properties-common -y && \
sudo apt-get install git libexif-dev liblzma-dev libz-dev libssl-dev \
libappindicator-dev libicu-dev libdee-dev libdrm-dev dh-autoreconf \
libgtk2.0-dev libice-dev libsm-dev libicu-dev libdrm-dev dh-autoreconf \
autoconf automake build-essential libass-dev libfreetype6-dev \
libgpac-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev \
libvorbis-dev libenchant-dev libxcb1-dev libxcb-image0-dev libxcb-shm0-dev \

3
.gitmodules vendored
View File

@ -61,3 +61,6 @@
[submodule "Telegram/lib_qr"]
path = Telegram/lib_qr
url = https://github.com/desktop-app/lib_qr.git
[submodule "Telegram/ThirdParty/libdbusmenu-qt"]
path = Telegram/ThirdParty/libdbusmenu-qt
url = https://github.com/desktop-app/libdbusmenu-qt.git

View File

@ -77,6 +77,14 @@ if (DESKTOP_APP_USE_PACKAGED)
)
endif()
if (LINUX AND NOT TDESKTOP_DISABLE_DBUS_INTEGRATION)
target_link_libraries(Telegram
PRIVATE
desktop-app::external_statusnotifieritem
desktop-app::external_dbusmenu_qt
)
endif()
target_link_libraries(Telegram
PRIVATE
tdesktop::lib_mtproto
@ -1059,28 +1067,6 @@ elseif (LINUX)
find_library(X11_LIBRARY X11)
target_link_libraries(Telegram PRIVATE ${X11_LIBRARY})
endif()
set(appindicator_packages
ayatana-appindicator3-0.1
ayatana-appindicator-0.1
appindicator3-0.1
appindicator-0.1
)
set(appindicator_found 0)
foreach (package ${appindicator_packages})
pkg_check_modules(APPIND_${package} ${package})
if (APPIND_${package}_FOUND)
set(appindicator_found 1)
target_include_directories(Telegram PRIVATE "${APPIND_${package}_INCLUDE_DIRS}")
if (${package} MATCHES "ayatana")
target_compile_definitions(Telegram PRIVATE TDESKTOP_USE_AYATANA_INDICATORS)
endif()
break()
endif()
endforeach()
if (NOT ${appindicator_found})
message(FATAL_ERROR "No libappindicator found by pkg-config.")
endif()
endif()
endif()

View File

@ -144,14 +144,10 @@ void MainWindow::firstShow() {
if (Platform::IsLinux()) {
trayIconMenu->addAction(tr::lng_open_from_tray(tr::now), this, SLOT(showFromTray()));
trayIconMenu->addAction(tr::lng_minimize_to_tray(tr::now), this, SLOT(minimizeToTray()));
trayIconMenu->addAction(notificationActionText, this, SLOT(toggleDisplayNotifyFromTray()));
trayIconMenu->addAction(tr::lng_quit_from_tray(tr::now), this, SLOT(quitFromTray()));
} else {
trayIconMenu->addAction(tr::lng_minimize_to_tray(tr::now), this, SLOT(minimizeToTray()));
trayIconMenu->addAction(notificationActionText, this, SLOT(toggleDisplayNotifyFromTray()));
trayIconMenu->addAction(tr::lng_quit_from_tray(tr::now), this, SLOT(quitFromTray()));
}
trayIconMenu->addAction(tr::lng_minimize_to_tray(tr::now), this, SLOT(minimizeToTray()));
trayIconMenu->addAction(notificationActionText, this, SLOT(toggleDisplayNotifyFromTray()));
trayIconMenu->addAction(tr::lng_quit_from_tray(tr::now), this, SLOT(quitFromTray()));
Global::RefWorkMode().setForced(Global::WorkMode().value(), true);
psFirstShow();
@ -564,7 +560,7 @@ void MainWindow::updateTrayMenu(bool force) {
auto actions = iconMenu->actions();
if (Platform::IsLinux()) {
auto minimizeAction = actions.at(1);
minimizeAction->setDisabled(!isVisible());
minimizeAction->setEnabled(isVisible());
} else {
updateIsActive(0);
auto active = isActive();
@ -588,12 +584,6 @@ void MainWindow::updateTrayMenu(bool force) {
: tr::lng_enable_notifications_from_tray(tr::now);
notificationAction->setText(notificationActionText);
#ifndef Q_OS_WIN
if (trayIcon && trayIcon->contextMenu() != iconMenu) {
trayIcon->setContextMenu(iconMenu);
}
#endif // !Q_OS_WIN
psTrayMenuUpdated();
}

View File

@ -9,10 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/linux/specific_linux.h"
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
#include <QDBusInterface>
#endif
namespace Platform {
namespace DesktopEnvironment {
namespace {
@ -44,18 +40,15 @@ Type Compute() {
return Type::Gnome;
}
return Type::Unity;
} else if (list.contains("pantheon")) {
return Type::Pantheon;
} else if (list.contains("gnome")) {
if (list.contains("ubuntu"))
return Type::Ubuntu;
return Type::Gnome;
} else if (list.contains("kde")) {
if (kdeSession == qstr("5")) {
return Type::KDE5;
}
return Type::KDE4;
} else if (list.contains("mate")) {
return Type::MATE;
}
}
@ -70,6 +63,8 @@ Type Compute() {
return Type::KDE4;
}
return Type::KDE3;
} else if (desktopSession == qstr("mate")) {
return Type::MATE;
}
}
@ -96,9 +91,8 @@ Type ComputeAndLog() {
case Type::KDE3: return "KDE3";
case Type::KDE4: return "KDE4";
case Type::KDE5: return "KDE5";
case Type::Ubuntu: return "Ubuntu";
case Type::Unity: return "Unity";
case Type::Pantheon: return "Pantheon";
case Type::MATE: return "MATE";
}
return QString::number(static_cast<int>(result));
};
@ -114,20 +108,5 @@ Type Get() {
return result;
}
bool TryQtTrayIcon() {
return !IsPantheon();
}
bool PreferAppIndicatorTrayIcon() {
return (InSandbox() && !IsKDE())
|| IsUnity()
|| IsUbuntu()
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
|| (IsGnome() && QDBusInterface("org.kde.StatusNotifierWatcher", "/").isValid());
#else
|| IsGnome();
#endif
}
} // namespace DesktopEnvironment
} // namespace Platform

View File

@ -16,9 +16,8 @@ enum class Type {
KDE3,
KDE4,
KDE5,
Ubuntu,
Unity,
Pantheon,
MATE,
};
Type Get();
@ -43,20 +42,13 @@ inline bool IsKDE() {
return IsKDE3() || IsKDE4() || IsKDE5();
}
inline bool IsUbuntu() {
return Get() == Type::Ubuntu;
}
inline bool IsUnity() {
return Get() == Type::Unity;
}
inline bool IsPantheon() {
return Get() == Type::Pantheon;
inline bool IsMATE() {
return Get() == Type::MATE;
}
bool TryQtTrayIcon();
bool PreferAppIndicatorTrayIcon();
} // namespace DesktopEnvironment
} // namespace Platform

View File

@ -126,16 +126,6 @@ bool setupGtkBase(QLibrary &lib_gtk) {
DEBUG_LOG(("Checked gtk with gtk_init_check!"));
return true;
}
bool setupAppIndicator(QLibrary &lib_indicator) {
if (!load(lib_indicator, "app_indicator_new", app_indicator_new)) return false;
if (!load(lib_indicator, "app_indicator_set_status", app_indicator_set_status)) return false;
if (!load(lib_indicator, "app_indicator_set_menu", app_indicator_set_menu)) return false;
if (!load(lib_indicator, "app_indicator_set_icon_full", app_indicator_set_icon_full)) return false;
DEBUG_LOG(("Library appindicator functions loaded!"));
return true;
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
} // namespace
@ -197,10 +187,6 @@ f_g_type_check_instance_cast g_type_check_instance_cast = nullptr;
f_g_type_check_instance_is_a g_type_check_instance_is_a = nullptr;
f_g_signal_connect_data g_signal_connect_data = nullptr;
f_g_signal_handler_disconnect g_signal_handler_disconnect = nullptr;
f_app_indicator_new app_indicator_new = nullptr;
f_app_indicator_set_status app_indicator_set_status = nullptr;
f_app_indicator_set_menu app_indicator_set_menu = nullptr;
f_app_indicator_set_icon_full app_indicator_set_icon_full = nullptr;
f_gdk_init_check gdk_init_check = nullptr;
f_gdk_pixbuf_new_from_data gdk_pixbuf_new_from_data = nullptr;
f_gdk_pixbuf_new_from_file gdk_pixbuf_new_from_file = nullptr;
@ -233,33 +219,14 @@ void start() {
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
bool gtkLoaded = false;
bool indicatorLoaded = false;
bool isWayland = QGuiApplication::platformName().startsWith(qsl("wayland"), Qt::CaseInsensitive);
QLibrary lib_gtk, lib_indicator;
if (loadLibrary(lib_indicator, "ayatana-appindicator3", 1) || loadLibrary(lib_indicator, "appindicator3", 1)) {
if (loadLibrary(lib_gtk, "gtk-3", 0)) {
gtkLoaded = setupGtkBase(lib_gtk);
indicatorLoaded = setupAppIndicator(lib_indicator);
}
}
if ((!gtkLoaded || !indicatorLoaded) && !isWayland) {
if (loadLibrary(lib_indicator, "ayatana-appindicator", 1) || loadLibrary(lib_indicator, "appindicator", 1)) {
if (loadLibrary(lib_gtk, "gtk-x11-2.0", 0)) {
gtkLoaded = indicatorLoaded = false;
gtkLoaded = setupGtkBase(lib_gtk);
indicatorLoaded = setupAppIndicator(lib_indicator);
}
}
}
QLibrary lib_gtk;
// If no appindicator, try at least load gtk.
if (!gtkLoaded && !indicatorLoaded) {
if (loadLibrary(lib_gtk, "gtk-3", 0)) {
gtkLoaded = setupGtkBase(lib_gtk);
}
if (!gtkLoaded && !isWayland && loadLibrary(lib_gtk, "gtk-x11-2.0", 0)) {
gtkLoaded = setupGtkBase(lib_gtk);
}
if (loadLibrary(lib_gtk, "gtk-3", 0)) {
gtkLoaded = setupGtkBase(lib_gtk);
}
if (!gtkLoaded && !isWayland && loadLibrary(lib_gtk, "gtk-x11-2.0", 0)) {
gtkLoaded = setupGtkBase(lib_gtk);
}
if (gtkLoaded) {
@ -287,7 +254,7 @@ void start() {
load(lib_gtk, "gtk_button_set_label", gtk_button_set_label);
load(lib_gtk, "gtk_button_get_type", gtk_button_get_type);
} else {
LOG(("Could not load gtk-x11-2.0!"));
LOG(("Could not load gtk-3 or gtk-x11-2.0!"));
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
}

View File

@ -13,11 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
extern "C" {
#undef signals
#ifdef TDESKTOP_USE_AYATANA_INDICATORS
#include <libayatana-appindicator/app-indicator.h>
#else
#include <libappindicator/app-indicator.h>
#endif
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#define signals public
@ -278,18 +273,6 @@ inline gulong g_signal_connect_swapped_helper(gpointer instance, const gchar *de
typedef void (*f_g_signal_handler_disconnect)(gpointer instance, gulong handler_id);
extern f_g_signal_handler_disconnect g_signal_handler_disconnect;
typedef AppIndicator* (*f_app_indicator_new)(const gchar *id, const gchar *icon_name, AppIndicatorCategory category);
extern f_app_indicator_new app_indicator_new;
typedef void (*f_app_indicator_set_status)(AppIndicator *self, AppIndicatorStatus status);
extern f_app_indicator_set_status app_indicator_set_status;
typedef void (*f_app_indicator_set_menu)(AppIndicator *self, GtkMenu *menu);
extern f_app_indicator_set_menu app_indicator_set_menu;
typedef void (*f_app_indicator_set_icon_full)(AppIndicator *self, const gchar *icon_name, const gchar *icon_desc);
extern f_app_indicator_set_icon_full app_indicator_set_icon_full;
typedef gboolean (*f_gdk_init_check)(gint *argc, gchar ***argv);
extern f_gdk_init_check gdk_init_check;

View File

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "mainwindow.h"
#include "core/application.h"
#include "core/sandbox.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "facades.h"
@ -22,101 +23,77 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
#include <QtDBus>
#endif
#include <QtWidgets/QMenu>
#include <QtWidgets/QAction>
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
namespace Platform {
namespace {
bool noQtTrayIcon = false, tryAppIndicator = false;
bool useGtkBase = false, useAppIndicator = false, useStatusIcon = false, trayIconChecked = false, useUnityCount = false;
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
AppIndicator *_trayIndicator = 0;
GtkStatusIcon *_trayIcon = 0;
GtkWidget *_trayMenu = 0;
GdkPixbuf *_trayPixbuf = 0;
QByteArray _trayPixbufData;
QList<QPair<GtkWidget*, QObject*> > _trayItems;
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
constexpr auto kDisableTrayCounter = "TDESKTOP_DISABLE_TRAY_COUNTER"_cs;
constexpr auto kTrayIconName = "telegram"_cs;
constexpr auto kPanelTrayIconName = "telegram-panel"_cs;
constexpr auto kMutePanelTrayIconName = "telegram-mute-panel"_cs;
constexpr auto kAttentionPanelTrayIconName = "telegram-attention-panel"_cs;
constexpr auto kSNIWatcherService = "org.kde.StatusNotifierWatcher"_cs;
constexpr auto kTrayIconFilename = "tdesktop-trayicon-XXXXXX.png"_cs;
int32 _trayIconSize = 48;
bool _trayIconMuted = true;
int32 _trayIconCount = 0;
QImage _trayIconImageBack, _trayIconImage;
QString _trayIconThemeName, _trayIconName;
QString _desktopFile;
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
QString _dbusPath = "/";
#endif
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
void _trayIconPopup(GtkStatusIcon *status_icon, guint button, guint32 activate_time, gpointer popup_menu) {
Libs::gtk_menu_popup(Libs::gtk_menu_cast(popup_menu), NULL, NULL, Libs::gtk_status_icon_position_menu, status_icon, button, activate_time);
}
void _trayIconActivate(GtkStatusIcon *status_icon, gpointer popup_menu) {
if (App::wnd()->isActiveWindow() && App::wnd()->isVisible()) {
Libs::gtk_menu_popup(Libs::gtk_menu_cast(popup_menu), NULL, NULL, Libs::gtk_status_icon_position_menu, status_icon, 0, Libs::gtk_get_current_event_time());
} else {
App::wnd()->showFromTray();
}
}
gboolean _trayIconResized(GtkStatusIcon *status_icon, gint size, gpointer popup_menu) {
_trayIconSize = size;
if (Global::started()) Notify::unreadCounterUpdated();
return FALSE;
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
bool UseUnityCount = false;
QString UnityCountDesktopFile;
QString UnityCountDBusPath = "/";
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
#define QT_RED 0
#define QT_GREEN 1
#define QT_BLUE 2
#define QT_ALPHA 3
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
#define GTK_RED 2
#define GTK_GREEN 1
#define GTK_BLUE 0
#define GTK_ALPHA 3
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
QString GetTrayIconName() {
const auto counter = Core::App().unreadBadge();
const auto muted = Core::App().unreadBadgeMuted();
QImage _trayIconImageGen(bool useSystemIcon) {
return (counter > 0)
? (muted
? kMutePanelTrayIconName.utf16()
: kAttentionPanelTrayIconName.utf16())
: kPanelTrayIconName.utf16();
}
QImage TrayIconImageGen() {
const auto counter = Core::App().unreadBadge();
const auto muted = Core::App().unreadBadgeMuted();
const auto counterSlice = (counter >= 1000)
? (1000 + (counter % 100))
: counter;
QString iconThemeName = QIcon::themeName();
QString iconName = (counter > 0)
? (muted ? "telegram-mute-panel" : "telegram-attention-panel")
: "telegram-panel";
const auto iconThemeName = QIcon::themeName();
const auto iconName = GetTrayIconName();
if (_trayIconImage.isNull() || _trayIconImage.width() != _trayIconSize
|| (useSystemIcon && (iconThemeName != _trayIconThemeName
|| iconName != _trayIconName))
|| muted != _trayIconMuted || counterSlice != _trayIconCount) {
if (_trayIconImage.isNull()
|| _trayIconImage.width() != _trayIconSize
|| iconThemeName != _trayIconThemeName
|| iconName != _trayIconName
|| muted != _trayIconMuted
|| counterSlice != _trayIconCount) {
if (_trayIconImageBack.isNull()
|| _trayIconImageBack.width() != _trayIconSize
|| iconThemeName != _trayIconThemeName
|| iconName != _trayIconName) {
_trayIconImageBack = Core::App().logo();
if (useSystemIcon) {
_trayIconImageBack = QIcon::fromTheme(
iconName,
QIcon::fromTheme(
"telegram",
QIcon(QPixmap::fromImage(_trayIconImageBack)))
).pixmap(_trayIconSize, _trayIconSize).toImage();
}
_trayIconImageBack = QIcon::fromTheme(
iconName,
QIcon::fromTheme(
kTrayIconName.utf16(),
QIcon(QPixmap::fromImage(_trayIconImageBack)))
).pixmap(_trayIconSize, _trayIconSize).toImage();
int w = _trayIconImageBack.width(),
auto w = _trayIconImageBack.width(),
h = _trayIconImageBack.height();
if (w != _trayIconSize || h != _trayIconSize) {
@ -132,8 +109,8 @@ QImage _trayIconImageGen(bool useSystemIcon) {
w = _trayIconImageBack.width();
h = _trayIconImageBack.height();
int perline = _trayIconImageBack.bytesPerLine();
uchar *bytes = _trayIconImageBack.bits();
const auto perline = _trayIconImageBack.bytesPerLine();
auto *bytes = _trayIconImageBack.bits();
for (int32 y = 0; y < h; ++y) {
for (int32 x = 0; x < w; ++x) {
@ -189,81 +166,66 @@ QImage _trayIconImageGen(bool useSystemIcon) {
return _trayIconImage;
}
QString _trayIconImageFile() {
const auto counter = Core::App().unreadBadge();
const auto muted = Core::App().unreadBadgeMuted();
const auto counterSlice = (counter >= 1000)
? (1000 + (counter % 100))
: counter;
QString iconThemeName = QIcon::themeName();
QString name = cWorkingDir() + qsl("tdata/ticons/icon%1_%2_%3_%4.png")
.arg(muted ? "mute" : "")
.arg(iconThemeName)
.arg(_trayIconSize)
.arg(counterSlice);
QFileInfo info(name);
if (info.exists()) return name;
QImage img = _trayIconImageGen(false);
if (img.save(name, "PNG")) return name;
QDir dir(info.absoluteDir());
if (!dir.exists()) {
dir.mkpath(dir.absolutePath());
if (img.save(name, "PNG")) return name;
}
return QString();
}
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
void loadPixbuf(QImage image) {
int w = image.width(), h = image.height(), perline = image.bytesPerLine(), s = image.byteCount();
_trayPixbufData.resize(w * h * 4);
uchar *result = (uchar*)_trayPixbufData.data(), *bytes = image.bits();
for (int32 y = 0; y < h; ++y) {
for (int32 x = 0; x < w; ++x) {
int32 offset = (y * w + x) * 4, srcoff = y * perline + x * 4;
result[offset + GTK_RED ] = bytes[srcoff + QT_RED ];
result[offset + GTK_GREEN] = bytes[srcoff + QT_GREEN];
result[offset + GTK_BLUE ] = bytes[srcoff + QT_BLUE ];
result[offset + GTK_ALPHA] = bytes[srcoff + QT_ALPHA];
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
static bool NeedTrayIconFile() {
// 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 TrayIconFileNeeded = [&] {
auto necessary = false;
const auto session = QDBusConnection::sessionBus();
const auto pid = session.interface()
->servicePid(kSNIWatcherService.utf16()).value();
const auto processName = ProcessNameByPID(QString::number(pid));
necessary = processName.endsWith(
qsl("indicator-application-service"));
if (!necessary) {
// Accessing to process name might be not allowed if the application
// is confined, thus we can just rely on the current desktop in use
necessary = DesktopEnvironment::IsUnity()
|| DesktopEnvironment::IsMATE();
}
}
if (_trayPixbuf) Libs::g_object_unref(_trayPixbuf);
_trayPixbuf = Libs::gdk_pixbuf_new_from_data(result, GDK_COLORSPACE_RGB, true, 8, w, h, w * 4, 0, 0);
return necessary;
}();
return TrayIconFileNeeded;
}
void _trayMenuCallback(GtkMenu *menu, gpointer data) {
for (int32 i = 0, l = _trayItems.size(); i < l; ++i) {
if ((void*)_trayItems.at(i).first == (void*)menu) {
QMetaObject::invokeMethod(_trayItems.at(i).second, "triggered");
}
}
static inline QString TrayIconFileTemplate() {
static const auto TempFileTemplate = AppRuntimeDirectory()
+ kTrayIconFilename.utf16();
return TempFileTemplate;
}
static gboolean _trayIconCheck(gpointer/* pIn*/) {
if (useStatusIcon && !trayIconChecked) {
if (Libs::gtk_status_icon_is_embedded(_trayIcon)) {
trayIconChecked = true;
cSetSupportTray(true);
if (Global::started()) {
Global::RefWorkMode().setForced(Global::WorkMode().value(), true);
}
if (App::wnd()) {
Notify::unreadCounterUpdated();
App::wnd()->updateTrayMenu();
}
}
}
return FALSE;
std::unique_ptr<QTemporaryFile> TrayIconFile(
const QPixmap &icon, QObject *parent) {
auto ret = std::make_unique<QTemporaryFile>(
TrayIconFileTemplate(),
parent);
ret->open();
icon.save(ret.get());
ret->close();
return ret;
}
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
bool IsSNIAvailable() {
static const auto SNIAvailable = [&] {
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
QDBusInterface systrayHost(
kSNIWatcherService.utf16(),
qsl("/StatusNotifierWatcher"),
kSNIWatcherService.utf16());
return systrayHost.isValid()
&& systrayHost
.property("IsStatusNotifierHostRegistered")
.toBool();
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
return false;
}();
return SNIAvailable;
}
quint32 djbStringHash(QString string) {
quint32 hash = 5381;
@ -278,71 +240,106 @@ quint32 djbStringHash(QString string) {
MainWindow::MainWindow(not_null<Window::Controller*> controller)
: Window::MainWindow(controller) {
connect(&_psCheckStatusIconTimer, SIGNAL(timeout()), this, SLOT(psStatusIconCheck()));
_psCheckStatusIconTimer.setSingleShot(false);
connect(&_psUpdateIndicatorTimer, SIGNAL(timeout()), this, SLOT(psUpdateIndicator()));
_psUpdateIndicatorTimer.setSingleShot(true);
}
bool MainWindow::hasTrayIcon() const {
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
return trayIcon || ((useAppIndicator || (useStatusIcon && trayIconChecked)) && (Global::WorkMode().value() != dbiwmWindowOnly));
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
return trayIcon || _sniTrayIcon;
#else
return trayIcon;
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
}
void MainWindow::psStatusIconCheck() {
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
_trayIconCheck(0);
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
if (cSupportTray() || !--_psCheckStatusIconLeft) {
_psCheckStatusIconTimer.stop();
return;
}
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
}
void MainWindow::psShowTrayMenu() {
if (!IsSNIAvailable()) {
_trayIconMenuXEmbed->popup(QCursor::pos());
}
}
void MainWindow::psTrayMenuUpdated() {
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
if (noQtTrayIcon && (useAppIndicator || useStatusIcon)) {
const QList<QAction*> &actions = trayIconMenu->actions();
if (_trayItems.isEmpty()) {
DEBUG_LOG(("Creating tray menu!"));
for (int32 i = 0, l = actions.size(); i != l; ++i) {
GtkWidget *item = Libs::gtk_menu_item_new_with_label(actions.at(i)->text().toUtf8());
Libs::gtk_menu_shell_append(Libs::gtk_menu_shell_cast(_trayMenu), item);
Libs::g_signal_connect_helper(item, "activate", G_CALLBACK(_trayMenuCallback), this);
Libs::gtk_widget_show(item);
Libs::gtk_widget_set_sensitive(item, actions.at(i)->isEnabled());
_trayItems.push_back(qMakePair(item, actions.at(i)));
}
} else {
DEBUG_LOG(("Updating tray menu!"));
for (int32 i = 0, l = actions.size(); i != l; ++i) {
if (i < _trayItems.size()) {
Libs::gtk_menu_item_set_label(reinterpret_cast<GtkMenuItem*>(_trayItems.at(i).first), actions.at(i)->text().toUtf8());
Libs::gtk_widget_set_sensitive(_trayItems.at(i).first, actions.at(i)->isEnabled());
}
}
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
if (IsSNIAvailable()) {
if (_sniTrayIcon && trayIconMenu) {
_sniTrayIcon->setContextMenu(trayIconMenu);
}
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
}
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
void MainWindow::setSNITrayIcon(
const QIcon &icon, const QPixmap &iconPixmap) {
if (!NeedTrayIconFile()) {
_sniTrayIcon->setIconByPixmap(icon);
_sniTrayIcon->setToolTipIconByPixmap(icon);
}
if (qEnvironmentVariableIsSet(kDisableTrayCounter.utf8())) {
const auto iconName = GetTrayIconName();
_sniTrayIcon->setIconByName(iconName);
_sniTrayIcon->setToolTipIconByName(iconName);
} else if (NeedTrayIconFile()) {
_trayIconFile = TrayIconFile(iconPixmap, this);
if (_trayIconFile) {
_sniTrayIcon->setIconByName(_trayIconFile->fileName());
_sniTrayIcon->setToolTipIconByName(_trayIconFile->fileName());
}
}
}
void MainWindow::attachToSNITrayIcon() {
_sniTrayIcon->setToolTipTitle(AppName.utf16());
connect(_sniTrayIcon,
&StatusNotifierItem::activateRequested,
this,
[=](const QPoint &) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
handleTrayIconActication(QSystemTrayIcon::Trigger);
});
});
connect(_sniTrayIcon,
&StatusNotifierItem::secondaryActivateRequested,
this,
[=](const QPoint &) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
handleTrayIconActication(QSystemTrayIcon::MiddleClick);
});
});
updateTrayMenu();
}
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
void MainWindow::psSetupTrayIcon() {
if (noQtTrayIcon) {
if (!cSupportTray()) return;
const auto iconPixmap = QPixmap::fromImage(TrayIconImageGen());
const auto icon = QIcon(iconPixmap);
if (IsSNIAvailable()) {
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
LOG(("Using SNI tray icon."));
if (!_sniTrayIcon) {
_sniTrayIcon = new StatusNotifierItem(
QCoreApplication::applicationName(),
this);
_sniTrayIcon->setTitle(QCoreApplication::applicationName());
setSNITrayIcon(icon, iconPixmap);
attachToSNITrayIcon();
}
updateIconCounters();
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
} else {
LOG(("Using Qt tray icon."));
if (!_trayIconMenuXEmbed) {
_trayIconMenuXEmbed = new Ui::PopupMenu(nullptr, trayIconMenu);
_trayIconMenuXEmbed->deleteOnHide(false);
}
if (!trayIcon) {
trayIcon = new QSystemTrayIcon(this);
trayIcon->setIcon(QIcon(QPixmap::fromImage(_trayIconImageGen(true))));
trayIcon->setIcon(icon);
attachToTrayIcon(trayIcon);
}
@ -356,14 +353,14 @@ void MainWindow::workmodeUpdated(DBIWorkMode mode) {
if (!cSupportTray()) return;
if (mode == dbiwmWindowOnly) {
if (noQtTrayIcon) {
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
if (useAppIndicator) {
Libs::app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_PASSIVE);
} else if (useStatusIcon) {
Libs::gtk_status_icon_set_visible(_trayIcon, false);
if (IsSNIAvailable()) {
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
if (_sniTrayIcon) {
_sniTrayIcon->setContextMenu(0);
_sniTrayIcon->deleteLater();
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
_sniTrayIcon = 0;
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
} else {
if (trayIcon) {
trayIcon->setContextMenu(0);
@ -372,35 +369,10 @@ void MainWindow::workmodeUpdated(DBIWorkMode mode) {
trayIcon = 0;
}
} else {
if (noQtTrayIcon) {
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
if (useAppIndicator) {
Libs::app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_ACTIVE);
} else if (useStatusIcon) {
Libs::gtk_status_icon_set_visible(_trayIcon, true);
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
} else {
psSetupTrayIcon();
}
psSetupTrayIcon();
}
}
void MainWindow::psUpdateIndicator() {
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
_psUpdateIndicatorTimer.stop();
_psLastIndicatorUpdate = crl::now();
QFileInfo iconFile(_trayIconImageFile());
if (iconFile.exists()) {
QByteArray path = QFile::encodeName(iconFile.absoluteFilePath()), name = QFile::encodeName(iconFile.fileName());
name = name.mid(0, name.size() - 4);
Libs::app_indicator_set_icon_full(_trayIndicator, path.constData(), name);
} else {
useAppIndicator = false;
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
}
void MainWindow::unreadCounterChangedHook() {
setWindowTitle(titleText());
updateIconCounters();
@ -410,208 +382,91 @@ void MainWindow::updateIconCounters() {
updateWindowIcon();
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
if (useUnityCount) {
if (UseUnityCount) {
const auto counter = Core::App().unreadBadge();
QVariantMap dbusUnityProperties;
if (counter > 0) {
// Gnome requires that count is a 64bit integer
dbusUnityProperties.insert("count", (qint64) ((counter > 9999) ? 9999 : (counter)));
dbusUnityProperties.insert(
"count",
(qint64) ((counter > 9999)
? 9999
: (counter)));
dbusUnityProperties.insert("count-visible", true);
} else {
dbusUnityProperties.insert("count-visible", false);
}
QDBusMessage signal = QDBusMessage::createSignal(_dbusPath, "com.canonical.Unity.LauncherEntry", "Update");
signal << "application://" + _desktopFile;
QDBusMessage signal = QDBusMessage::createSignal(
UnityCountDBusPath,
"com.canonical.Unity.LauncherEntry",
"Update");
signal << "application://" + UnityCountDesktopFile;
signal << dbusUnityProperties;
QDBusConnection::sessionBus().send(signal);
}
#endif
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
if (noQtTrayIcon) {
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
if (useAppIndicator) {
if (crl::now() > _psLastIndicatorUpdate + 1000) {
psUpdateIndicator();
} else if (!_psUpdateIndicatorTimer.isActive()) {
_psUpdateIndicatorTimer.start(100);
}
} else if (useStatusIcon && trayIconChecked) {
loadPixbuf(_trayIconImageGen(true));
Libs::gtk_status_icon_set_from_pixbuf(_trayIcon, _trayPixbuf);
const auto iconPixmap = QPixmap::fromImage(TrayIconImageGen());
const auto icon = QIcon(iconPixmap);
if (IsSNIAvailable()) {
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
if (_sniTrayIcon) {
setSNITrayIcon(icon, iconPixmap);
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
} else if (trayIcon) {
trayIcon->setIcon(QIcon(QPixmap::fromImage(_trayIconImageGen(true))));
trayIcon->setIcon(icon);
}
}
void MainWindow::LibsLoaded() {
noQtTrayIcon = !DesktopEnvironment::TryQtTrayIcon();
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
tryAppIndicator = DesktopEnvironment::PreferAppIndicatorTrayIcon();
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
qDBusRegisterMetaType<ToolTip>();
qDBusRegisterMetaType<IconPixmap>();
qDBusRegisterMetaType<IconPixmapList>();
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
LOG(("Tray Icon: Try Qt = %1, Prefer appindicator = %2").arg(Logs::b(!noQtTrayIcon)).arg(Logs::b(tryAppIndicator)));
if (noQtTrayIcon) cSetSupportTray(false);
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
useGtkBase = (Libs::gtk_init_check != nullptr)
&& (Libs::gtk_menu_new != nullptr)
&& (Libs::gtk_menu_get_type != nullptr)
&& (Libs::gtk_menu_item_new_with_label != nullptr)
&& (Libs::gtk_menu_item_set_label != nullptr)
&& (Libs::gtk_menu_shell_append != nullptr)
&& (Libs::gtk_menu_shell_get_type != nullptr)
&& (Libs::gtk_widget_show != nullptr)
&& (Libs::gtk_widget_get_toplevel != nullptr)
&& (Libs::gtk_widget_get_visible != nullptr)
&& (Libs::gtk_widget_set_sensitive != nullptr)
&& (Libs::g_type_check_instance_cast != nullptr)
&& (Libs::g_signal_connect_data != nullptr)
&& (Libs::g_object_ref_sink != nullptr)
&& (Libs::g_object_unref != nullptr);
useAppIndicator = useGtkBase
&& (Libs::app_indicator_new != nullptr)
&& (Libs::app_indicator_set_status != nullptr)
&& (Libs::app_indicator_set_menu != nullptr)
&& (Libs::app_indicator_set_icon_full != nullptr);
if (tryAppIndicator && useGtkBase && useAppIndicator) {
noQtTrayIcon = true;
cSetSupportTray(false);
if (!IsSNIAvailable()) {
_trayIconSize = 22;
}
useStatusIcon = (Libs::gdk_init_check != nullptr)
&& (Libs::gdk_pixbuf_new_from_data != nullptr)
&& (Libs::gtk_status_icon_new_from_pixbuf != nullptr)
&& (Libs::gtk_status_icon_set_from_pixbuf != nullptr)
&& (Libs::gtk_status_icon_new_from_file != nullptr)
&& (Libs::gtk_status_icon_set_from_file != nullptr)
&& (Libs::gtk_status_icon_set_title != nullptr)
&& (Libs::gtk_status_icon_set_tooltip_text != nullptr)
&& (Libs::gtk_status_icon_set_visible != nullptr)
&& (Libs::gtk_status_icon_is_embedded != nullptr)
&& (Libs::gtk_status_icon_get_geometry != nullptr)
&& (Libs::gtk_status_icon_position_menu != nullptr)
&& (Libs::gtk_menu_popup != nullptr)
&& (Libs::gtk_get_current_event_time != nullptr)
&& (Libs::g_idle_add != nullptr);
if (useStatusIcon) {
DEBUG_LOG(("Status icon api loaded!"));
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
}
void MainWindow::psCreateTrayIcon() {
if (!noQtTrayIcon) {
LOG(("Tray Icon: Using Qt tray icon, available: %1").arg(Logs::b(QSystemTrayIcon::isSystemTrayAvailable())));
cSetSupportTray(QSystemTrayIcon::isSystemTrayAvailable());
return;
}
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
if (useAppIndicator) {
DEBUG_LOG(("Trying to create AppIndicator"));
_trayMenu = Libs::gtk_menu_new();
if (_trayMenu) {
DEBUG_LOG(("Created gtk menu for appindicator!"));
QFileInfo iconFile(_trayIconImageFile());
if (iconFile.exists()) {
QByteArray path = QFile::encodeName(iconFile.absoluteFilePath());
_trayIndicator = Libs::app_indicator_new("Telegram Desktop", path.constData(), APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
if (_trayIndicator) {
LOG(("Tray Icon: Using appindicator tray icon."));
} else {
DEBUG_LOG(("Failed to app_indicator_new()!"));
}
} else {
useAppIndicator = false;
DEBUG_LOG(("Failed to create image file!"));
}
} else {
DEBUG_LOG(("Failed to gtk_menu_new()!"));
}
if (_trayMenu && _trayIndicator) {
Libs::app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_ACTIVE);
Libs::app_indicator_set_menu(_trayIndicator, Libs::gtk_menu_cast(_trayMenu));
useStatusIcon = false;
} else {
DEBUG_LOG(("AppIndicator failed!"));
useAppIndicator = false;
}
}
if (useStatusIcon) {
if (Libs::gdk_init_check(0, 0)) {
if (!_trayMenu) _trayMenu = Libs::gtk_menu_new();
if (_trayMenu) {
loadPixbuf(_trayIconImageGen(true));
_trayIcon = Libs::gtk_status_icon_new_from_pixbuf(_trayPixbuf);
if (_trayIcon) {
LOG(("Tray Icon: Using GTK status tray icon."));
Libs::g_signal_connect_helper(_trayIcon, "popup-menu", GCallback(_trayIconPopup), _trayMenu);
Libs::g_signal_connect_helper(_trayIcon, "activate", GCallback(_trayIconActivate), _trayMenu);
Libs::g_signal_connect_helper(_trayIcon, "size-changed", GCallback(_trayIconResized), _trayMenu);
Libs::gtk_status_icon_set_title(_trayIcon, "Telegram Desktop");
Libs::gtk_status_icon_set_tooltip_text(_trayIcon, "Telegram Desktop");
Libs::gtk_status_icon_set_visible(_trayIcon, true);
} else {
useStatusIcon = false;
}
} else {
useStatusIcon = false;
}
} else {
useStatusIcon = false;
}
}
if (!useStatusIcon && !useAppIndicator) {
LOG(("Tray Icon: Not able to use any tray icon :("));
if (_trayMenu) {
Libs::g_object_ref_sink(_trayMenu);
Libs::g_object_unref(_trayMenu);
_trayMenu = nullptr;
}
}
cSetSupportTray(useAppIndicator);
if (useStatusIcon) {
Libs::g_idle_add((GSourceFunc)_trayIconCheck, 0);
_psCheckStatusIconTimer.start(100);
} else {
workmodeUpdated(Global::WorkMode().value());
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
}
void MainWindow::psFirstShow() {
psCreateTrayIcon();
const auto trayAvailable = IsSNIAvailable()
|| QSystemTrayIcon::isSystemTrayAvailable();
LOG(("System tray available: %1").arg(Logs::b(trayAvailable)));
cSetSupportTray(trayAvailable);
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
if (QDBusInterface("com.canonical.Unity", "/").isValid()) {
std::vector<QString> possibleDesktopFiles = {
const std::vector<QString> possibleDesktopFiles = {
GetLauncherFilename(),
"Telegram.desktop"
};
for (auto it = possibleDesktopFiles.begin(); it != possibleDesktopFiles.end(); it++) {
if (!QStandardPaths::locate(QStandardPaths::ApplicationsLocation, *it).isEmpty()) {
_desktopFile = *it;
LOG(("Found Unity Launcher entry %1!").arg(_desktopFile));
useUnityCount = true;
for (auto it = possibleDesktopFiles.begin();
it != possibleDesktopFiles.end(); it++) {
if (!QStandardPaths::locate(
QStandardPaths::ApplicationsLocation, *it).isEmpty()) {
UnityCountDesktopFile = *it;
LOG(("Found Unity Launcher entry %1!")
.arg(UnityCountDesktopFile));
UseUnityCount = true;
break;
}
}
if (!useUnityCount) {
if (!UseUnityCount) {
LOG(("Could not get Unity Launcher entry!"));
}
_dbusPath = "/com/canonical/unity/launcherentry/" + QString::number(djbStringHash("application://" + _desktopFile));
UnityCountDBusPath = "/com/canonical/unity/launcherentry/"
+ QString::number(
djbStringHash("application://" + UnityCountDesktopFile));
} else {
LOG(("Not using Unity Launcher count."));
}
#endif
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
show();
if (cWindowPos().maximized) {
@ -619,13 +474,15 @@ void MainWindow::psFirstShow() {
setWindowState(Qt::WindowMaximized);
}
if ((cLaunchMode() == LaunchModeAutoStart && cStartMinimized()) || cStartInTray()) {
if ((cLaunchMode() == LaunchModeAutoStart && cStartMinimized())
|| cStartInTray()) {
// If I call hide() synchronously here after show() then on Ubuntu 14.04
// it will show a window frame with transparent window body, without content.
// And to be able to "Show from tray" one more hide() will be required.
crl::on_main(this, [=] {
setWindowState(Qt::WindowMinimized);
if (Global::WorkMode().value() == dbiwmTrayOnly || Global::WorkMode().value() == dbiwmWindowAndTray) {
if (Global::WorkMode().value() == dbiwmTrayOnly
|| Global::WorkMode().value() == dbiwmWindowAndTray) {
hide();
} else {
show();
@ -637,25 +494,11 @@ void MainWindow::psFirstShow() {
}
MainWindow::~MainWindow() {
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
if (_trayIcon) {
Libs::g_object_unref(_trayIcon);
_trayIcon = nullptr;
}
if (_trayPixbuf) {
Libs::g_object_unref(_trayPixbuf);
_trayPixbuf = nullptr;
}
if (_trayMenu) {
Libs::g_object_ref_sink(_trayMenu);
Libs::g_object_unref(_trayMenu);
_trayMenu = nullptr;
}
if (_trayIndicator) {
Libs::g_object_unref(_trayIndicator);
_trayIndicator = nullptr;
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
delete _sniTrayIcon;
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
delete _trayIconMenuXEmbed;
}
} // namespace Platform

View File

@ -9,7 +9,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/platform_main_window.h"
#include <QtCore/QTimer>
#include "ui/widgets/popup_menu.h"
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
#include "statusnotifieritem.h"
#include <QtCore/QTemporaryFile>
#endif
namespace Platform {
@ -21,7 +26,12 @@ public:
void psFirstShow();
virtual QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) = 0;
virtual QImage iconWithCounter(
int size,
int count,
style::color bg,
style::color fg,
bool smallIcon) = 0;
static void LibsLoaded();
@ -30,9 +40,6 @@ public:
public slots:
void psShowTrayMenu();
void psStatusIconCheck();
void psUpdateIndicator();
protected:
void unreadCounterChangedHook() override;
@ -46,17 +53,26 @@ protected:
void psTrayMenuUpdated();
void psSetupTrayIcon();
virtual void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) = 0;
virtual void placeSmallCounter(
QImage &img,
int size,
int count,
style::color bg,
const QPoint &shift,
style::color color) = 0;
private:
Ui::PopupMenu *_trayIconMenuXEmbed = nullptr;
void updateIconCounters();
void psCreateTrayIcon();
QTimer _psCheckStatusIconTimer;
int _psCheckStatusIconLeft = 100;
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
StatusNotifierItem *_sniTrayIcon = nullptr;
std::unique_ptr<QTemporaryFile> _trayIconFile = nullptr;
QTimer _psUpdateIndicatorTimer;
crl::time _psLastIndicatorUpdate = 0;
void setSNITrayIcon(const QIcon &icon, const QPixmap &iconPixmap);
void attachToSNITrayIcon();
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
};

View File

@ -44,7 +44,7 @@ namespace {
constexpr auto kDesktopFile = ":/misc/telegramdesktop.desktop"_cs;
bool XDGDesktopPortalIsPresent = false;
bool XDGDesktopPortalPresent = false;
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
void SandboxAutostart(bool autostart) {
@ -196,13 +196,13 @@ bool InSnap() {
}
bool IsXDGDesktopPortalPresent() {
return XDGDesktopPortalIsPresent;
};
return XDGDesktopPortalPresent;
}
QString CurrentExecutablePath(int argc, char *argv[]) {
QString ProcessNameByPID(const QString &pid) {
constexpr auto kMaxPath = 1024;
char result[kMaxPath] = { 0 };
auto count = readlink("/proc/self/exe", result, kMaxPath);
auto count = readlink("/proc/" + pid.toLatin1() + "/exe", result, kMaxPath);
if (count > 0) {
auto filename = QFile::decodeName(result);
auto deletedPostfix = qstr(" (deleted)");
@ -212,26 +212,53 @@ QString CurrentExecutablePath(int argc, char *argv[]) {
return filename;
}
return QString();
}
QString CurrentExecutablePath(int argc, char *argv[]) {
const auto processName = ProcessNameByPID(qsl("self"));
// Fallback to the first command line argument.
return argc ? QFile::decodeName(argv[0]) : QString();
return !processName.isEmpty()
? processName
: argc
? QFile::decodeName(argv[0])
: QString();
}
QString AppRuntimeDirectory() {
static const auto RuntimeDirectory = [&] {
auto runtimeDir = QStandardPaths::writableLocation(
QStandardPaths::RuntimeLocation);
if (InSandbox()) {
runtimeDir += qsl("/app/")
+ QString::fromLatin1(qgetenv("FLATPAK_ID"));
}
if (!QFileInfo::exists(runtimeDir)) { // non-systemd distros
runtimeDir = QDir::tempPath();
}
if (runtimeDir.isEmpty()) {
runtimeDir = qsl("/tmp/");
}
if (!runtimeDir.endsWith('/')) {
runtimeDir += '/';
}
return runtimeDir;
}();
return RuntimeDirectory;
}
QString SingleInstanceLocalServerName(const QString &hash) {
const auto runtimeDir = QStandardPaths::writableLocation(
QStandardPaths::RuntimeLocation);
if (InSandbox()) {
return runtimeDir
+ qsl("/app/")
+ QString::fromLatin1(qgetenv("FLATPAK_ID"))
+ '/' + hash;
} else if (QFileInfo::exists(runtimeDir) && InSnap()) {
return runtimeDir + '/' + hash;
} else if (QFileInfo::exists(runtimeDir)) {
return runtimeDir + '/' + hash + '-' + cGUIDStr();
} else { // non-systemd distros
return QStandardPaths::writableLocation(QStandardPaths::TempLocation)
+ '/' + hash + '-' + cGUIDStr();
if (InSandbox() || InSnap()) {
return AppRuntimeDirectory() + hash;
} else {
return AppRuntimeDirectory() + hash + '-' + cGUIDStr();
}
}
@ -381,12 +408,12 @@ void start() {
#if !defined(TDESKTOP_DISABLE_DBUS_INTEGRATION) && defined(TDESKTOP_FORCE_GTK_FILE_DIALOG)
LOG(("Checking for XDG Desktop Portal..."));
XDGDesktopPortalIsPresent = QDBusInterface(
XDGDesktopPortalPresent = QDBusInterface(
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop").isValid();
// this can give us a chance to use a proper file dialog for current session
if(XDGDesktopPortalIsPresent) {
if(XDGDesktopPortalPresent) {
LOG(("XDG Desktop Portal is present!"));
qputenv("QT_QPA_PLATFORMTHEME", "xdgdesktopportal");
} else {
@ -467,6 +494,8 @@ bool OpenSystemSettings(SystemSettingsType type) {
add("kcmshell4 phonon");
} else if (DesktopEnvironment::IsGnome()) {
add("gnome-control-center sound");
} else if (DesktopEnvironment::IsMATE()) {
add("mate-volume-control");
}
add("pavucontrol-qt");
add("pavucontrol");

View File

@ -25,8 +25,10 @@ bool InSnap();
bool IsXDGDesktopPortalPresent();
QString ProcessNameByPID(const QString &pid);
QString CurrentExecutablePath(int argc, char *argv[]);
QString AppRuntimeDirectory();
QString SingleInstanceLocalServerName(const QString &hash);
QString GetLauncherBasename();

View File

@ -548,6 +548,9 @@ void MainWindow::psShowTrayMenu() {
}
void MainWindow::psTrayMenuUpdated() {
if (trayIcon && trayIconMenu && trayIcon->contextMenu() != trayIconMenu) {
trayIcon->setContextMenu(trayIconMenu);
}
}
void MainWindow::psSetupTrayIcon() {

1
Telegram/ThirdParty/libdbusmenu-qt vendored Submodule

@ -0,0 +1 @@
Subproject commit 75afa1003c1d0f6fdfa3a76ce2db689b49f86968

View File

@ -0,0 +1,75 @@
/* 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;
}

View File

@ -0,0 +1,60 @@
/* 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

View File

@ -0,0 +1,69 @@
<!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>

View File

@ -0,0 +1,331 @@
/* 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 <QDBusInterface>
#include <QDBusServiceWatcher>
#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(id),
mTitle(QLatin1String("Test")),
mStatus(QLatin1String("Active")),
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()
{
QDBusInterface interface(QLatin1String("org.kde.StatusNotifierWatcher"),
QLatin1String("/StatusNotifierWatcher"),
QLatin1String("org.kde.StatusNotifierWatcher"),
mSessionBus);
interface.asyncCall(QLatin1String("RegisterStatusNotifierItem"), mSessionBus.baseService());
}
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::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)
{
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)
{
QDBusInterface interface(QLatin1String("org.freedesktop.Notifications"), QLatin1String("/org/freedesktop/Notifications"),
QLatin1String("org.freedesktop.Notifications"), mSessionBus);
interface.call(QLatin1String("Notify"), mTitle, (uint) 0, iconName, title,
msg, QStringList(), QVariantMap(), secs);
}
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(),
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
image.byteCount());
#else
image.sizeInBytes());
#endif
// 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;
}

View File

@ -0,0 +1,185 @@
/* 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 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);
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;
// 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

View File

@ -8,7 +8,7 @@ option(TDESKTOP_FORCE_GTK_FILE_DIALOG "Force using GTK file dialog (Linux only).
option(TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME "Disable automatic 'tg://' URL scheme handler registration." ${DESKTOP_APP_USE_PACKAGED})
option(TDESKTOP_DISABLE_NETWORK_PROXY "Disable all code for working through Socks5 or MTProxy." OFF)
option(TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION "Disable automatic '.desktop' file generation (Linux only)." ${DESKTOP_APP_USE_PACKAGED})
option(TDESKTOP_DISABLE_GTK_INTEGRATION "Disable all code for GTK integration (Linux only)." OFF)
option(TDESKTOP_DISABLE_GTK_INTEGRATION "Disable all code for GTK integration (Linux only)." ON)
option(TDESKTOP_DISABLE_DBUS_INTEGRATION "Disable all code for D-Bus integration (Linux only)." OFF)
option(TDESKTOP_USE_PACKAGED_TGVOIP "Find libtgvoip using CMake instead of bundled one." ${DESKTOP_APP_USE_PACKAGED})
option(TDESKTOP_API_TEST "Use test API credentials." OFF)
@ -48,8 +48,8 @@ if (NOT DESKTOP_APP_SPECIAL_TARGET STREQUAL "")
set(TDESKTOP_FORCE_GTK_FILE_DIALOG ON)
endif()
if (TDESKTOP_FORCE_GTK_FILE_DIALOG AND TDESKTOP_DISABLE_GTK_INTEGRATION)
message(FATAL_ERROR "Option TDESKTOP_FORCE_GTK_FILE_DIALOG conflicts with option TDESKTOP_DISABLE_GTK_INTEGRATION.")
if (TDESKTOP_FORCE_GTK_FILE_DIALOG)
set(TDESKTOP_DISABLE_GTK_INTEGRATION OFF)
endif()
if (DESKTOP_APP_DISABLE_SPELLCHECK)

View File

@ -14,7 +14,7 @@ You will need GCC 8 installed. To install them and all the required dependencies
sudo apt-get install software-properties-common -y && \
sudo apt-get install git libexif-dev liblzma-dev libz-dev libssl-dev \
libappindicator-dev libicu-dev libdee-dev libdrm-dev dh-autoreconf \
libgtk2.0-dev libice-dev libsm-dev libicu-dev libdrm-dev dh-autoreconf \
autoconf automake build-essential libass-dev libfreetype6-dev \
libgpac-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev \
libvorbis-dev libenchant-dev libxcb1-dev libxcb-image0-dev libxcb-shm0-dev \

View File

@ -52,15 +52,13 @@ parts:
- g++-8
- qtbase5-private-dev
- libmapbox-variant-dev
- libmsgsl-dev
- libayatana-appindicator3-dev
- libgtk-3-dev
- libasound2-dev
- libavcodec-dev
- libavformat-dev
- libavutil-dev
- libswscale-dev
- libswresample-dev
- libdbusmenu-qt5-dev
- liblz4-dev
- liblzma-dev
- libminizip-dev
@ -71,13 +69,13 @@ parts:
- zlib1g-dev
stage-packages:
- qt5-image-formats-plugins
- libayatana-appindicator3-1
- libasound2
- libavcodec57
- libavformat57
- libavutil55
- libswscale4
- libswresample2
- libdbusmenu-qt5-2
- liblz4-1
- liblzma5
- libminizip1
@ -116,6 +114,7 @@ parts:
- cmake
- desktop-qt5
- enchant
- gsl
- range-v3
- xxhash
@ -205,6 +204,15 @@ parts:
- --enable-relocatable
prime: [-./bin/*]
gsl:
source: https://github.com/microsoft/GSL.git
source-depth: 1
source-tag: v2.1.0
plugin: cmake
configflags:
- -DGSL_TEST=OFF
prime: [-./*]
range-v3:
source: https://github.com/ericniebler/range-v3.git
source-depth: 1