Fabcoin Core  0.16.2
P2P Digital Currency
notificator.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2017 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 <notificator.h>
6 
7 #include <QApplication>
8 #include <QByteArray>
9 #include <QIcon>
10 #include <QImageWriter>
11 #include <QMessageBox>
12 #include <QMetaType>
13 #include <QStyle>
14 #include <QSystemTrayIcon>
15 #include <QTemporaryFile>
16 #include <QVariant>
17 #ifdef USE_DBUS
18 #include <stdint.h>
19 #include <QtDBus>
20 #endif
21 // Include ApplicationServices.h after QtDbus to avoid redefinition of check().
22 // This affects at least OSX 10.6. See /usr/include/AssertMacros.h for details.
23 // Note: This could also be worked around using:
24 // #define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
25 #ifdef Q_OS_MAC
26 #include <ApplicationServices/ApplicationServices.h>
27 #include <macnotificationhandler.h>
28 #endif
29 
30 
31 #ifdef USE_DBUS
32 // https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128
33 const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128;
34 #endif
35 
36 Notificator::Notificator(const QString &_programName, QSystemTrayIcon *_trayIcon, QWidget *_parent) :
37  QObject(_parent),
38  parent(_parent),
39  programName(_programName),
40  mode(None),
41  trayIcon(_trayIcon)
42 #ifdef USE_DBUS
43  ,interface(0)
44 #endif
45 {
46  if(_trayIcon && _trayIcon->supportsMessages())
47  {
48  mode = QSystemTray;
49  }
50 #ifdef USE_DBUS
51  interface = new QDBusInterface("org.freedesktop.Notifications",
52  "/org/freedesktop/Notifications", "org.freedesktop.Notifications");
53  if(interface->isValid())
54  {
55  mode = Freedesktop;
56  }
57 #endif
58 #ifdef Q_OS_MAC
59  // check if users OS has support for NSUserNotification
62  }
63  else {
64  // Check if Growl is installed (based on Qt's tray icon implementation)
65  CFURLRef cfurl;
66  OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl);
67  if (status != kLSApplicationNotFoundErr) {
68  CFBundleRef bundle = CFBundleCreate(0, cfurl);
69  if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"), kCFCompareCaseInsensitive | kCFCompareBackwards) == kCFCompareEqualTo) {
70  if (CFStringHasSuffix(CFURLGetString(cfurl), CFSTR("/Growl.app/")))
71  mode = Growl13;
72  else
73  mode = Growl12;
74  }
75  CFRelease(cfurl);
76  CFRelease(bundle);
77  }
78  }
79 #endif
80 }
81 
83 {
84 #ifdef USE_DBUS
85  delete interface;
86 #endif
87 }
88 
89 #ifdef USE_DBUS
90 
91 // Loosely based on http://www.qtcentre.org/archive/index.php/t-25879.html
92 class FreedesktopImage
93 {
94 public:
95  FreedesktopImage() {}
96  FreedesktopImage(const QImage &img);
97 
98  static int metaType();
99 
100  // Image to variant that can be marshalled over DBus
101  static QVariant toVariant(const QImage &img);
102 
103 private:
104  int width, height, stride;
105  bool hasAlpha;
106  int channels;
107  int bitsPerSample;
108  QByteArray image;
109 
110  friend QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i);
111  friend const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i);
112 };
113 
114 Q_DECLARE_METATYPE(FreedesktopImage);
115 
116 // Image configuration settings
117 const int CHANNELS = 4;
118 const int BYTES_PER_PIXEL = 4;
119 const int BITS_PER_SAMPLE = 8;
120 
121 FreedesktopImage::FreedesktopImage(const QImage &img):
122  width(img.width()),
123  height(img.height()),
124  stride(img.width() * BYTES_PER_PIXEL),
125  hasAlpha(true),
126  channels(CHANNELS),
127  bitsPerSample(BITS_PER_SAMPLE)
128 {
129  // Convert 00xAARRGGBB to RGBA bytewise (endian-independent) format
130  QImage tmp = img.convertToFormat(QImage::Format_ARGB32);
131  const uint32_t *data = reinterpret_cast<const uint32_t*>(tmp.bits());
132 
133  unsigned int num_pixels = width * height;
134  image.resize(num_pixels * BYTES_PER_PIXEL);
135 
136  for(unsigned int ptr = 0; ptr < num_pixels; ++ptr)
137  {
138  image[ptr*BYTES_PER_PIXEL+0] = data[ptr] >> 16; // R
139  image[ptr*BYTES_PER_PIXEL+1] = data[ptr] >> 8; // G
140  image[ptr*BYTES_PER_PIXEL+2] = data[ptr]; // B
141  image[ptr*BYTES_PER_PIXEL+3] = data[ptr] >> 24; // A
142  }
143 }
144 
145 QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i)
146 {
147  a.beginStructure();
148  a << i.width << i.height << i.stride << i.hasAlpha << i.bitsPerSample << i.channels << i.image;
149  a.endStructure();
150  return a;
151 }
152 
153 const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i)
154 {
155  a.beginStructure();
156  a >> i.width >> i.height >> i.stride >> i.hasAlpha >> i.bitsPerSample >> i.channels >> i.image;
157  a.endStructure();
158  return a;
159 }
160 
161 int FreedesktopImage::metaType()
162 {
163  return qDBusRegisterMetaType<FreedesktopImage>();
164 }
165 
166 QVariant FreedesktopImage::toVariant(const QImage &img)
167 {
168  FreedesktopImage fimg(img);
169  return QVariant(FreedesktopImage::metaType(), &fimg);
170 }
171 
172 void Notificator::notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
173 {
174  Q_UNUSED(cls);
175  // Arguments for DBus call:
176  QList<QVariant> args;
177 
178  // Program Name:
179  args.append(programName);
180 
181  // Unique ID of this notification type:
182  args.append(0U);
183 
184  // Application Icon, empty string
185  args.append(QString());
186 
187  // Summary
188  args.append(title);
189 
190  // Body
191  args.append(text);
192 
193  // Actions (none, actions are deprecated)
194  QStringList actions;
195  args.append(actions);
196 
197  // Hints
198  QVariantMap hints;
199 
200  // If no icon specified, set icon based on class
201  QIcon tmpicon;
202  if(icon.isNull())
203  {
204  QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion;
205  switch(cls)
206  {
207  case Information: sicon = QStyle::SP_MessageBoxInformation; break;
208  case Warning: sicon = QStyle::SP_MessageBoxWarning; break;
209  case Critical: sicon = QStyle::SP_MessageBoxCritical; break;
210  default: break;
211  }
212  tmpicon = QApplication::style()->standardIcon(sicon);
213  }
214  else
215  {
216  tmpicon = icon;
217  }
218  hints["icon_data"] = FreedesktopImage::toVariant(tmpicon.pixmap(FREEDESKTOP_NOTIFICATION_ICON_SIZE).toImage());
219  args.append(hints);
220 
221  // Timeout (in msec)
222  args.append(millisTimeout);
223 
224  // "Fire and forget"
225  interface->callWithArgumentList(QDBus::NoBlock, "Notify", args);
226 }
227 #endif
228 
229 void Notificator::notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
230 {
231  Q_UNUSED(icon);
232  QSystemTrayIcon::MessageIcon sicon = QSystemTrayIcon::NoIcon;
233  switch(cls) // Set icon based on class
234  {
235  case Information: sicon = QSystemTrayIcon::Information; break;
236  case Warning: sicon = QSystemTrayIcon::Warning; break;
237  case Critical: sicon = QSystemTrayIcon::Critical; break;
238  }
239  trayIcon->showMessage(title, text, sicon, millisTimeout);
240 }
241 
242 // Based on Qt's tray icon implementation
243 #ifdef Q_OS_MAC
244 void Notificator::notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon)
245 {
246  const QString script(
247  "tell application \"%5\"\n"
248  " set the allNotificationsList to {\"Notification\"}\n" // -- Make a list of all the notification types (all)
249  " set the enabledNotificationsList to {\"Notification\"}\n" // -- Make a list of the notifications (enabled)
250  " register as application \"%1\" all notifications allNotificationsList default notifications enabledNotificationsList\n" // -- Register our script with Growl
251  " notify with name \"Notification\" title \"%2\" description \"%3\" application name \"%1\"%4\n" // -- Send a Notification
252  "end tell"
253  );
254 
255  QString notificationApp(QApplication::applicationName());
256  if (notificationApp.isEmpty())
257  notificationApp = "Application";
258 
259  QPixmap notificationIconPixmap;
260  if (icon.isNull()) { // If no icon specified, set icon based on class
261  QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion;
262  switch (cls)
263  {
264  case Information: sicon = QStyle::SP_MessageBoxInformation; break;
265  case Warning: sicon = QStyle::SP_MessageBoxWarning; break;
266  case Critical: sicon = QStyle::SP_MessageBoxCritical; break;
267  }
268  notificationIconPixmap = QApplication::style()->standardPixmap(sicon);
269  }
270  else {
271  QSize size = icon.actualSize(QSize(48, 48));
272  notificationIconPixmap = icon.pixmap(size);
273  }
274 
275  QString notificationIcon;
276  QTemporaryFile notificationIconFile;
277  if (!notificationIconPixmap.isNull() && notificationIconFile.open()) {
278  QImageWriter writer(&notificationIconFile, "PNG");
279  if (writer.write(notificationIconPixmap.toImage()))
280  notificationIcon = QString(" image from location \"file://%1\"").arg(notificationIconFile.fileName());
281  }
282 
283  QString quotedTitle(title), quotedText(text);
284  quotedTitle.replace("\\", "\\\\").replace("\"", "\\");
285  quotedText.replace("\\", "\\\\").replace("\"", "\\");
286  QString growlApp(this->mode == Notificator::Growl13 ? "Growl" : "GrowlHelperApp");
287  MacNotificationHandler::instance()->sendAppleScript(script.arg(notificationApp, quotedTitle, quotedText, notificationIcon, growlApp));
288 }
289 
290 void Notificator::notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon) {
291  // icon is not supported by the user notification center yet. OSX will use the app icon.
293 }
294 
295 #endif
296 
297 void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
298 {
299  switch(mode)
300  {
301 #ifdef USE_DBUS
302  case Freedesktop:
303  notifyDBus(cls, title, text, icon, millisTimeout);
304  break;
305 #endif
306  case QSystemTray:
307  notifySystray(cls, title, text, icon, millisTimeout);
308  break;
309 #ifdef Q_OS_MAC
311  notifyMacUserNotificationCenter(cls, title, text, icon);
312  break;
313  case Growl12:
314  case Growl13:
315  notifyGrowl(cls, title, text, icon);
316  break;
317 #endif
318  default:
319  if(cls == Critical)
320  {
321  // Fall back to old fashioned pop-up dialog if critical and no other notification available
322  QMessageBox::critical(parent, title, text, QMessageBox::Ok, QMessageBox::Ok);
323  }
324  break;
325  }
326 }
QString programName
Definition: notificator.h:65
bool hasUserNotificationCenterSupport(void)
check if OS can handle UserNotifications
Use DBus org.freedesktop.Notifications.
Definition: notificator.h:59
QWidget * parent
Definition: notificator.h:56
evm_mode mode
Definition: SmartVM.cpp:47
Notificator(const QString &programName, QSystemTrayIcon *trayIcon, QWidget *parent)
Create a new notificator.
Definition: notificator.cpp:36
Notify user of potential problem.
Definition: notificator.h:39
#define a(i)
Use the 10.8+ User Notification Center (Mac only)
Definition: notificator.h:63
void notify(Class cls, const QString &title, const QString &text, const QIcon &icon=QIcon(), int millisTimeout=10000)
Show notification message.
Informational message.
Definition: notificator.h:38
static MacNotificationHandler * instance()
An error occurred.
Definition: notificator.h:40
std::ostream & operator<<(std::ostream &_out, bytes const &_e)
Definition: CommonIO.h:80
void sendAppleScript(const QString &script)
executes AppleScript
QSystemTrayIcon * trayIcon
Definition: notificator.h:67
uint8_t const size_t const size
Definition: sha3.h:20
Use the Growl 1.2 notification system (Mac only)
Definition: notificator.h:61
std::istream & operator>>(std::istream &in, Integer &a)
Definition: integer.cpp:3604
Use QSystemTray::showMessage.
Definition: notificator.h:60
#define USE_DBUS
void notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
void showNotification(const QString &title, const QString &text)
shows a 10.8+ UserNotification in the UserNotificationCenter
Use the Growl 1.3 notification system (Mac only)
Definition: notificator.h:62
uint8_t const * data
Definition: sha3.h:19