~ubuntu-branches/ubuntu/oneiric/psi/oneiric

« back to all changes in this revision

Viewing changes to src/tools/growlnotifier/growlnotifier.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2006-01-20 00:20:36 UTC
  • mfrom: (1.2.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060120002036-7nw6yo6totip0ee5
Tags: 0.10-2
* Added upstream changelog (Closes: Bug#327748)
* Mention --no-gpg and --no-gpg-agent in manpage (Closes: Bug#204416)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
34
extern "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
 */
 
53
class GrowlNotifierSignaler : public QObject
 
54
{
 
55
        Q_OBJECT
 
56
 
 
57
public: 
 
58
        GrowlNotifierSignaler() { };
 
59
        void emitNotificationClicked(void* context) { emit notificationClicked(context); }
 
60
        void emitNotificationTimeout(void* context) { emit notificationTimedOut(context); }
 
61
 
 
62
signals:
 
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
 */
 
75
static 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
 */
 
105
void 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
 */
 
154
CFPropertyListRef 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
 */
 
186
void 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
 */
 
210
void 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
 */
 
239
GrowlNotifier::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
 */
 
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, 
 
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"