From 5862dcdf8dc6e1bb160693ad6a5ca0609ddb990a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
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 <boost/intrusive_ptr.hpp>
+
+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 <cassert>
-
-#include <Swiften/Base/String.h>
-#include <Swiften/Base/ByteArray.h>
-#include <SwifTools/Notifier/GrowlNotifier.h>
-#include <Swiften/Base/foreach.h>
-
-#pragma GCC diagnostic ignored "-Wold-style-cast"
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-
-namespace {
-	struct Context {
-		Context() : callback(0) {}
-		Context(const boost::function<void()>& callback) : callback(new boost::function<void()>(callback)) {}
-
-		boost::function<void()>* 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 = &notificationClicked;
-	delegate_.growlNotificationTimedOut = &notificationTimedout;
-	Growl_SetDelegate(&delegate_);
-}
-
-void GrowlNotifier::showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picturePath, boost::function<void()> 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 <CoreFoundation/CoreFoundation.h>
-#include <Growl/Growl.h>
 #include <boost/filesystem/fstream.hpp>
 
 #include <SwifTools/Notifier/Notifier.h>
@@ -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<void()> 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<Private> 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 <SwifTools/Notifier/GrowlNotifier.h>
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <SwifTools/Notifier/GrowlNotifierDelegate.h>
+#include <SwifTools/Cocoa/CocoaUtil.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/ByteArray.h>
+
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+namespace {
+	struct Context {
+		Context(const boost::function<void()>& callback) : callback(new boost::function<void()>(callback)) {}
+
+		boost::function<void()>* callback;
+	};
+}
+
+namespace Swift {
+
+class GrowlNotifier::Private {
+	public:
+		boost::intrusive_ptr<GrowlNotifierDelegate> delegate;
+};
+
+GrowlNotifier::GrowlNotifier(const std::string& name) {
+	p = boost::make_shared<Private>();
+	p->delegate = boost::intrusive_ptr<GrowlNotifierDelegate>([[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<void()> 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 <Growl/Growl.h>
+
+namespace Swift {
+	class GrowlNotifier;
+}
+
+@interface GrowlNotifierDelegate : NSObject<GrowlApplicationBridgeDelegate> {
+	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 <SwifTools/Notifier/GrowlNotifier.h>
+
+@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