summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRemko Tronçon <git@el-tramo.be>2012-03-09 11:22:00 (GMT)
committerRemko Tronçon <git@el-tramo.be>2012-03-09 11:22:00 (GMT)
commitc83da56f6c93db3dc8040be1137fd40a08bf4132 (patch)
treec4141dc2f333a6517a5dbc70c04c013878bee1bf
parent0833f7da453db9cd0fc3a78c793e7532663ab86b (diff)
downloadswift-contrib-c83da56f6c93db3dc8040be1137fd40a08bf4132.zip
swift-contrib-c83da56f6c93db3dc8040be1137fd40a08bf4132.tar.bz2
Handle double growl callbacks.
-rw-r--r--BuildTools/SCons/SConstruct2
-rw-r--r--SwifTools/Notifier/GrowlNotifier.h1
-rw-r--r--SwifTools/Notifier/GrowlNotifier.mm28
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];
}
}