Bitcoin ABC 0.32.4
P2P Digital Currency
notificator.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2016 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <qt/notificator.h>
6
7#include <QApplication>
8#include <QByteArray>
9#include <QImageWriter>
10#include <QMessageBox>
11#include <QMetaType>
12#include <QStyle>
13#include <QSystemTrayIcon>
14#include <QTemporaryFile>
15#include <QVariant>
16#ifdef USE_DBUS
17#include <QDBusMetaType>
18#include <QtDBus>
19#include <cstdint>
20#endif
21#ifdef Q_OS_MAC
23#endif
24
25#ifdef USE_DBUS
26// https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least
27// 128
28const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128;
29#endif
30
31Notificator::Notificator(const QString &_programName,
32 QSystemTrayIcon *_trayIcon, QWidget *_parent)
33 : QObject(_parent), parent(_parent), programName(_programName), mode(None),
34 trayIcon(_trayIcon)
35#ifdef USE_DBUS
36 ,
37 interface(nullptr)
38#endif
39{
40 if (_trayIcon && _trayIcon->supportsMessages()) {
42 }
43#ifdef USE_DBUS
44 interface = new QDBusInterface("org.freedesktop.Notifications",
45 "/org/freedesktop/Notifications",
46 "org.freedesktop.Notifications");
47 if (interface->isValid()) {
49 }
50#endif
51#ifdef Q_OS_MAC
52 // check if users OS has support for NSUserNotification
54 ->hasUserNotificationCenterSupport()) {
56 }
57#endif
58}
59
61#ifdef USE_DBUS
62 delete interface;
63#endif
64}
65
66#ifdef USE_DBUS
67
68// Loosely based on http://www.qtcentre.org/archive/index.php/t-25879.html
69class FreedesktopImage {
70public:
71 FreedesktopImage() = default;
72 explicit FreedesktopImage(const QImage &img);
73
74 // Image to variant that can be marshalled over DBus
75 static QVariant toVariant(const QImage &img);
76
77private:
78 int width, height, stride;
79 bool hasAlpha;
80 int channels;
81 int bitsPerSample;
82 QByteArray image;
83
84 friend QDBusArgument &operator<<(QDBusArgument &a,
85 const FreedesktopImage &i);
86 friend const QDBusArgument &operator>>(const QDBusArgument &a,
87 FreedesktopImage &i);
88};
89
90Q_DECLARE_METATYPE(FreedesktopImage);
91
92// Image configuration settings
93const int CHANNELS = 4;
94const int BYTES_PER_PIXEL = 4;
95const int BITS_PER_SAMPLE = 8;
96
97FreedesktopImage::FreedesktopImage(const QImage &img)
98 : width(img.width()), height(img.height()),
99 stride(img.width() * BYTES_PER_PIXEL), hasAlpha(true), channels(CHANNELS),
100 bitsPerSample(BITS_PER_SAMPLE) {
101 // Convert 00xAARRGGBB to RGBA bytewise (endian-independent) format
102 QImage tmp = img.convertToFormat(QImage::Format_ARGB32);
103 const uint32_t *data = reinterpret_cast<const uint32_t *>(tmp.bits());
104
105 unsigned int num_pixels = width * height;
106 image.resize(num_pixels * BYTES_PER_PIXEL);
107
108 for (unsigned int ptr = 0; ptr < num_pixels; ++ptr) {
109 image[ptr * BYTES_PER_PIXEL + 0] = data[ptr] >> 16; // R
110 image[ptr * BYTES_PER_PIXEL + 1] = data[ptr] >> 8; // G
111 image[ptr * BYTES_PER_PIXEL + 2] = data[ptr]; // B
112 image[ptr * BYTES_PER_PIXEL + 3] = data[ptr] >> 24; // A
113 }
114}
115
116QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i) {
117 a.beginStructure();
118 a << i.width << i.height << i.stride << i.hasAlpha << i.bitsPerSample
119 << i.channels << i.image;
120 a.endStructure();
121 return a;
122}
123
124const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i) {
125 a.beginStructure();
126 a >> i.width >> i.height >> i.stride >> i.hasAlpha >> i.bitsPerSample >>
127 i.channels >> i.image;
128 a.endStructure();
129 return a;
130}
131
132QVariant FreedesktopImage::toVariant(const QImage &img) {
133 FreedesktopImage fimg(img);
134 return QVariant(qDBusRegisterMetaType<FreedesktopImage>(), &fimg);
135}
136
137void Notificator::notifyDBus(Class cls, const QString &title,
138 const QString &text, const QIcon &icon,
139 int millisTimeout) {
140 // https://developer.gnome.org/notification-spec/
141 // Arguments for DBus call:
142 QList<QVariant> args;
143
144 // Program Name:
145 args.append(programName);
146
147 // Replaces ID; A value of 0 means that this notification won't replace any
148 // existing notifications:
149 args.append(0U);
150
151 // Application Icon, empty string
152 args.append(QString());
153
154 // Summary
155 args.append(title);
156
157 // Body
158 args.append(text);
159
160 // Actions (none, actions are deprecated)
161 QStringList actions;
162 args.append(actions);
163
164 // Hints
165 QVariantMap hints;
166
167 // If no icon specified, set icon based on class
168 QIcon tmpicon;
169 if (icon.isNull()) {
170 QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion;
171 switch (cls) {
172 case Information:
173 sicon = QStyle::SP_MessageBoxInformation;
174 break;
175 case Warning:
176 sicon = QStyle::SP_MessageBoxWarning;
177 break;
178 case Critical:
179 sicon = QStyle::SP_MessageBoxCritical;
180 break;
181 default:
182 break;
183 }
184 tmpicon = QApplication::style()->standardIcon(sicon);
185 } else {
186 tmpicon = icon;
187 }
188 hints["icon_data"] = FreedesktopImage::toVariant(
189 tmpicon.pixmap(FREEDESKTOP_NOTIFICATION_ICON_SIZE).toImage());
190 args.append(hints);
191
192 // Timeout (in msec)
193 args.append(millisTimeout);
194
195 // "Fire and forget"
196 interface->callWithArgumentList(QDBus::NoBlock, "Notify", args);
197}
198#endif
199
200void Notificator::notifySystray(Class cls, const QString &title,
201 const QString &text, int millisTimeout) {
202 QSystemTrayIcon::MessageIcon sicon = QSystemTrayIcon::NoIcon;
203 // Set icon based on class
204 switch (cls) {
205 case Information:
206 sicon = QSystemTrayIcon::Information;
207 break;
208 case Warning:
209 sicon = QSystemTrayIcon::Warning;
210 break;
211 case Critical:
212 sicon = QSystemTrayIcon::Critical;
213 break;
214 }
215 trayIcon->showMessage(title, text, sicon, millisTimeout);
216}
217
218#ifdef Q_OS_MAC
219void Notificator::notifyMacUserNotificationCenter(const QString &title,
220 const QString &text) {
221 // icon is not supported by the user notification center yet. OSX will use
222 // the app icon.
224}
225#endif
226
227void Notificator::notify(Class cls, const QString &title, const QString &text,
228 const QIcon &icon, int millisTimeout) {
229 switch (mode) {
230#ifdef USE_DBUS
231 case Freedesktop:
232 notifyDBus(cls, title, text, icon, millisTimeout);
233 break;
234#endif
235 case QSystemTray:
236 notifySystray(cls, title, text, millisTimeout);
237 break;
238#ifdef Q_OS_MAC
240 notifyMacUserNotificationCenter(title, text);
241 break;
242#endif
243 default:
244 if (cls == Critical) {
245 // Fall back to old fashioned pop-up dialog if critical and no
246 // other notification available
247 QMessageBox::critical(parent, title, text, QMessageBox::Ok,
248 QMessageBox::Ok);
249 }
250 break;
251 }
252}
static MacNotificationHandler * instance()
void showNotification(const QString &title, const QString &text)
shows a macOS 10.8+ UserNotification in the UserNotificationCenter
QString programName
Definition: notificator.h:66
QWidget * parent
Definition: notificator.h:57
@ Information
Informational message.
Definition: notificator.h:37
@ Critical
An error occurred.
Definition: notificator.h:39
@ Warning
Notify user of potential problem.
Definition: notificator.h:38
@ UserNotificationCenter
Use the 10.8+ User Notification Center (Mac only)
Definition: notificator.h:63
@ QSystemTray
Use QSystemTrayIcon::showMessage()
Definition: notificator.h:62
@ Freedesktop
Use DBus org.freedesktop.Notifications.
Definition: notificator.h:61
void notifySystray(Class cls, const QString &title, const QString &text, int millisTimeout)
Notificator(const QString &programName, QSystemTrayIcon *trayIcon, QWidget *parent)
Create a new notificator.
Definition: notificator.cpp:31
QSystemTrayIcon * trayIcon
Definition: notificator.h:68
void notify(Class cls, const QString &title, const QString &text, const QIcon &icon=QIcon(), int millisTimeout=10000)
Show notification message.
std::ostream & operator<<(std::ostream &os, BigO const &bigO)