summaryrefslogtreecommitdiffstats
blob: 46355e900a7b03ade49446897c5618b58c5c3243 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/*
 * 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() {}
		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;
	picture.readFromFile(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*) 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);
}

}