LCOV - code coverage report
Current view: top level - src/qt - notificator.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 0 76 0.0 %
Date: 2015-10-12 22:39:14 Functions: 0 11 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2011-2013 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           0 : 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           0 :     ,interface(0)
      44             : #endif
      45             : {
      46           0 :     if(trayicon && trayicon->supportsMessages())
      47             :     {
      48           0 :         mode = QSystemTray;
      49             :     }
      50             : #ifdef USE_DBUS
      51             :     interface = new QDBusInterface("org.freedesktop.Notifications",
      52           0 :         "/org/freedesktop/Notifications", "org.freedesktop.Notifications");
      53           0 :     if(interface->isValid())
      54             :     {
      55           0 :         mode = Freedesktop;
      56             :     }
      57             : #endif
      58             : #ifdef Q_OS_MAC
      59             :     // check if users OS has support for NSUserNotification
      60             :     if( MacNotificationHandler::instance()->hasUserNotificationCenterSupport()) {
      61             :         mode = UserNotificationCenter;
      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           0 : }
      81             : 
      82           0 : Notificator::~Notificator()
      83             : {
      84             : #ifdef USE_DBUS
      85           0 :     delete interface;
      86             : #endif
      87           0 : }
      88             : 
      89             : #ifdef USE_DBUS
      90             : 
      91             : // Loosely based on http://www.qtcentre.org/archive/index.php/t-25879.html
      92           0 : class FreedesktopImage
      93             : {
      94             : public:
      95           0 :     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           0 : 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           0 : FreedesktopImage::FreedesktopImage(const QImage &img):
     122           0 :     width(img.width()),
     123           0 :     height(img.height()),
     124           0 :     stride(img.width() * BYTES_PER_PIXEL),
     125             :     hasAlpha(true),
     126             :     channels(CHANNELS),
     127           0 :     bitsPerSample(BITS_PER_SAMPLE)
     128             : {
     129             :     // Convert 00xAARRGGBB to RGBA bytewise (endian-independent) format
     130           0 :     QImage tmp = img.convertToFormat(QImage::Format_ARGB32);
     131           0 :     const uint32_t *data = reinterpret_cast<const uint32_t*>(tmp.bits());
     132             : 
     133           0 :     unsigned int num_pixels = width * height;
     134           0 :     image.resize(num_pixels * BYTES_PER_PIXEL);
     135             : 
     136           0 :     for(unsigned int ptr = 0; ptr < num_pixels; ++ptr)
     137             :     {
     138           0 :         image[ptr*BYTES_PER_PIXEL+0] = data[ptr] >> 16; // R
     139           0 :         image[ptr*BYTES_PER_PIXEL+1] = data[ptr] >> 8;  // G
     140           0 :         image[ptr*BYTES_PER_PIXEL+2] = data[ptr];       // B
     141           0 :         image[ptr*BYTES_PER_PIXEL+3] = data[ptr] >> 24; // A
     142           0 :     }
     143           0 : }
     144             : 
     145           0 : QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i)
     146             : {
     147           0 :     a.beginStructure();
     148           0 :     a << i.width << i.height << i.stride << i.hasAlpha << i.bitsPerSample << i.channels << i.image;
     149           0 :     a.endStructure();
     150           0 :     return a;
     151             : }
     152             : 
     153           0 : const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i)
     154             : {
     155           0 :     a.beginStructure();
     156           0 :     a >> i.width >> i.height >> i.stride >> i.hasAlpha >> i.bitsPerSample >> i.channels >> i.image;
     157           0 :     a.endStructure();
     158           0 :     return a;
     159             : }
     160             : 
     161           0 : int FreedesktopImage::metaType()
     162             : {
     163           0 :     return qDBusRegisterMetaType<FreedesktopImage>();
     164             : }
     165             : 
     166           0 : QVariant FreedesktopImage::toVariant(const QImage &img)
     167             : {
     168           0 :     FreedesktopImage fimg(img);
     169           0 :     return QVariant(FreedesktopImage::metaType(), &fimg);
     170             : }
     171             : 
     172           0 : 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           0 :     args.append(programName);
     180             : 
     181             :     // Unique ID of this notification type:
     182           0 :     args.append(0U);
     183             : 
     184             :     // Application Icon, empty string
     185           0 :     args.append(QString());
     186             : 
     187             :     // Summary
     188           0 :     args.append(title);
     189             : 
     190             :     // Body
     191           0 :     args.append(text);
     192             : 
     193             :     // Actions (none, actions are deprecated)
     194             :     QStringList actions;
     195           0 :     args.append(actions);
     196             : 
     197             :     // Hints
     198           0 :     QVariantMap hints;
     199             : 
     200             :     // If no icon specified, set icon based on class
     201           0 :     QIcon tmpicon;
     202           0 :     if(icon.isNull())
     203             :     {
     204           0 :         QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion;
     205           0 :         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           0 :         tmpicon = QApplication::style()->standardIcon(sicon);
     213             :     }
     214             :     else
     215             :     {
     216           0 :         tmpicon = icon;
     217             :     }
     218           0 :     hints["icon_data"] = FreedesktopImage::toVariant(tmpicon.pixmap(FREEDESKTOP_NOTIFICATION_ICON_SIZE).toImage());
     219           0 :     args.append(hints);
     220             : 
     221             :     // Timeout (in msec)
     222           0 :     args.append(millisTimeout);
     223             : 
     224             :     // "Fire and forget"
     225           0 :     interface->callWithArgumentList(QDBus::NoBlock, "Notify", args);
     226           0 : }
     227             : #endif
     228             : 
     229           0 : void Notificator::notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
     230             : {
     231             :     Q_UNUSED(icon);
     232           0 :     QSystemTrayIcon::MessageIcon sicon = QSystemTrayIcon::NoIcon;
     233           0 :     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           0 :     trayIcon->showMessage(title, text, sicon, millisTimeout);
     240           0 : }
     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.
     292             :     MacNotificationHandler::instance()->showNotification(title, text);
     293             : }
     294             : 
     295             : #endif
     296             : 
     297           0 : void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
     298             : {
     299           0 :     switch(mode)
     300             :     {
     301             : #ifdef USE_DBUS
     302             :     case Freedesktop:
     303           0 :         notifyDBus(cls, title, text, icon, millisTimeout);
     304           0 :         break;
     305             : #endif
     306             :     case QSystemTray:
     307             :         notifySystray(cls, title, text, icon, millisTimeout);
     308             :         break;
     309             : #ifdef Q_OS_MAC
     310             :     case UserNotificationCenter:
     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           0 :         if(cls == Critical)
     320             :         {
     321             :             // Fall back to old fashioned pop-up dialog if critical and no other notification available
     322           0 :             QMessageBox::critical(parent, title, text, QMessageBox::Ok, QMessageBox::Ok);
     323             :         }
     324             :         break;
     325             :     }
     326           0 : }

Generated by: LCOV version 1.11