diff options
Diffstat (limited to 'Swiften/Notifier')
-rw-r--r-- | Swiften/Notifier/GrowlNotifier.cpp | 91 | ||||
-rw-r--r-- | Swiften/Notifier/GrowlNotifier.h | 28 | ||||
-rw-r--r-- | Swiften/Notifier/Notifier.cpp | 8 | ||||
-rw-r--r-- | Swiften/Notifier/Notifier.h | 24 |
4 files changed, 151 insertions, 0 deletions
diff --git a/Swiften/Notifier/GrowlNotifier.cpp b/Swiften/Notifier/GrowlNotifier.cpp new file mode 100644 index 0000000..13d3e6d --- /dev/null +++ b/Swiften/Notifier/GrowlNotifier.cpp @@ -0,0 +1,91 @@ +// FIXME: Is it safe to pass boost::function<void()> by raw values? +// FIXME: Should we release the strings created in the constructor? + +#include <cassert> + +#include "Swiften/Base/ByteArray.h" +#include "Swiften/Notifier/GrowlNotifier.h" + +#pragma GCC diagnostic ignored "-Wold-style-cast" + +namespace { + struct Context { + Context() {} + Context(const boost::function<void()>& callback) : callback(callback) {} + + boost::function<void()> callback; + }; + + void notificationClicked(CFPropertyListRef growlContext) { + Context context; + + CFDataRef growlContextData = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) growlContext, 0); + assert(CFDataGetLength(growlContextData) == sizeof(Context)); + CFDataGetBytes(growlContextData, CFRangeMake(0, CFDataGetLength(growlContextData)), (UInt8*) &context); + + context.callback(); + } + + void notificationTimedout(CFPropertyListRef) { + } +} + +namespace Swift { + +GrowlNotifier::GrowlNotifier(const String& name) { + // All notifications + CFMutableArrayRef allNotifications = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + CFArrayAppendValue(allNotifications, SWIFTEN_STRING_TO_CFSTRING(typeToString(ContactAvailable))); + CFArrayAppendValue(allNotifications, SWIFTEN_STRING_TO_CFSTRING(typeToString(ContactUnavailable))); + CFArrayAppendValue(allNotifications, SWIFTEN_STRING_TO_CFSTRING(typeToString(ContactStatusChange))); + CFArrayAppendValue(allNotifications, SWIFTEN_STRING_TO_CFSTRING(typeToString(IncomingMessage))); + + // Default Notifications + CFMutableArrayRef defaultNotifications = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + CFArrayAppendValue(defaultNotifications, SWIFTEN_STRING_TO_CFSTRING(typeToString(ContactAvailable))); + CFArrayAppendValue(defaultNotifications, SWIFTEN_STRING_TO_CFSTRING(typeToString(IncomingMessage))); + + // 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 String& subject, const String& description, const ByteArray& picture, boost::function<void()> callback) { + 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*) picture.getData(), picture.getSize()); + + 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); +} + +String GrowlNotifier::typeToString(Type type) { + switch (type) { + case ContactAvailable: return "Contact Becomes Available"; + case ContactUnavailable: return "Contact Becomes Unavailable"; + case ContactStatusChange: return "Contact Changes Status"; + case IncomingMessage: return "Incoming Message"; + } + assert(false); + return ""; +} + +} diff --git a/Swiften/Notifier/GrowlNotifier.h b/Swiften/Notifier/GrowlNotifier.h new file mode 100644 index 0000000..3bf53be --- /dev/null +++ b/Swiften/Notifier/GrowlNotifier.h @@ -0,0 +1,28 @@ +#pragma once + +#include <CoreFoundation/CoreFoundation.h> +#include <Growl/Growl.h> + +#include "Swiften/Notifier/Notifier.h" + +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. + * TODO: Find out what the easiest way is to do this without a QApplication. + */ + class GrowlNotifier : public Notifier { + public: + GrowlNotifier(const String& name); + + virtual void showMessage(Type type, const String& subject, const String& description, const ByteArray& picture, boost::function<void()> callback); + + private: + String typeToString(Type type); + + private: + Growl_Delegate delegate_; + }; +} diff --git a/Swiften/Notifier/Notifier.cpp b/Swiften/Notifier/Notifier.cpp new file mode 100644 index 0000000..88cb0ee --- /dev/null +++ b/Swiften/Notifier/Notifier.cpp @@ -0,0 +1,8 @@ +#include "Swiften/Notifier/Notifier.h" + +namespace Swift { + +Notifier::~Notifier() { +} + +} diff --git a/Swiften/Notifier/Notifier.h b/Swiften/Notifier/Notifier.h new file mode 100644 index 0000000..2ab3ba8 --- /dev/null +++ b/Swiften/Notifier/Notifier.h @@ -0,0 +1,24 @@ +#pragma once + +#include <boost/function.hpp> + +#include "Swiften/Base/String.h" + +namespace Swift { + class Notifier { + public: + virtual ~Notifier(); + + enum Type { ContactAvailable, ContactUnavailable, ContactStatusChange, IncomingMessage }; + + /** + * Picture is a PNG image. + */ + virtual void showMessage( + Type type, + const String& subject, + const String& description, + const ByteArray& picture, + boost::function<void()> callback) = 0; + }; +} |