Wladimir J. van der Laan
13 years ago
6 changed files with 337 additions and 29 deletions
@ -0,0 +1,224 @@ |
|||||||
|
#include "notificator.h" |
||||||
|
|
||||||
|
#include <QMetaType> |
||||||
|
#include <QVariant> |
||||||
|
#include <QIcon> |
||||||
|
#include <QApplication> |
||||||
|
#include <QStyle> |
||||||
|
#include <QByteArray> |
||||||
|
#include <QSystemTrayIcon> |
||||||
|
#include <QMessageBox> |
||||||
|
|
||||||
|
#ifdef QT_DBUS |
||||||
|
#include <QtDBus/QtDBus> |
||||||
|
#include <stdint.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
// https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128
|
||||||
|
const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128; |
||||||
|
|
||||||
|
Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, QWidget *parent): |
||||||
|
QObject(parent), |
||||||
|
parent(parent), |
||||||
|
programName(programName), |
||||||
|
mode(None), |
||||||
|
trayIcon(trayicon) |
||||||
|
#ifdef QT_DBUS |
||||||
|
,interface(0) |
||||||
|
#endif |
||||||
|
{ |
||||||
|
if(trayicon && trayicon->supportsMessages()) |
||||||
|
{ |
||||||
|
mode = QSystemTray; |
||||||
|
} |
||||||
|
#ifdef QT_DBUS |
||||||
|
interface = new QDBusInterface("org.freedesktop.Notifications", |
||||||
|
"/org/freedesktop/Notifications", "org.freedesktop.Notifications"); |
||||||
|
if(interface->isValid()) |
||||||
|
{ |
||||||
|
mode = Freedesktop; |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
Notificator::~Notificator() |
||||||
|
{ |
||||||
|
#ifdef QT_DBUS |
||||||
|
delete interface; |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef QT_DBUS |
||||||
|
|
||||||
|
// Loosely based on http://www.qtcentre.org/archive/index.php/t-25879.html
|
||||||
|
class FreedesktopImage |
||||||
|
{ |
||||||
|
public: |
||||||
|
FreedesktopImage() {} |
||||||
|
FreedesktopImage(const QImage &img); |
||||||
|
|
||||||
|
static int metaType(); |
||||||
|
|
||||||
|
// Image to variant that can be marshaled over DBus
|
||||||
|
static QVariant toVariant(const QImage &img); |
||||||
|
|
||||||
|
private: |
||||||
|
int width, height, stride; |
||||||
|
bool hasAlpha; |
||||||
|
int channels; |
||||||
|
int bitsPerSample; |
||||||
|
QByteArray image; |
||||||
|
|
||||||
|
friend QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i); |
||||||
|
friend const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i); |
||||||
|
}; |
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(FreedesktopImage); |
||||||
|
|
||||||
|
// Image configuration settings
|
||||||
|
const int CHANNELS = 4; |
||||||
|
const int BYTES_PER_PIXEL = 4; |
||||||
|
const int BITS_PER_SAMPLE = 8; |
||||||
|
|
||||||
|
FreedesktopImage::FreedesktopImage(const QImage &img): |
||||||
|
width(img.width()), |
||||||
|
height(img.height()), |
||||||
|
stride(img.width() * BYTES_PER_PIXEL), |
||||||
|
hasAlpha(true), |
||||||
|
channels(CHANNELS), |
||||||
|
bitsPerSample(BITS_PER_SAMPLE) |
||||||
|
{ |
||||||
|
// Convert 00xAARRGGBB to RGBA bytewise (endian-independent) format
|
||||||
|
QImage tmp = img.convertToFormat(QImage::Format_ARGB32); |
||||||
|
const uint32_t *data = reinterpret_cast<const uint32_t*>(tmp.constBits()); |
||||||
|
|
||||||
|
unsigned int num_pixels = width * height; |
||||||
|
image.resize(num_pixels * BYTES_PER_PIXEL); |
||||||
|
|
||||||
|
for(unsigned int ptr = 0; ptr < num_pixels; ++ptr) |
||||||
|
{ |
||||||
|
image[ptr*BYTES_PER_PIXEL+0] = data[ptr] >> 16; // R
|
||||||
|
image[ptr*BYTES_PER_PIXEL+1] = data[ptr] >> 8; // G
|
||||||
|
image[ptr*BYTES_PER_PIXEL+2] = data[ptr]; // B
|
||||||
|
image[ptr*BYTES_PER_PIXEL+3] = data[ptr] >> 24; // A
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i) |
||||||
|
{ |
||||||
|
a.beginStructure(); |
||||||
|
a << i.width << i.height << i.stride << i.hasAlpha << i.bitsPerSample << i.channels << i.image; |
||||||
|
a.endStructure(); |
||||||
|
return a; |
||||||
|
} |
||||||
|
|
||||||
|
const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i) |
||||||
|
{ |
||||||
|
a.beginStructure(); |
||||||
|
a >> i.width >> i.height >> i.stride >> i.hasAlpha >> i.bitsPerSample >> i.channels >> i.image; |
||||||
|
a.endStructure(); |
||||||
|
return a; |
||||||
|
} |
||||||
|
|
||||||
|
int FreedesktopImage::metaType() |
||||||
|
{ |
||||||
|
return qDBusRegisterMetaType<FreedesktopImage>(); |
||||||
|
} |
||||||
|
|
||||||
|
QVariant FreedesktopImage::toVariant(const QImage &img) |
||||||
|
{ |
||||||
|
FreedesktopImage fimg(img); |
||||||
|
return QVariant(FreedesktopImage::metaType(), &fimg); |
||||||
|
} |
||||||
|
|
||||||
|
void Notificator::notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout) |
||||||
|
{ |
||||||
|
Q_UNUSED(cls); |
||||||
|
// Arguments for DBus call:
|
||||||
|
QList<QVariant> args; |
||||||
|
|
||||||
|
// Program Name:
|
||||||
|
args.append(programName); |
||||||
|
|
||||||
|
// Unique ID of this notification type:
|
||||||
|
args.append(0U); |
||||||
|
|
||||||
|
// Application Icon, empty string
|
||||||
|
args.append(QString()); |
||||||
|
|
||||||
|
// Summary
|
||||||
|
args.append(title); |
||||||
|
|
||||||
|
// Body
|
||||||
|
args.append(text); |
||||||
|
|
||||||
|
// Actions (none, actions are deprecated)
|
||||||
|
QStringList actions; |
||||||
|
args.append(actions); |
||||||
|
|
||||||
|
// Hints
|
||||||
|
QVariantMap hints; |
||||||
|
|
||||||
|
// If no icon specified, set icon based on class
|
||||||
|
QIcon tmpicon; |
||||||
|
if(icon.isNull()) |
||||||
|
{ |
||||||
|
QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion; |
||||||
|
switch(cls) |
||||||
|
{ |
||||||
|
case Information: sicon = QStyle::SP_MessageBoxInformation; break; |
||||||
|
case Warning: sicon = QStyle::SP_MessageBoxWarning; break; |
||||||
|
case Critical: sicon = QStyle::SP_MessageBoxCritical; break; |
||||||
|
default: break; |
||||||
|
} |
||||||
|
tmpicon = QApplication::style()->standardIcon(sicon); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
tmpicon = icon; |
||||||
|
} |
||||||
|
hints["icon_data"] = FreedesktopImage::toVariant(tmpicon.pixmap(FREEDESKTOP_NOTIFICATION_ICON_SIZE).toImage()); |
||||||
|
args.append(hints); |
||||||
|
|
||||||
|
// Timeout (in msec)
|
||||||
|
args.append(millisTimeout); |
||||||
|
|
||||||
|
// "Fire and forget"
|
||||||
|
interface->callWithArgumentList(QDBus::NoBlock, "Notify", args); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
void Notificator::notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout) |
||||||
|
{ |
||||||
|
Q_UNUSED(icon); |
||||||
|
QSystemTrayIcon::MessageIcon sicon = QSystemTrayIcon::NoIcon; |
||||||
|
switch(cls) // Set icon based on class
|
||||||
|
{ |
||||||
|
case Information: sicon = QSystemTrayIcon::Information; break; |
||||||
|
case Warning: sicon = QSystemTrayIcon::Warning; break; |
||||||
|
case Critical: sicon = QSystemTrayIcon::Critical; break; |
||||||
|
} |
||||||
|
trayIcon->showMessage(title, text, sicon, millisTimeout); |
||||||
|
} |
||||||
|
|
||||||
|
void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout) |
||||||
|
{ |
||||||
|
switch(mode) |
||||||
|
{ |
||||||
|
#ifdef QT_DBUS |
||||||
|
case Freedesktop: |
||||||
|
notifyDBus(cls, title, text, icon, millisTimeout); |
||||||
|
break; |
||||||
|
#endif |
||||||
|
case QSystemTray: |
||||||
|
notifySystray(cls, title, text, icon, millisTimeout); |
||||||
|
break; |
||||||
|
default: |
||||||
|
if(cls == Critical) |
||||||
|
{ |
||||||
|
// Fall back to old fashioned popup dialog if critical and no other notification available
|
||||||
|
QMessageBox::critical(parent, title, text, QMessageBox::Ok, QMessageBox::Ok); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
#ifndef NOTIFICATOR_H |
||||||
|
#define NOTIFICATOR_H |
||||||
|
|
||||||
|
#include <QObject> |
||||||
|
#include <QIcon> |
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE |
||||||
|
class QSystemTrayIcon; |
||||||
|
#ifdef QT_DBUS |
||||||
|
class QDBusInterface; |
||||||
|
#endif |
||||||
|
QT_END_NAMESPACE |
||||||
|
|
||||||
|
// Cross-platform desktop notification client
|
||||||
|
class Notificator: public QObject |
||||||
|
{ |
||||||
|
Q_OBJECT |
||||||
|
public: |
||||||
|
// Create a new notificator
|
||||||
|
// Ownership of trayIcon is not transferred to this object
|
||||||
|
Notificator(const QString &programName=QString(), QSystemTrayIcon *trayIcon=0, QWidget *parent=0); |
||||||
|
~Notificator(); |
||||||
|
|
||||||
|
// Message class
|
||||||
|
enum Class |
||||||
|
{ |
||||||
|
Information, |
||||||
|
Warning, |
||||||
|
Critical, |
||||||
|
}; |
||||||
|
|
||||||
|
public slots: |
||||||
|
|
||||||
|
/* Show notification message.
|
||||||
|
* |
||||||
|
* cls: general message class |
||||||
|
* title: title shown with message |
||||||
|
* text: message content |
||||||
|
* icon: optional icon to show with message |
||||||
|
* millisTimeout: notification timeout in milliseconds (default 10 seconds) |
||||||
|
*/ |
||||||
|
void notify(Class cls, const QString &title, const QString &text, |
||||||
|
const QIcon &icon = QIcon(), int millisTimeout = 10000); |
||||||
|
|
||||||
|
private: |
||||||
|
QWidget *parent; |
||||||
|
enum Mode { |
||||||
|
None, |
||||||
|
Freedesktop, // Use DBus org.freedesktop.Notifications
|
||||||
|
QSystemTray, // Use QSystemTray::showMessage
|
||||||
|
}; |
||||||
|
QString programName; |
||||||
|
Mode mode; |
||||||
|
QSystemTrayIcon *trayIcon; |
||||||
|
#ifdef QT_DBUS |
||||||
|
QDBusInterface *interface; |
||||||
|
|
||||||
|
void notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout); |
||||||
|
#endif |
||||||
|
void notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout); |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // NOTIFICATOR_H
|
Loading…
Reference in new issue