Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (10.4 kB)

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