Wladimir J. van der Laan
13 years ago
6 changed files with 337 additions and 29 deletions
@ -0,0 +1,224 @@
@@ -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 @@
@@ -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