diff options
-rw-r--r-- | BuildTools/SCons/SConstruct | 2 | ||||
-rw-r--r-- | SwifTools/Notifier/GrowlNotifier.h | 1 | ||||
-rw-r--r-- | SwifTools/Notifier/GrowlNotifier.mm | 28 |
3 files changed, 25 insertions, 6 deletions
diff --git a/BuildTools/SCons/SConstruct b/BuildTools/SCons/SConstruct index 1412b56..b3d3c8f 100644 --- a/BuildTools/SCons/SConstruct +++ b/BuildTools/SCons/SConstruct @@ -236,19 +236,19 @@ if env["PLATFORM"] == "darwin" : env["HAVE_GROWL"] = 0 if env["PLATFORM"] == "darwin" : growl_flags = { "FRAMEWORKPATH": ["/Library/Frameworks"], "FRAMEWORKS": ["Growl"] } 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" conf.Finish() # Snarl if env["PLATFORM"] == "win32" : env["HAVE_SNARL"] = True 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 @@ -14,18 +14,19 @@ namespace Swift { /** * Preconditions for using growlnotifier: * - Must be part a bundle. * - The Carbon/Cocoa application loop must be running (e.g. through QApplication) * such that notifications are coming through. */ 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; // Called by the delegate. Don't call. void handleNotificationClicked(void* data); void handleNotificationTimedOut(void* data); private: 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 @@ -1,18 +1,19 @@ /* * 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 <set> #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 { @@ -21,18 +22,19 @@ namespace { boost::function<void()>* callback; }; } namespace Swift { class GrowlNotifier::Private { public: + std::set<Context*> pendingNotifications; 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]; @@ -49,42 +51,58 @@ GrowlNotifier::GrowlNotifier(const std::string& name) { initWithObjects: [NSArray arrayWithObjects: allNotifications, defaultNotifications, nil] forKeys: [NSArray arrayWithObjects: GROWL_NOTIFICATIONS_ALL, GROWL_NOTIFICATIONS_DEFAULT, nil]] autorelease]; [allNotifications release]; [defaultNotifications release]; [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) notificationName: STD2NSSTRING(typeToString(type)) iconData: [NSData dataWithBytes: vecptr(picture) length: picture.size()] priority: 0 isSticky: NO clickContext: [NSData dataWithBytes: &context length: sizeof(context)]]; } 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 { return ![GrowlApplicationBridge isMistEnabled]; } } |