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