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