diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 5c67fe279..451ccb398 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -244,6 +244,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_settings_show_name" = "Show sender's name"; "lng_settings_show_preview" = "Show message preview"; "lng_settings_use_windows" = "Use Windows notifications"; +"lng_settings_use_native_notifications" = "Use native notifications"; "lng_settings_sound_notify" = "Play sound"; "lng_settings_include_muted" = "Include muted chats in unread count"; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 01e3c90fb..3ebe4eec0 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -634,7 +634,7 @@ struct Data { bool RestoreSoundNotifyFromTray = false; bool IncludeMuted = true; DBINotifyView NotifyView = dbinvShowPreview; - bool WindowsNotifications = true; + bool NativeNotifications = false; base::Observable NotifySettingsChanged; DBIConnectionType ConnectionType = dbictAuto; @@ -741,7 +741,7 @@ DefineVar(Global, bool, DesktopNotify); DefineVar(Global, bool, RestoreSoundNotifyFromTray); DefineVar(Global, bool, IncludeMuted); DefineVar(Global, DBINotifyView, NotifyView); -DefineVar(Global, bool, WindowsNotifications); +DefineVar(Global, bool, NativeNotifications); DefineRefVar(Global, base::Observable, NotifySettingsChanged); DefineVar(Global, DBIConnectionType, ConnectionType); diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 72d5699c2..4d2577e52 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -307,7 +307,7 @@ DeclareVar(bool, DesktopNotify); DeclareVar(bool, RestoreSoundNotifyFromTray); DeclareVar(bool, IncludeMuted); DeclareVar(DBINotifyView, NotifyView); -DeclareVar(bool, WindowsNotifications); +DeclareVar(bool, NativeNotifications); DeclareRefVar(base::Observable, NotifySettingsChanged); DeclareVar(DBIConnectionType, ConnectionType); diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index 19dc70dcf..e8c6cc448 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -534,7 +534,7 @@ enum { dbiDcOption = 0x27, dbiTryIPv6 = 0x28, dbiSongVolume = 0x29, - dbiWindowsNotifications = 0x30, + dbiWindowsNotificationsOld = 0x30, dbiIncludeMuted = 0x31, dbiMegagroupSizeMax = 0x32, dbiDownloadPath = 0x33, @@ -548,6 +548,7 @@ enum { dbiModerateMode = 0x41, dbiVideoVolume = 0x42, dbiStickersRecentLimit = 0x43, + dbiNativeNotifications = 0x44, dbiEncryptedWithSalt = 333, dbiEncrypted = 444, @@ -989,12 +990,18 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version) { if (App::wnd()) App::wnd()->updateTrayMenu(); } break; - case dbiWindowsNotifications: { + case dbiWindowsNotificationsOld: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + } break; + + case dbiNativeNotifications: { qint32 v; stream >> v; if (!_checkStreamStatus(stream)) return false; - Global::SetWindowsNotifications(v == 1); + Global::SetNativeNotifications(v == 1); } break; case dbiWorkMode: { @@ -1589,7 +1596,7 @@ void _writeUserSettings() { data.stream << quint32(dbiShowingSavedGifs) << qint32(cShowingSavedGifs()); data.stream << quint32(dbiDesktopNotify) << qint32(Global::DesktopNotify()); data.stream << quint32(dbiNotifyView) << qint32(Global::NotifyView()); - data.stream << quint32(dbiWindowsNotifications) << qint32(Global::WindowsNotifications()); + data.stream << quint32(dbiNativeNotifications) << qint32(Global::NativeNotifications()); data.stream << quint32(dbiAskDownloadPath) << qint32(Global::AskDownloadPath()); data.stream << quint32(dbiDownloadPath) << (Global::AskDownloadPath() ? QString() : Global::DownloadPath()) << (Global::AskDownloadPath() ? QByteArray() : Global::DownloadPathBookmark()); data.stream << quint32(dbiCompressPastedImage) << qint32(cCompressPastedImage()); diff --git a/Telegram/SourceFiles/platform/linux/linux_libnotify.cpp b/Telegram/SourceFiles/platform/linux/linux_libnotify.cpp index ddae19ce1..13cadc086 100644 --- a/Telegram/SourceFiles/platform/linux/linux_libnotify.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_libnotify.cpp @@ -80,7 +80,11 @@ void startLibNotify() { QLibrary lib_notify; if (!loadLibrary(lib_notify, "notify", 4)) { - return; + if (!loadLibrary(lib_notify, "notify", 5)) { + if (!loadLibrary(lib_notify, "notify", 1)) { + return; + } + } } load(lib_notify, "notify_init", notify_init); diff --git a/Telegram/SourceFiles/platform/linux/linux_libs.cpp b/Telegram/SourceFiles/platform/linux/linux_libs.cpp index 92185611e..110180a4c 100644 --- a/Telegram/SourceFiles/platform/linux/linux_libs.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_libs.cpp @@ -93,10 +93,16 @@ bool setupGtkBase(QLibrary &lib_gtk) { if (!load(lib_gtk, "g_type_check_instance_cast", g_type_check_instance_cast)) return false; if (!load(lib_gtk, "g_type_check_instance_is_a", g_type_check_instance_is_a)) return false; if (!load(lib_gtk, "g_signal_connect_data", g_signal_connect_data)) return false; + if (!load(lib_gtk, "g_signal_handler_disconnect", g_signal_handler_disconnect)) return false; if (!load(lib_gtk, "g_object_ref_sink", g_object_ref_sink)) return false; if (!load(lib_gtk, "g_object_unref", g_object_unref)) return false; if (!load(lib_gtk, "g_free", g_free)) return false; + if (!load(lib_gtk, "g_list_foreach", g_list_foreach)) return false; + if (!load(lib_gtk, "g_list_free", g_list_free)) return false; + if (!load(lib_gtk, "g_list_free_full", g_list_free_full)) return false; + + if (!load(lib_gtk, "g_error_free", g_error_free)) return false; if (!load(lib_gtk, "g_slist_free", g_slist_free)) return false; DEBUG_LOG(("Library gtk functions loaded!")); @@ -170,6 +176,7 @@ f_gtk_dialog_run gtk_dialog_run = nullptr; 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; @@ -193,6 +200,10 @@ f_g_object_ref_sink g_object_ref_sink = nullptr; f_g_object_unref g_object_unref = nullptr; f_g_idle_add g_idle_add = nullptr; f_g_free g_free = nullptr; +f_g_list_foreach g_list_foreach = nullptr; +f_g_list_free g_list_free = nullptr; +f_g_list_free_full g_list_free_full = nullptr; +f_g_error_free g_error_free = nullptr; f_g_slist_free g_slist_free = nullptr; #ifndef TDESKTOP_DISABLE_UNITY_INTEGRATION f_unity_launcher_entry_set_count unity_launcher_entry_set_count = nullptr; diff --git a/Telegram/SourceFiles/platform/linux/linux_libs.h b/Telegram/SourceFiles/platform/linux/linux_libs.h index 88f1e659c..a413dabe1 100644 --- a/Telegram/SourceFiles/platform/linux/linux_libs.h +++ b/Telegram/SourceFiles/platform/linux/linux_libs.h @@ -255,6 +255,9 @@ inline gulong g_signal_connect_swapped_helper(gpointer instance, const gchar *de return g_signal_connect_data(instance, detailed_signal, c_handler, data, NULL, G_CONNECT_SWAPPED); } +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; @@ -324,6 +327,18 @@ extern f_g_idle_add g_idle_add; typedef void (*f_g_free)(gpointer mem); extern f_g_free g_free; +typedef void (*f_g_list_foreach)(GList *list, GFunc func, gpointer user_data); +extern f_g_list_foreach g_list_foreach; + +typedef void (*f_g_list_free)(GList *list); +extern f_g_list_free g_list_free; + +typedef void (*f_g_list_free_full)(GList *list, GDestroyNotify free_func); +extern f_g_list_free_full g_list_free_full; + +typedef void (*f_g_error_free)(GError *error); +extern f_g_error_free g_error_free; + typedef void (*f_g_slist_free)(GSList *list); extern f_g_slist_free g_slist_free; diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 9559058f5..42e825463 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "platform/linux/main_window_linux.h" #include "platform/linux/linux_libs.h" +#include "platform/platform_notifications_manager.h" #include "mainwindow.h" #include "application.h" #include "lang.h" @@ -355,6 +356,10 @@ void MainWindow::psUpdateCounter() { } } +bool MainWindow::psHasNativeNotifications() { + return Notifications::supported(); +} + void MainWindow::LibsLoaded() { QString cdesktop = QString(getenv("XDG_CURRENT_DESKTOP")).toLower(); noQtTrayIcon = (cdesktop == qstr("pantheon")) || (cdesktop == qstr("gnome")); diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index a7cee027e..f5b2aa2e6 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -58,9 +58,7 @@ public: void psUpdateCounter(); - bool psHasNativeNotifications() { - return false; - } + bool psHasNativeNotifications(); virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0; diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 5ef3467cf..961482bd4 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/notifications_utilities.h" #include "platform/linux/linux_libnotify.h" #include "platform/linux/linux_libs.h" +#include "lang.h" namespace Platform { namespace Notifications { @@ -31,7 +32,7 @@ namespace { NeverFreedPointer ManagerInstance; -bool supported() { +bool LibNotifyLoaded() { return (Libs::notify_init != nullptr) && (Libs::notify_uninit != nullptr) && (Libs::notify_is_initted != nullptr) @@ -43,7 +44,7 @@ bool supported() { && (Libs::notify_notification_update != nullptr) && (Libs::notify_notification_show != nullptr) // && (Libs::notify_notification_set_app_name != nullptr) -// && (Libs::notify_notification_set_timeout != nullptr) + && (Libs::notify_notification_set_timeout != nullptr) // && (Libs::notify_notification_set_category != nullptr) // && (Libs::notify_notification_set_urgency != nullptr) // && (Libs::notify_notification_set_icon_from_pixbuf != nullptr) @@ -52,23 +53,139 @@ bool supported() { // && (Libs::notify_notification_set_hint_int32 != nullptr) // && (Libs::notify_notification_set_hint_uint32 != nullptr) // && (Libs::notify_notification_set_hint_double != nullptr) -// && (Libs::notify_notification_set_hint_string != nullptr) + && (Libs::notify_notification_set_hint_string != nullptr) // && (Libs::notify_notification_set_hint_byte != nullptr) // && (Libs::notify_notification_set_hint_byte_array != nullptr) // && (Libs::notify_notification_clear_hints != nullptr) -// && (Libs::notify_notification_add_action != nullptr) -// && (Libs::notify_notification_clear_actions != nullptr) + && (Libs::notify_notification_add_action != nullptr) + && (Libs::notify_notification_clear_actions != nullptr) && (Libs::notify_notification_close != nullptr) && (Libs::notify_notification_get_closed_reason != nullptr) + && (Libs::g_object_ref_sink != nullptr) && (Libs::g_object_unref != nullptr) + && (Libs::g_list_free_full != nullptr) + && (Libs::g_error_free != nullptr) + && (Libs::g_signal_connect_data != nullptr) + && (Libs::g_signal_handler_disconnect != nullptr) // && (Libs::gdk_pixbuf_new_from_data != nullptr) && (Libs::gdk_pixbuf_new_from_file != nullptr); } +QString escapeNotificationHtml(QString text) { + text = text.replace(QChar('<'), qstr("<")); + text = text.replace(QChar('>'), qstr(">")); + text = text.replace(QChar('&'), qstr("&")); + return text; +} + +class NotificationData { +public: + NotificationData(const QString &title, const QString &body, PeerId peerId, MsgId msgId) + : _data(Libs::notify_notification_new(title.toUtf8().constData(), body.toUtf8().constData(), nullptr)) + , _peerId(peerId) + , _msgId(msgId) { + if (valid()) { + init(); + } + } + bool valid() const { + return (_data != nullptr); + } + NotificationData(const NotificationData &other) = delete; + NotificationData &operator=(const NotificationData &other) = delete; + NotificationData(NotificationData &&other) = delete; + NotificationData &operator=(NotificationData &&other) = delete; + + void setImage(const QString &imagePath) { + auto imagePathNative = QFile::encodeName(imagePath); + if (auto pixbuf = Libs::gdk_pixbuf_new_from_file(imagePathNative.constData(), nullptr)) { + Libs::notify_notification_set_image_from_pixbuf(_data, pixbuf); + Libs::g_object_unref(Libs::g_object_cast(pixbuf)); + } + } + bool show() { + if (valid()) { + GError *error = nullptr; + Libs::notify_notification_show(_data, &error); + if (!error) { + return true; + } + + logError(error); + } + return false; + } + + bool close() { + if (valid()) { + GError *error = nullptr; + Libs::notify_notification_close(_data, &error); + if (!error) { + return true; + } + + logError(error); + } + return false; + } + + ~NotificationData() { + if (valid()) { + if (_handlerId > 0) { + Libs::g_signal_handler_disconnect(Libs::g_object_cast(_data), _handlerId); + } + Libs::notify_notification_clear_actions(_data); + Libs::g_object_unref(Libs::g_object_cast(_data)); + } + } + +private: + void init() { + _handlerId = Libs::g_signal_connect_helper(Libs::g_object_cast(_data), "closed", G_CALLBACK(NotificationData::notificationClosed), this); + Libs::notify_notification_set_timeout(_data, Libs::NOTIFY_EXPIRES_DEFAULT); + + if (auto manager = ManagerInstance.data()) { + if (manager->hasActionsSupport()) { + Libs::notify_notification_add_action(_data, "default", lang(lng_context_reply_msg).toUtf8().constData(), NotificationData::notificationClicked, this, nullptr); + } + } + } + void onClose() { + if (auto manager = ManagerInstance.data()) { + manager->clearNotification(_peerId, _msgId); + } + } + void onClick() { + if (auto manager = ManagerInstance.data()) { + manager->notificationActivated(_peerId, _msgId); + } + } + + void logError(GError *error) { + LOG(("LibNotify Error: domain %1, code %2, message '%3'").arg(error->domain).arg(error->code).arg(QString::fromUtf8(error->message))); + Libs::g_error_free(error); + } + + static void notificationClosed(Libs::NotifyNotification *notification, gpointer data) { + static_cast(data)->onClose(); + } + static void notificationClicked(Libs::NotifyNotification *notification, char *action, gpointer data) { + static_cast(data)->onClick(); + } + + Libs::NotifyNotification *_data = nullptr; + PeerId _peerId = 0; + MsgId _msgId = 0; + gulong _handlerId = 0; + +}; + +using Notification = QSharedPointer; + } // namespace void start() { - if (supported()) { + if (LibNotifyLoaded()) { if (Libs::notify_is_initted() || Libs::notify_init("Telegram Desktop")) { ManagerInstance.makeIfNull(); } @@ -76,7 +193,14 @@ void start() { } Manager *manager() { - return ManagerInstance.data(); + if (Global::NativeNotifications()) { + return ManagerInstance.data(); + } + return nullptr; +} + +bool supported() { + return ManagerInstance.data() != nullptr; } void finish() { @@ -88,131 +212,214 @@ void finish() { class Manager::Impl { public: + Impl(); + void showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, bool showUserpic, const QString &msg, bool showReplyButton); void clearAll(); void clearFromHistory(History *history); + void clearNotification(PeerId peerId, MsgId msgId); + + bool hasPoorSupport() const { + return _poorSupported; + } + bool hasActionsSupport() const { + return _actionsSupported; + } private: - static void notificationClosed(Libs::NotifyNotification *notification, gpointer data); - void clearNotification(Libs::NotifyNotification *notification); + void showNextNotification(); - using Notifications = QMap>; + struct QueuedNotification { + PeerData *peer = nullptr; + MsgId msgId = 0; + QString title; + QString body; + bool showUserpic = false; + }; + + using QueuedNotifications = QList; + QueuedNotifications _queuedNotifications; + + using Notifications = QMap>; Notifications _notifications; - using NotificationsData = QMap>; - NotificationsData _notificationsData; - Window::Notifications::CachedUserpics _cachedUserpics; + bool _actionsSupported = false; + bool _poorSupported = true; }; -void Manager::Impl::notificationClosed(Libs::NotifyNotification *notification, gpointer data) { - if (auto manager = ManagerInstance.data()) { - manager->_impl->clearNotification(notification); - } +void FreeCapability(void *ptr, void *data) { + Libs::g_free(ptr); } -void Manager::Impl::clearNotification(Libs::NotifyNotification *notification) { - auto dataIt = _notificationsData.find(notification); - if (dataIt == _notificationsData.cend()) { - return; +Manager::Impl::Impl() { + if (auto capabilities = Libs::notify_get_server_caps()) { + for (auto capability = capabilities; capability; capability = capability->next) { + if (QString::fromUtf8(static_cast(capability->data)) == qstr("actions")) { + _actionsSupported = true; + break; + } + } + Libs::g_list_free_full(capabilities, g_free); + Libs::g_list_free(capabilities); } - auto peerId = dataIt->first; - auto msgId = dataIt->second; - _notificationsData.erase(dataIt); - - auto i = _notifications.find(peerId); - if (i != _notifications.cend()) { - auto it = i.value().find(msgId); - if (it != i.value().cend()) { - Libs::g_object_unref(Libs::g_object_cast(it.value())); - i.value().erase(it); - if (i.value().isEmpty()) { - _notifications.erase(i); + // Unity and other Notify OSD users handle desktop notifications + // extremely poor, even without the ability to close() them. + gchar *name = nullptr; + if (Libs::notify_get_server_info(&name, nullptr, nullptr, nullptr)) { + if (name) { + auto serverName = QString::fromUtf8(static_cast(name)); + LOG(("Notifications Server: %1").arg(serverName)); + if (serverName == qstr("notify-osd")) { + _actionsSupported = false; } + Libs::g_free(name); } } + if (!_actionsSupported) { + _poorSupported = true; + } } void Manager::Impl::showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, bool showUserpic, const QString &msg, bool showReplyButton) { - auto titleUtf8 = title.toUtf8(); - auto bodyUtf8 = (subtitle.isEmpty() ? msg : ("" + subtitle + "\n" + msg)).toUtf8(); - auto notification = Libs::notify_notification_new(titleUtf8.constData(), bodyUtf8.constData(), nullptr); - if (!notification) { + auto titleText = escapeNotificationHtml(title); + auto bodyText = subtitle.isEmpty() ? escapeNotificationHtml(msg) : ("" + escapeNotificationHtml(subtitle) + "\n" + escapeNotificationHtml(msg)); + + QueuedNotification notification; + notification.peer = peer; + notification.msgId = msgId; + notification.title = titleText; + notification.body = bodyText; + notification.showUserpic = showUserpic; + _queuedNotifications.push_back(notification); + + showNextNotification(); +} + +void Manager::Impl::showNextNotification() { + // Show only one notification at a time in Unity / Notify OSD. + if (_poorSupported) { + for (auto b = _notifications.begin(); !_notifications.isEmpty() && b->isEmpty();) { + _notifications.erase(b); + } + if (!_notifications.isEmpty()) { + return; + } + } + + QueuedNotification data; + while (!_queuedNotifications.isEmpty()) { + data = _queuedNotifications.front(); + _queuedNotifications.pop_front(); + if (data.peer) { + break; + } + } + if (!data.peer) { return; } - Libs::g_signal_connect_helper(Libs::g_object_cast(notification), "closed", G_CALLBACK(Manager::Impl::notificationClosed), peer); + auto peerId = data.peer->id; + auto msgId = data.msgId; + auto notification = MakeShared(data.title, data.body, peerId, msgId); + if (!notification->valid()) { + return; + } StorageKey key; - if (showUserpic) { - key = peer->userpicUniqueKey(); + if (data.showUserpic) { + key = data.peer->userpicUniqueKey(); } else { key = StorageKey(0, 0); } - auto userpicPath = _cachedUserpics.get(key, peer); - auto userpicPathNative = QFile::encodeName(userpicPath); - if (auto pixbuf = Libs::gdk_pixbuf_new_from_file(userpicPathNative.constData(), nullptr)) { - Libs::notify_notification_set_image_from_pixbuf(notification, pixbuf); - Libs::g_object_unref(Libs::g_object_cast(pixbuf)); - } + notification->setImage(_cachedUserpics.get(key, data.peer)); - auto i = _notifications.find(peer->id); + auto i = _notifications.find(peerId); if (i != _notifications.cend()) { auto j = i->find(msgId); if (j != i->cend()) { auto oldNotification = j.value(); i->erase(j); - _notificationsData.remove(oldNotification); - Libs::notify_notification_close(oldNotification, nullptr); - Libs::g_object_unref(Libs::g_object_cast(oldNotification)); - i = _notifications.find(peer->id); + oldNotification->close(); + i = _notifications.find(peerId); } } if (i == _notifications.cend()) { - i = _notifications.insert(peer->id, QMap()); + i = _notifications.insert(peerId, QMap()); } - auto result = Libs::notify_notification_show(notification, nullptr); - if (!result) { - Libs::g_object_unref(Libs::g_object_cast(notification)); - i = _notifications.find(peer->id); - if (i != _notifications.cend() && i->isEmpty()) _notifications.erase(i); - return; + _notifications[peerId].insert(msgId, notification); + if (!notification->show()) { + i = _notifications.find(peerId); + if (i != _notifications.cend()) { + i->remove(msgId); + if (i->isEmpty()) _notifications.erase(i); + } + showNextNotification(); } - _notifications[peer->id].insert(msgId, notification); - _notificationsData.insert(notification, qMakePair(peer->id, msgId)); } void Manager::Impl::clearAll() { - _notificationsData.clear(); + _queuedNotifications.clear(); auto temp = createAndSwap(_notifications); for_const (auto ¬ifications, temp) { for_const (auto notification, notifications) { - Libs::notify_notification_close(notification, nullptr); - Libs::g_object_unref(Libs::g_object_cast(notification)); + notification->close(); } } } void Manager::Impl::clearFromHistory(History *history) { + for (auto i = _queuedNotifications.begin(); i != _queuedNotifications.end();) { + if (i->peer == history->peer) { + i = _queuedNotifications.erase(i); + } else { + ++i; + } + } + auto i = _notifications.find(history->peer->id); if (i != _notifications.cend()) { auto temp = createAndSwap(i.value()); _notifications.erase(i); for_const (auto notification, temp) { - _notificationsData.remove(notification); - Libs::notify_notification_close(notification, nullptr); - Libs::g_object_unref(Libs::g_object_cast(notification)); + notification->close(); } } + + showNextNotification(); +} + +void Manager::Impl::clearNotification(PeerId peerId, MsgId msgId) { + auto i = _notifications.find(peerId); + if (i != _notifications.cend()) { + i.value().remove(msgId); + if (i.value().isEmpty()) { + _notifications.erase(i); + } + } + + showNextNotification(); } Manager::Manager() : _impl(std_::make_unique()) { } +void Manager::clearNotification(PeerId peerId, MsgId msgId) { + _impl->clearNotification(peerId, msgId); +} + +bool Manager::hasPoorSupport() const { + return _impl->hasPoorSupport(); +} + +bool Manager::hasActionsSupport() const { + return _impl->hasActionsSupport(); +} + Manager::~Manager() = default; void Manager::doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, bool showUserpic, const QString &msg, bool showReplyButton) { diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h index e7b7e0683..38672584a 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h @@ -29,6 +29,7 @@ class Manager; void start(); Manager *manager(); +bool supported(); void finish(); inline void defaultNotificationShown(QWidget *widget) { @@ -37,6 +38,11 @@ inline void defaultNotificationShown(QWidget *widget) { class Manager : public Window::Notifications::NativeManager { public: Manager(); + + void clearNotification(PeerId peerId, MsgId msgId); + bool hasPoorSupport() const; + bool hasActionsSupport() const; + ~Manager(); protected: diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index 7294ef2be..489e7b637 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "platform/win/main_window_win.h" -#include "platform/win/notifications_manager_win.h" +#include "platform/platform_notifications_manager.h" #include "platform/win/windows_dlls.h" #include "window/notifications_manager.h" #include "mainwindow.h" diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp index 417f18604..c5746d3a3 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp @@ -321,7 +321,7 @@ void start() { } Manager *manager() { - if (Global::WindowsNotifications()) { + if (Global::NativeNotifications()) { return ManagerInstance.data(); } return nullptr; diff --git a/Telegram/SourceFiles/settings/settings_notifications_widget.cpp b/Telegram/SourceFiles/settings/settings_notifications_widget.cpp index 6dd4b8f93..03370744b 100644 --- a/Telegram/SourceFiles/settings/settings_notifications_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications_widget.cpp @@ -60,11 +60,20 @@ void NotificationsWidget::createControls() { } addChildRow(_playSound, margin, lang(lng_settings_sound_notify), SLOT(onPlaySound()), Global::SoundNotify()); addChildRow(_includeMuted, margin, lang(lng_settings_include_muted), SLOT(onIncludeMuted()), Global::IncludeMuted()); + + QString nativeNotificationsLabel; #ifdef Q_OS_WIN if (App::wnd()->psHasNativeNotifications()) { - addChildRow(_windowsNative, margin, lang(lng_settings_use_windows), SLOT(onWindowsNative()), Global::WindowsNotifications()); + nativeNotificationsLabel = lang(lng_settings_use_windows); + } +#elif defined Q_OS_LINUX64 || defined Q_OS_LINUX32 + if (App::wnd()->psHasNativeNotifications()) { + nativeNotificationsLabel = lang(lng_settings_use_native_notifications); } #endif // Q_OS_WIN + if (!nativeNotificationsLabel.isEmpty()) { + addChildRow(_nativeNotifications, margin, nativeNotificationsLabel, SLOT(onNativeNotifications()), Global::NativeNotifications()); + } } void NotificationsWidget::onDesktopNotifications() { @@ -132,13 +141,13 @@ void NotificationsWidget::viewParamUpdated() { } } -void NotificationsWidget::onWindowsNative() { - if (Global::WindowsNotifications() == _windowsNative->checked()) { +void NotificationsWidget::onNativeNotifications() { + if (Global::NativeNotifications() == _nativeNotifications->checked()) { return; } Window::Notifications::manager()->clearAllFast(); - Global::SetWindowsNotifications(_windowsNative->checked()); + Global::SetNativeNotifications(_nativeNotifications->checked()); Local::writeUserSettings(); } diff --git a/Telegram/SourceFiles/settings/settings_notifications_widget.h b/Telegram/SourceFiles/settings/settings_notifications_widget.h index 7625a76b7..3c68f1c34 100644 --- a/Telegram/SourceFiles/settings/settings_notifications_widget.h +++ b/Telegram/SourceFiles/settings/settings_notifications_widget.h @@ -34,7 +34,7 @@ private slots: void onDesktopNotifications(); void onShowSenderName(); void onShowMessagePreview(); - void onWindowsNative(); + void onNativeNotifications(); void onPlaySound(); void onIncludeMuted(); @@ -46,7 +46,7 @@ private: ChildWidget _desktopNotifications = { nullptr }; ChildWidget> _showSenderName = { nullptr }; ChildWidget> _showMessagePreview = { nullptr }; - ChildWidget _windowsNative = { nullptr }; + ChildWidget _nativeNotifications = { nullptr }; ChildWidget _playSound = { nullptr }; ChildWidget _includeMuted = { nullptr };