source: psi/trunk/src/tools/growlnotifier/growlnotifier.cpp

Last change on this file was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

File size: 11.0 KB
Line 
1/*
2 * growlnotifier.cpp - A simple Qt interface to Growl
3 *
4 * Copyright (C) 2005 Remko Troncon
5 *
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.
10 *
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.
15 *
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.
20 *
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
24 *
25 */
26
27
28/*
29 * TODO:
30 * - Write a destructor, which CFReleases all datastructures
31 * - Make the signal/slot work when Growl is fixed
32 **/
33
34extern "C" {
35 #include <CoreFoundation/CoreFoundation.h>
36 #include <Growl/Growl.h>
37}
38
39#include <qstringlist.h>
40#include <qpixmap.h>
41#include <qbuffer.h>
42
43//#include <sys/types.h>
44//#include <unistd.h>
45
46#include "growlnotifier.h"
47
48//------------------------------------------------------------------------------
49
50/*!
51 * A class for emitting a clicked signal to the interested party.
52 */
53class GrowlNotifierSignaler : public QObject
54{
55 Q_OBJECT
56
57public:
58 GrowlNotifierSignaler() { };
59 void emitNotificationClicked(void* context) { emit notificationClicked(context); }
60 void emitNotificationTimeout(void* context) { emit notificationTimedOut(context); }
61
62signals:
63 void notificationClicked(void*);
64 void notificationTimedOut(void*);
65};
66
67//------------------------------------------------------------------------------
68
69/*!
70 * Converts a QString to a CoreFoundation string, preserving Unicode.
71 *
72 * \param s the string to be converted.
73 * \return a reference to a CoreFoundation string.
74 */
75static CFStringRef qString2CFString(const QString& s)
76{
77 if (s.isNull())
78 return 0;
79
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,
84 (UInt8*) buffer,
85 s.length()*sizeof(ushort),
86 kCFStringEncodingUnicode, false);
87
88 delete buffer;
89 return result;
90}
91
92//------------------------------------------------------------------------------
93
94/*!
95 * Retrieves the values from the context.
96 *
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
103 * May be NULL.
104 */
105void getContext( CFPropertyListRef context, GrowlNotifierSignaler** signaler, const QObject** receiver, const char** clicked_slot, const char** timeout_slot, void** qcontext/*, pid_t* pid*/)
106{
107 CFDataRef data;
108
109 if (signaler) {
110 data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 0);
111 CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) signaler);
112 }
113
114 if (receiver){
115 data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 1);
116 CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) receiver);
117 }
118
119 if (clicked_slot) {
120 data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 2);
121 CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) clicked_slot);
122 }
123
124 if (timeout_slot) {
125 data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 3);
126 CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) timeout_slot);
127 }
128
129 if (qcontext) {
130 data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 4);
131 CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) qcontext);
132 }
133
134 //if (pid) {
135 // data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 5);
136 // CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) pid);
137 //}
138}
139
140//------------------------------------------------------------------------------
141
142/*!
143 * Creates a context for a notification, which will be sent back by Growl
144 * when a notification is clicked.
145 *
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
151 * May be NULL.
152 * \return the context
153 */
154CFPropertyListRef createContext( GrowlNotifierSignaler* signaler, const QObject* receiver, const char* clicked_slot, const char* timeout_slot, void* qcontext /*, pid_t pid*/)
155{
156 CFDataRef context[5];
157
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));
164
165 CFArrayRef array = CFArrayCreate( kCFAllocatorDefault,
166 (const void **)context, 5, &kCFTypeArrayCallBacks );
167
168 // Cleaning up
169 CFRelease(context[0]);
170 CFRelease(context[1]);
171 CFRelease(context[2]);
172 CFRelease(context[3]);
173 CFRelease(context[4]);
174 //CFRelease(context[5]);
175
176 return array;
177}
178
179//------------------------------------------------------------------------------
180
181/*!
182 * The callback function, used by Growl to notify that a notification was
183 * clicked.
184 * \param context the context of the notification
185 */
186void notification_clicked(CFPropertyListRef context)
187{
188 GrowlNotifierSignaler* signaler;
189 const QObject* receiver;
190 const char* slot;
191 void* qcontext;
192 //pid_t pid;
193
194 getContext(context, &signaler, &receiver, &slot, 0, &qcontext/*, &pid*/);
195
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);
200 //}
201}
202
203//------------------------------------------------------------------------------
204
205/*!
206 * The callback function, used by Growl to notify that a notification has
207 * timed out.
208 * \param context the context of the notification
209 */
210void notification_timeout(CFPropertyListRef context)
211{
212 GrowlNotifierSignaler* signaler;
213 const QObject* receiver;
214 const char* slot;
215 void* qcontext;
216 //pid_t pid;
217
218 getContext(context, &signaler, &receiver, 0, &slot, &qcontext /*, &pid*/);
219
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);
224 //}
225}
226
227//------------------------------------------------------------------------------
228
229/*!
230 * Constructs a GrowlNotifier.
231 *
232 * \param notifications the list names of all notifications that can be sent
233 * by this notifier.
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.
238 */
239GrowlNotifier::GrowlNotifier(
240 const QStringList& notifications, const QStringList& default_notifications,
241 const QString& app)
242{
243 // Initialize signaler
244 signaler_ = new GrowlNotifierSignaler();
245
246 // All Notifications
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));
252
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));
258
259 // Initialize delegate
260 InitGrowlDelegate(&delegate_);
261 if (!app.isEmpty())
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 = &notification_clicked;
269 delegate_.growlNotificationTimedOut = &notification_timeout;
270
271 // Register with Growl
272 Growl_SetDelegate(&delegate_);
273}
274
275
276/*!
277 * Sends a notification to Growl.
278 *
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
284 * click to discard.
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
289 * May be NULL.
290 */
291void 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,
295 void* qcontext)
296{
297 // Convert the image if necessary
298 CFDataRef icon = 0;
299 if (!p.isNull()) {
300 QByteArray img_data;
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());
305 }
306
307 // Convert strings
308 CFStringRef cf_title = qString2CFString(title);
309 CFStringRef cf_description = qString2CFString(description);
310 CFStringRef cf_name = qString2CFString(name);
311
312 // Do notification
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);
316
317 // Release intermediary datastructures
318 CFRelease(context);
319 if (icon)
320 CFRelease(icon);
321 if (cf_title)
322 CFRelease(cf_title);
323 if (cf_description)
324 CFRelease(cf_description);
325 if (cf_name)
326 CFRelease(cf_name);
327}
328
329//-----------------------------------------------------------------------------
330
331#include "growlnotifier.moc"
Note: See TracBrowser for help on using the repository browser.