From 5862dcdf8dc6e1bb160693ad6a5ca0609ddb990a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= Date: Fri, 23 Dec 2011 23:35:52 +0100 Subject: Updated Growl notifier to work against Growl 1.3. diff --git a/SwifTools/Cocoa/CocoaUtil.h b/SwifTools/Cocoa/CocoaUtil.h new file mode 100644 index 0000000..55fc325 --- /dev/null +++ b/SwifTools/Cocoa/CocoaUtil.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +// Conversion utilities +#define NS2STDSTRING(a) (a == nil ? std::string() : std::string([a cStringUsingEncoding:NSUTF8StringEncoding])) +#define STD2NSSTRING(a) [NSString stringWithCString:a.c_str() encoding:NSUTF8StringEncoding] + +// Intrusive pointer for NSObjects +#include + +namespace boost { + inline void intrusive_ptr_add_ref(NSObject* object) { + [object retain]; + } + + inline void intrusive_ptr_release(NSObject* object) { + [object release]; + } +} diff --git a/SwifTools/Notifier/GrowlNotifier.cpp b/SwifTools/Notifier/GrowlNotifier.cpp deleted file mode 100644 index d83634d..0000000 --- a/SwifTools/Notifier/GrowlNotifier.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -// FIXME: Should we release the strings created in the constructor? - -#include - -#include -#include -#include -#include - -#pragma GCC diagnostic ignored "-Wold-style-cast" -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - -namespace { - struct Context { - Context() : callback(0) {} - Context(const boost::function& callback) : callback(new boost::function(callback)) {} - - boost::function* callback; - }; - - void processNotification(CFPropertyListRef growlContext, bool activateCallback) { - Context context; - - CFDataRef growlContextData = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) growlContext, 0); - assert(CFDataGetLength(growlContextData) == sizeof(Context)); - CFDataGetBytes(growlContextData, CFRangeMake(0, CFDataGetLength(growlContextData)), (UInt8*) &context); - - if (activateCallback && !context.callback->empty()) { - (*context.callback)(); - } - delete context.callback; - } - - void notificationClicked(CFPropertyListRef growlContext) { - processNotification(growlContext, true); - } - - void notificationTimedout(CFPropertyListRef growlContext) { - processNotification(growlContext, false); - } -} - -namespace Swift { - -GrowlNotifier::GrowlNotifier(const std::string& name) { - // All notifications - CFMutableArrayRef allNotifications = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - foreach(Type type, getAllTypes()) { - CFArrayAppendValue(allNotifications, SWIFTEN_STRING_TO_CFSTRING(typeToString(type))); - } - - // Default Notifications - CFMutableArrayRef defaultNotifications = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - foreach(Type type, getDefaultTypes()) { - CFArrayAppendValue(defaultNotifications, SWIFTEN_STRING_TO_CFSTRING(typeToString(type))); - } - - // Initialize delegate - InitGrowlDelegate(&delegate_); - delegate_.applicationName = SWIFTEN_STRING_TO_CFSTRING(name); - CFTypeRef keys[] = { GROWL_NOTIFICATIONS_ALL, GROWL_NOTIFICATIONS_DEFAULT }; - CFTypeRef values[] = { allNotifications, defaultNotifications }; - delegate_.registrationDictionary = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - delegate_.growlNotificationWasClicked = ¬ificationClicked; - delegate_.growlNotificationTimedOut = ¬ificationTimedout; - Growl_SetDelegate(&delegate_); -} - -void GrowlNotifier::showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picturePath, boost::function callback) { - ByteArray picture; - readByteArrayFromFile(picture, picturePath.string()); - - CFStringRef cfSubject = SWIFTEN_STRING_TO_CFSTRING(subject); - CFStringRef cfDescription = SWIFTEN_STRING_TO_CFSTRING(description); - CFStringRef cfName = SWIFTEN_STRING_TO_CFSTRING(typeToString(type)); - CFDataRef cfIcon = CFDataCreate( NULL, (UInt8*) vecptr(picture), picture.size()); - - Context context(callback); - CFDataRef cfContextData[1]; - cfContextData[0] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &context, sizeof(Context)); - CFArrayRef cfContext = CFArrayCreate( kCFAllocatorDefault, (const void **) cfContextData, 1, &kCFTypeArrayCallBacks ); - CFRelease(cfContextData[0]); - - Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext(cfSubject, cfDescription, cfName, cfIcon, 0, false, cfContext); - - CFRelease(cfContext); - CFRelease(cfIcon); - CFRelease(cfName); - CFRelease(cfDescription); - CFRelease(cfSubject); -} - -} diff --git a/SwifTools/Notifier/GrowlNotifier.h b/SwifTools/Notifier/GrowlNotifier.h index e2686e8..ffd717a 100644 --- a/SwifTools/Notifier/GrowlNotifier.h +++ b/SwifTools/Notifier/GrowlNotifier.h @@ -1,13 +1,11 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once -#include -#include #include #include @@ -24,8 +22,13 @@ namespace Swift { GrowlNotifier(const std::string& name); virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback); + + // Called by the delegate. Don't call. + void handleNotificationClicked(void* data); + void handleNotificationTimedOut(void* data); private: - Growl_Delegate delegate_; + class Private; + boost::shared_ptr p; }; } diff --git a/SwifTools/Notifier/GrowlNotifier.mm b/SwifTools/Notifier/GrowlNotifier.mm new file mode 100644 index 0000000..108259a --- /dev/null +++ b/SwifTools/Notifier/GrowlNotifier.mm @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2010-2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include + +#include + +#include +#include +#include +#include + +#pragma GCC diagnostic ignored "-Wold-style-cast" + +namespace { + struct Context { + Context(const boost::function& callback) : callback(new boost::function(callback)) {} + + boost::function* callback; + }; +} + +namespace Swift { + +class GrowlNotifier::Private { + public: + boost::intrusive_ptr delegate; +}; + +GrowlNotifier::GrowlNotifier(const std::string& name) { + p = boost::make_shared(); + p->delegate = boost::intrusive_ptr([[GrowlNotifierDelegate alloc] init], false); + p->delegate.get().name = STD2NSSTRING(name); + + NSMutableArray* allNotifications = [[NSMutableArray alloc] init]; + foreach(Type type, getAllTypes()) { + [allNotifications addObject: STD2NSSTRING(typeToString(type))]; + } + + NSMutableArray* defaultNotifications = [[NSMutableArray alloc] init]; + foreach(Type type, getDefaultTypes()) { + [defaultNotifications addObject: STD2NSSTRING(typeToString(type))]; + } + + p->delegate.get().registrationDictionary = [[[NSDictionary alloc] + initWithObjects: [NSArray arrayWithObjects: allNotifications, defaultNotifications, nil] + forKeys: [NSArray arrayWithObjects: GROWL_NOTIFICATIONS_ALL, GROWL_NOTIFICATIONS_DEFAULT, nil]] autorelease]; + + [GrowlApplicationBridge setGrowlDelegate: p->delegate.get()]; +} + +void GrowlNotifier::showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picturePath, boost::function callback) { + ByteArray picture; + readByteArrayFromFile(picture, picturePath.string()); + + Context* contextPtr = new Context(callback); + NSData* context = [NSData dataWithBytes: &contextPtr length: sizeof(contextPtr)]; + + [GrowlApplicationBridge + notifyWithTitle: STD2NSSTRING(subject) + description: STD2NSSTRING(description) + notificationName: STD2NSSTRING(typeToString(type)) + iconData: [NSData dataWithBytes: vecptr(picture) length: picture.size()] + priority: 0 + isSticky: NO + clickContext: context]; +} + +void GrowlNotifier::handleNotificationClicked(void* rawData) { + Context* context = *(Context**) [((NSData*) rawData) bytes]; + if (!context->callback->empty()) { + (*context->callback)(); + } + delete context; +} + +void GrowlNotifier::handleNotificationTimedOut(void* rawData) { + delete *(Context**) [((NSData*) rawData) bytes]; +} + +} diff --git a/SwifTools/Notifier/GrowlNotifierDelegate.h b/SwifTools/Notifier/GrowlNotifierDelegate.h new file mode 100644 index 0000000..7a556cc --- /dev/null +++ b/SwifTools/Notifier/GrowlNotifierDelegate.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#import + +namespace Swift { + class GrowlNotifier; +} + +@interface GrowlNotifierDelegate : NSObject { + Swift::GrowlNotifier* notifier; + NSString* name; + NSDictionary* registrationDictionary; +} + +@property (nonatomic, retain) NSDictionary* registrationDictionary; +@property (nonatomic, copy) NSString* name; + +- (NSDictionary*) registrationDictionaryForGrowl; +- (NSString *) applicationNameForGrowl; +- (void) growlNotificationWasClicked: (id) clickContext; +- (void) growlNotificationTimedOut: (id) clickContext; + + +@end diff --git a/SwifTools/Notifier/GrowlNotifierDelegate.mm b/SwifTools/Notifier/GrowlNotifierDelegate.mm new file mode 100644 index 0000000..c7f725f --- /dev/null +++ b/SwifTools/Notifier/GrowlNotifierDelegate.mm @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#import "GrowlNotifierDelegate.h" + +#include + +@implementation GrowlNotifierDelegate; + +@synthesize registrationDictionary; +@synthesize name; + +using namespace Swift; + +- (NSString *) applicationNameForGrowl { + return name; +} + +- (NSDictionary*) registrationDictionaryForGrowl { + return registrationDictionary; +} + +- (void) growlNotificationWasClicked: (id) clickContext { + notifier->handleNotificationClicked(clickContext); +} + +- (void) growlNotificationTimedOut: (id) clickContext { + notifier->handleNotificationTimedOut(clickContext); +} + +@end diff --git a/SwifTools/Notifier/SConscript b/SwifTools/Notifier/SConscript index 9ad2fd7..98b5400 100644 --- a/SwifTools/Notifier/SConscript +++ b/SwifTools/Notifier/SConscript @@ -8,7 +8,8 @@ sources = [ if swiftools_env.get("HAVE_GROWL", False) : sources += [ - "GrowlNotifier.cpp", + "GrowlNotifier.mm", + "GrowlNotifierDelegate.mm", ] if swiftools_env.get("HAVE_SNARL", False) : myenv.MergeFlags(myenv["SNARL_FLAGS"]) -- cgit v0.10.2-6-g49f6