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