Statistics
| Branch: | Tag: | Revision:

root / modules / growl_notify / growlnotifier.cpp @ 18636b4a

History | View | Annotate | Download (10.4 kB)

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
 * \class GrowlNotifier
30
 * \todo Write a destructor, which CFReleases all datastructures
31
 */ 
32
33
extern "C" {
34
#include <CoreFoundation/CoreFoundation.h>
35
#include <Growl/Growl.h>
36
}
37
38
#include <QStringList>
39
#include <QPixmap>
40
#include <QBuffer>
41
42
//#include <sys/types.h>
43
//#include <unistd.h>
44
45
#include "growlnotifier.h"
46
47
//------------------------------------------------------------------------------ 
48
49
/**
50
 * \brief Converts a QString to a CoreFoundation string, preserving Unicode.
51
 *
52
 * \param s the string to be converted.
53
 * \return a reference to a CoreFoundation string.
54
 */
55
static CFStringRef qString2CFString(const QString& s)
56
{
57
        if (s.isNull())
58
                return 0;
59
        
60
        ushort* buffer = new ushort[s.length()];
61
        for (int i = 0; i < s.length(); ++i)
62
                buffer[i] = s[i].unicode();
63
        CFStringRef result = CFStringCreateWithBytes ( NULL, 
64
                (UInt8*) buffer, 
65
                s.length()*sizeof(ushort),
66
                kCFStringEncodingUnicode, false);
67
68
        delete buffer;
69
        return result;
70
}
71
72
//------------------------------------------------------------------------------ 
73
74
/**
75
 * \brief Retrieves the values from the context.
76
 *
77
 * \param context the context
78
 * \param receiver the receiving object which will be signaled when the
79
 *        notification is clicked. May be NULL.
80
 * \param clicked_slot the slot to be signaled when the notification is clicked.
81
 * \param timeout_slot the slot to be signaled when the notification isn't clicked.
82
 * \param context the context which will be passed back to the slot
83
 *        May be NULL.
84
 */
85
void getContext( CFPropertyListRef context, GrowlNotifierSignaler** signaler, const QObject** receiver, const char** clicked_slot, const char** timeout_slot, void** qcontext/*, pid_t* pid*/)
86
{
87
        CFDataRef data;
88
89
        if (signaler) {
90
                data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 0);
91
                CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) signaler);
92
        }
93
94
        if (receiver){
95
                data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 1);
96
                CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) receiver);
97
        }
98
        
99
        if (clicked_slot) {
100
                data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 2);
101
                CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) clicked_slot);
102
        }
103
        
104
        if (timeout_slot) {
105
                data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 3);
106
                CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) timeout_slot);
107
        }
108
        
109
        if (qcontext) {
110
                data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 4);
111
                CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) qcontext);
112
        }
113
114
        //if (pid) {
115
        //        data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 5);
116
        //        CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) pid);
117
        //}
118
}
119
120
//------------------------------------------------------------------------------ 
121
122
/**
123
 * Creates a context for a notification, which will be sent back by Growl
124
 * when a notification is clicked.
125
 *
126
 * \param receiver the receiving object which will be signaled when the
127
 *        notification is clicked. May be NULL.
128
 * \param clicked_slot the slot to be signaled when the notification is clicked.
129
 * \param timeout_slot the slot to be signaled when the notification isn't clicked.
130
 * \param context the context which will be passed back to the slot
131
 *        May be NULL.
132
 * \return the context
133
 */
134
CFPropertyListRef createContext( GrowlNotifierSignaler* signaler, const QObject* receiver, const char* clicked_slot, const char* timeout_slot, void* qcontext /*, pid_t pid*/)
135
{
136
        CFDataRef context[5];
137
138
        context[0] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &signaler, sizeof(GrowlNotifierSignaler*));
139
        context[1] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &receiver, sizeof(const QObject *));
140
        context[2] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &clicked_slot, sizeof(const char*));
141
        context[3] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &timeout_slot, sizeof(const char*));
142
        context[4] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &qcontext, sizeof(void*));
143
        //context[5] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &pid, sizeof(pid_t));
144
145
        CFArrayRef array = CFArrayCreate( kCFAllocatorDefault, 
146
                (const void **)context, 5, &kCFTypeArrayCallBacks );
147
148
        // Cleaning up
149
        CFRelease(context[0]);
150
        CFRelease(context[1]);
151
        CFRelease(context[2]);
152
        CFRelease(context[3]);
153
        CFRelease(context[4]);
154
        //CFRelease(context[5]);
155
156
        return array;
157
}
158
159
//------------------------------------------------------------------------------ 
160
161
/**
162
 * The callback function, used by Growl to notify that a notification was
163
 * clicked.
164
 * \param context the context of the notification
165
 */
166
void notification_clicked(CFPropertyListRef context)
167
{
168
        GrowlNotifierSignaler* signaler;
169
        const QObject* receiver;
170
        const char* slot;
171
        void* qcontext;
172
        //pid_t pid;
173
174
        getContext(context, &signaler, &receiver, &slot, 0, &qcontext/*, &pid*/);
175
        
176
        //if (pid == getpid()) {
177
        QObject::connect(signaler,SIGNAL(notificationClicked(void*)),receiver,slot);
178
        signaler->emitNotificationClicked(qcontext);
179
        QObject::disconnect(signaler,SIGNAL(notificationClicked(void*)),receiver,slot);
180
        //}
181
}
182
183
//------------------------------------------------------------------------------ 
184
185
/**
186
 * The callback function, used by Growl to notify that a notification has
187
 * timed out.
188
 * \param context the context of the notification
189
 */
