From c83da56f6c93db3dc8040be1137fd40a08bf4132 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Fri, 9 Mar 2012 12:22:00 +0100
Subject: Handle double growl callbacks.


diff --git a/BuildTools/SCons/SConstruct b/BuildTools/SCons/SConstruct
index 1412b56..b3d3c8f 100644
--- a/BuildTools/SCons/SConstruct
+++ b/BuildTools/SCons/SConstruct
@@ -242,7 +242,7 @@ if env["PLATFORM"] == "darwin" :
 	growl_env = conf_env.Clone()
 	growl_env.MergeFlags(growl_flags)
 	conf = Configure(growl_env, custom_tests = { "CheckObjCHeader" : checkObjCHeader })
-	if False and conf.CheckObjCHeader("Growl/Growl.h") :
+	if conf.CheckObjCHeader("Growl/Growl.h") :
 		env["HAVE_GROWL"] = 1
 		env["GROWL_FLAGS"] = growl_flags
 		env["GROWL_FRAMEWORK"] = "/Library/Frameworks/Growl.framework"
diff --git a/SwifTools/Notifier/GrowlNotifier.h b/SwifTools/Notifier/GrowlNotifier.h
index cb0d089..f4e6803 100644
--- a/SwifTools/Notifier/GrowlNotifier.h
+++ b/SwifTools/Notifier/GrowlNotifier.h
@@ -20,6 +20,7 @@ namespace Swift {
 	class GrowlNotifier : public Notifier {
 		public:
 			GrowlNotifier(const std::string& name);
+			~GrowlNotifier();
 
 			virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback);
 			virtual bool isExternallyConfigured() const;
diff --git a/SwifTools/Notifier/GrowlNotifier.mm b/SwifTools/Notifier/GrowlNotifier.mm
index cb4790e..31eabd3 100644
--- a/SwifTools/Notifier/GrowlNotifier.mm
+++ b/SwifTools/Notifier/GrowlNotifier.mm
@@ -7,6 +7,7 @@
 #include <SwifTools/Notifier/GrowlNotifier.h>
 
 #include <boost/smart_ptr/make_shared.hpp>
+#include <set>
 
 #include <SwifTools/Notifier/GrowlNotifierDelegate.h>
 #include <SwifTools/Cocoa/CocoaUtil.h>
@@ -27,6 +28,7 @@ namespace Swift {
 
 class GrowlNotifier::Private {
 	public:
+		std::set<Context*> pendingNotifications;
 		boost::intrusive_ptr<GrowlNotifierDelegate> delegate;
 };
 
@@ -55,12 +57,23 @@ GrowlNotifier::GrowlNotifier(const std::string& name) {
 	[GrowlApplicationBridge setGrowlDelegate: p->delegate.get()];
 }
 
+GrowlNotifier::~GrowlNotifier() {
+	[GrowlApplicationBridge setGrowlDelegate: nil];
+	foreach (Context* context, p->pendingNotifications) {
+		delete context;
+	}
+	p->pendingNotifications.clear();
+}
+
 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* context = new Context(callback);
-
+	// Growl sometimes sends timeout notifications twice for the same message. We therefore need
+	// to keep track of which ones have already been processed.
+	p->pendingNotifications.insert(context);
+	
 	[GrowlApplicationBridge 
 		notifyWithTitle: STD2NSSTRING(subject)
 		description: STD2NSSTRING(description)
@@ -73,14 +86,19 @@ void GrowlNotifier::showMessage(Type type, const std::string& subject, const std
 
 void GrowlNotifier::handleNotificationClicked(void* rawData) {
 	Context* context = *(Context**) [((NSData*) rawData) bytes];
-	if (!context->callback->empty()) {
-		(*context->callback)();
+	if (p->pendingNotifications.erase(context) > 0) {
+		if (!context->callback->empty()) {
+			(*context->callback)();
+		}
+		delete context;
 	}
-	delete context;
 }
 
 void GrowlNotifier::handleNotificationTimedOut(void* rawData) {
-	delete *(Context**) [((NSData*) rawData) bytes];
+	Context* context = *(Context**) [((NSData*) rawData) bytes];
+	if (p->pendingNotifications.erase(context) > 0) {
+		delete context;
+	}
 }
 
 bool GrowlNotifier::isExternallyConfigured() const {
-- 
cgit v0.10.2-6-g49f6