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