190
void notification_timeout(CFPropertyListRef context)
191
{
192
        GrowlNotifierSignaler* signaler;
193
        const QObject* receiver;
194
        const char* slot;
195
        void* qcontext;
196
197
        getContext(context, &signaler, &receiver, 0, &slot, &qcontext /*, &pid*/);
198
        
199
        if (slot != NULL)
200
        {
201
                QObject::connect(signaler,SIGNAL(notificationTimedOut(void*)),receiver,slot);
202
                signaler->emitNotificationTimeout(qcontext);
203
                QObject::disconnect(signaler,SIGNAL(notificationTimedOut(void*)),receiver,slot);
204
        }
205
}
206
207
//------------------------------------------------------------------------------ 
208
209
/**
210
 * Constructs a GrowlNotifier.
211
 *
212
 * \param notifications the list names of all notifications that can be sent
213
 *        by this notifier.
214
 * \param default_notifications the list of names of the notifications that
215
 *  should be enabled by default.
216
 * \param app the name of the application under which the notifier should 
217
 *        register with growl.
218
 */
219
GrowlNotifier::GrowlNotifier(
220
        const QStringList& notifications, const QStringList& default_notifications,
221
        const QString& app)
222
{
223
        // Initialize signaler
224
        signaler_ = new GrowlNotifierSignaler();
225
226
        // All Notifications
227
        QStringList::ConstIterator it;
228
        CFMutableArrayRef allNotifications = CFArrayCreateMutable(
229
                kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
230
        for ( it = notifications.begin(); it != notifications.end(); ++it ) 
231
                CFArrayAppendValue(allNotifications, qString2CFString(*it));
232
233
        // Default Notifications
234
        CFMutableArrayRef defaultNotifications = CFArrayCreateMutable(
235
                kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
236
        for ( it = default_notifications.begin(); it != default_notifications.end(); ++it ) 
237
                CFArrayAppendValue(defaultNotifications, qString2CFString(*it));
238
        
239
        // Initialize delegate
240
        InitGrowlDelegate(&delegate_);
241
        if (!app.isEmpty())
242
                delegate_.applicationName = qString2CFString(app);
243
        CFTypeRef keys[] = { GROWL_NOTIFICATIONS_ALL, GROWL_NOTIFICATIONS_DEFAULT };
244
        CFTypeRef values[] = { allNotifications, defaultNotifications };
245
        delegate_.registrationDictionary = CFDictionaryCreate(
246
                kCFAllocatorDefault, keys, values, 2, 
247
                &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
248
        delegate_.growlNotificationWasClicked = &notification_clicked;
249
        delegate_.growlNotificationTimedOut = &notification_timeout;
250
251
        // Register with Growl
252
        Growl_SetDelegate(&delegate_);
253
}
254
        
255
256
/**
257
 * \brief Sends a notification to Growl.
258
 *
259
 * \param name the registered name of the notification.
260
 * \param title the title for the notification.
261
 * \param description the description of the notification.
262
 * \param icon the icon of the notification.
263
 * \param sticky whether the notification should be sticky (i.e. require a 
264
 *        click to discard.
265
 * \param receiver the receiving object which will be signaled when the
266
 *        notification is clicked. May be NULL.
267
 * \param slot the slot to be signaled when the notification is clicked.
268
 * \param context the context which will be passed back to the slot
269
 *        May be NULL.
270
 */
271
void GrowlNotifier::notify(const QString& name, const QString& title, 
272
        const QString& description, const QPixmap& p, bool sticky, 
273
        const QObject* receiver, 
274
        const char* clicked_slot, const char* timeout_slot, 
275
        void* qcontext)
276
{
277
        // Convert the image if necessary
278
        CFDataRef icon = 0;
279
        if (!p.isNull()) {
280
                QByteArray img_data;
281
                QBuffer buffer(&img_data);
282
                buffer.open(QIODevice::WriteOnly);
283
                p.save(&buffer, "PNG");
284
                icon = CFDataCreate( NULL, (UInt8*) img_data.data(), img_data.size());
285
        }
286
287
        // Convert strings
288
        CFStringRef cf_title = qString2CFString(title);
289
        CFStringRef cf_description = qString2CFString(description);
290
        CFStringRef cf_name = qString2CFString(name);
291
292
        // Do notification
293
        CFPropertyListRef context = createContext(signaler_, receiver, clicked_slot, timeout_slot, qcontext/*, getpid()*/);
294
        Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext(
295
                cf_title, cf_description, cf_name, icon, 0, sticky, context);
296
        
297
        // Release intermediary datastructures
298
        CFRelease(context);
299
        if (icon) 
300
                CFRelease(icon);
301
        if (cf_title) 
302
                CFRelease(cf_title);
303
        if (cf_description) 
304
                CFRelease(cf_description);
305
        if (cf_name) 
306
                CFRelease(cf_name);
307
}
308
309
//----------------------------------------------------------------------------- 
310