2
* growlnotifier.cpp - A simple Qt interface to Growl
4
* Copyright (C) 2005 Remko Troncon
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License
8
* as published by the Free Software Foundation; either version 2
9
* of the License, or (at your option) any later version.
11
* You can also redistribute and/or modify this program under the
12
* terms of the Psi License, specified in the accompanied COPYING
13
* file, as published by the Psi Project; either dated January 1st,
14
* 2005, or (at your option) any later version.
16
* This program is distributed in the hope that it will be useful,
17
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
* GNU General Public License for more details.
21
* You should have received a copy of the GNU General Public License
22
* along with this library; if not, write to the Free Software
23
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30
* - Write a destructor, which CFReleases all datastructures
31
* - Make the signal/slot work when Growl is fixed
35
#include <CoreFoundation/CoreFoundation.h>
36
#include <Growl/Growl.h>
39
#include <qstringlist.h>
43
//#include <sys/types.h>
46
#include "growlnotifier.h"
48
//------------------------------------------------------------------------------
51
* A class for emitting a clicked signal to the interested party.
53
class GrowlNotifierSignaler : public QObject
58
GrowlNotifierSignaler() { };
59
void emitNotificationClicked(void* context) { emit notificationClicked(context); }
60
void emitNotificationTimeout(void* context) { emit notificationTimedOut(context); }
63
void notificationClicked(void*);
64
void notificationTimedOut(void*);
67
//------------------------------------------------------------------------------
70
* Converts a QString to a CoreFoundation string, preserving Unicode.
72
* \param s the string to be converted.
73
* \return a reference to a CoreFoundation string.
75
static CFStringRef qString2CFString(const QString& s)
80
ushort* buffer = new ushort[s.length()];
81
for (unsigned int i = 0; i < s.length(); ++i)
82
buffer[i] = s[i].unicode();
83
CFStringRef result = CFStringCreateWithBytes ( NULL,
85
s.length()*sizeof(ushort),
86
kCFStringEncodingUnicode, false);
92
//------------------------------------------------------------------------------
95
* Retrieves the values from the context.
97
* \param context the context
98
* \param receiver the receiving object which will be signaled when the
99
* notification is clicked. May be NULL.
100
* \param clicked_slot the slot to be signaled when the notification is clicked.
101
* \param timeout_slot the slot to be signaled when the notification isn't clicked.
102
* \param context the context which will be passed back to the slot
105
void getContext( CFPropertyListRef context, GrowlNotifierSignaler** signaler, const QObject** receiver, const char** clicked_slot, const char** timeout_slot, void** qcontext/*, pid_t* pid*/)
110
data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 0);
111
CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) signaler);
115
data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 1);
116
CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) receiver);
120
data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 2);
121
CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) clicked_slot);
125
data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 3);
126
CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) timeout_slot);
130
data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 4);
131
CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) qcontext);
135
// data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 5);
136
// CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) pid);
140
//------------------------------------------------------------------------------
143
* Creates a context for a notification, which will be sent back by Growl
144
* when a notification is clicked.
146
* \param receiver the receiving object which will be signaled when the
147
* notification is clicked. May be NULL.
148
* \param clicked_slot the slot to be signaled when the notification is clicked.
149
* \param timeout_slot the slot to be signaled when the notification isn't clicked.
150
* \param context the context which will be passed back to the slot
152
* \return the context
154
CFPropertyListRef createContext( GrowlNotifierSignaler* signaler, const QObject* receiver, const char* clicked_slot, const char* timeout_slot, void* qcontext /*, pid_t pid*/)
156
CFDataRef context[5];
158
context[0] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &signaler, sizeof(GrowlNotifierSignaler*));
159
context[1] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &receiver, sizeof(const QObject *));
160
context[2] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &clicked_slot, sizeof(const char*));
161
context[3] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &timeout_slot, sizeof(const char*));
162
context[4] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &qcontext, sizeof(void*));
163
//context[5] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &pid, sizeof(pid_t));
165
CFArrayRef array = CFArrayCreate( kCFAllocatorDefault,
166
(const void **)context, 5, &kCFTypeArrayCallBacks );
169
CFRelease(context[0]);
170
CFRelease(context[1]);
171
CFRelease(context[2]);
172
CFRelease(context[3]);
173
CFRelease(context[4]);
174
//CFRelease(context[5]);
179
//------------------------------------------------------------------------------
182
* The callback function, used by Growl to notify that a notification was
184
* \param context the context of the notification
186
void notification_clicked(CFPropertyListRef context)
188
GrowlNotifierSignaler* signaler;
189
const QObject* receiver;
194
getContext(context, &signaler, &receiver, &slot, 0, &qcontext/*, &pid*/);
196
//if (pid == getpid()) {
197
QObject::connect(signaler,SIGNAL(notificationClicked(void*)),receiver,slot);
198
signaler->emitNotificationClicked(qcontext);
199
QObject::disconnect(signaler,SIGNAL(notificationClicked(void*)),receiver,slot);
203
//------------------------------------------------------------------------------
206
* The callback function, used by Growl to notify that a notification has
208
* \param context the context of the notification
210
void notification_timeout(CFPropertyListRef context)
212
GrowlNotifierSignaler* signaler;
213
const QObject* receiver;
218
getContext(context, &signaler, &receiver, 0, &slot, &qcontext /*, &pid*/);
220
//if (pid == getpid()) {
221
QObject::connect(signaler,SIGNAL(notificationTimedOut(void*)),receiver,slot);
222
signaler->emitNotificationTimeout(qcontext);
223
QObject::disconnect(signaler,SIGNAL(notificationTimedOut(void*)),receiver,slot);
227
//------------------------------------------------------------------------------
230
* Constructs a GrowlNotifier.
232
* \param notifications the list names of all notifications that can be sent
234
* \param default_notifications the list of names of the notifications that
235
* should be enabled by default.
236
* \param app the name of the application under which the notifier should
237
* register with growl.
239
GrowlNotifier::GrowlNotifier(
240
const QStringList& notifications, const QStringList& default_notifications,
243
// Initialize signaler
244
signaler_ = new GrowlNotifierSignaler();
247
QStringList::ConstIterator it;
248
CFMutableArrayRef allNotifications = CFArrayCreateMutable(
249
kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
250
for ( it = notifications.begin(); it != notifications.end(); ++it )
251
CFArrayAppendValue(allNotifications, qString2CFString(*it));
253
// Default Notifications
254
CFMutableArrayRef defaultNotifications = CFArrayCreateMutable(
255
kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
256
for ( it = default_notifications.begin(); it != default_notifications.end(); ++it )
257
CFArrayAppendValue(defaultNotifications, qString2CFString(*it));
259
// Initialize delegate
260
InitGrowlDelegate(&delegate_);
262
delegate_.applicationName = qString2CFString(app);
263
CFTypeRef keys[] = { GROWL_NOTIFICATIONS_ALL, GROWL_NOTIFICATIONS_DEFAULT };
264
CFTypeRef values[] = { allNotifications, defaultNotifications };
265
delegate_.registrationDictionary = CFDictionaryCreate(
266
kCFAllocatorDefault, keys, values, 2,
267
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
268
delegate_.growlNotificationWasClicked = ¬ification_clicked;
269
delegate_.growlNotificationTimedOut = ¬ification_timeout;
271
// Register with Growl
272
Growl_SetDelegate(&delegate_);
277
* Sends a notification to Growl.
279
* \param name the registered name of the notification.
280
* \param title the title for the notification.
281
* \param description the description of the notification.
282
* \param icon the icon of the notification.
283
* \param sticky whether the notification should be sticky (i.e. require a
285
* \param receiver the receiving object which will be signaled when the
286
* notification is clicked. May be NULL.
287
* \param slot the slot to be signaled when the notification is clicked.
288
* \param context the context which will be passed back to the slot
291
void GrowlNotifier::notify(const QString& name, const QString& title,
292
const QString& description, const QPixmap& p, bool sticky,
293
const QObject* receiver,
294
const char* clicked_slot, const char* timeout_slot,
297
// Convert the image if necessary
301
QBuffer buffer(img_data);
302
buffer.open(IO_WriteOnly);
303
p.save(&buffer, "PNG");
304
icon = CFDataCreate( NULL, (UInt8*) img_data.data(), img_data.size());
308
CFStringRef cf_title = qString2CFString(title);
309
CFStringRef cf_description = qString2CFString(description);
310
CFStringRef cf_name = qString2CFString(name);
313
CFPropertyListRef context = createContext(signaler_, receiver, clicked_slot, timeout_slot, qcontext/*, getpid()*/);
314
Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext(
315
cf_title, cf_description, cf_name, icon, 0, sticky, context);
317
// Release intermediary datastructures
324
CFRelease(cf_description);
329
//-----------------------------------------------------------------------------
331
#include "growlnotifier.moc"