|
boredzo@1385
|
1 |
//
|
|
boredzo@1385
|
2 |
// GrowlApplicationTicket.m
|
|
boredzo@1385
|
3 |
// Growl
|
|
boredzo@1385
|
4 |
//
|
|
boredzo@1385
|
5 |
// Created by Karl Adam on Tue Apr 27 2004.
|
|
ingmarstein@3040
|
6 |
// Copyright 2004-2006 The Growl Project. All rights reserved.
|
|
boredzo@1385
|
7 |
//
|
|
boredzo@1385
|
8 |
// This file is under the BSD License, refer to License.txt for details
|
|
boredzo@1385
|
9 |
|
|
boredzo@1385
|
10 |
|
|
boredzo@1385
|
11 |
#import "GrowlApplicationTicket.h"
|
|
boredzo@2501
|
12 |
#import "GrowlNotificationTicket.h"
|
|
boredzo@1385
|
13 |
#import "GrowlDefines.h"
|
|
ingmarstein@2695
|
14 |
#import "GrowlDisplayPlugin.h"
|
|
ingmarstein@2100
|
15 |
#import "NSWorkspaceAdditions.h"
|
|
boredzo@2461
|
16 |
#import "GrowlPathUtilities.h"
|
|
ingmarstein@2641
|
17 |
#include "CFGrowlAdditions.h"
|
|
ingmarstein@2639
|
18 |
#include "CFURLAdditions.h"
|
|
ingmarstein@2641
|
19 |
#include "CFDictionaryAdditions.h"
|
|
boredzo@1385
|
20 |
|
|
ingmarstein@1518
|
21 |
#define UseDefaultsKey @"useDefaults"
|
|
ingmarstein@1518
|
22 |
#define TicketEnabledKey @"ticketEnabled"
|
|
ingmarstein@1958
|
23 |
#define ClickHandlersEnabledKey @"clickHandlersEnabled"
|
|
bgannin@3317
|
24 |
#define PositionTypeKey @"positionType"
|
|
boredzo@1385
|
25 |
|
|
boredzo@1385
|
26 |
#pragma mark -
|
|
boredzo@1385
|
27 |
|
|
boredzo@1385
|
28 |
@implementation GrowlApplicationTicket
|
|
boredzo@1385
|
29 |
|
|
boredzo@1415
|
30 |
//these are specifically for auto-discovery tickets, hence the requirement of GROWL_TICKET_VERSION.
|
|
ingmarstein@1958
|
31 |
+ (BOOL) isValidTicketDictionary:(NSDictionary *)dict {
|
|
ingmarstein@2641
|
32 |
if (!dict)
|
|
ingmarstein@2641
|
33 |
return NO;
|
|
ingmarstein@2641
|
34 |
|
|
ingmarstein@2641
|
35 |
NSNumber *versionNum = getObjectForKey(dict, GROWL_TICKET_VERSION);
|
|
ingmarstein@1483
|
36 |
if ([versionNum intValue] == 1) {
|
|
ingmarstein@2641
|
37 |
return getObjectForKey(dict, GROWL_NOTIFICATIONS_ALL)
|
|
ingmarstein@2641
|
38 |
&& getObjectForKey(dict, GROWL_APP_NAME);
|
|
ingmarstein@1474
|
39 |
} else {
|
|
boredzo@1415
|
40 |
return NO;
|
|
ingmarstein@1474
|
41 |
}
|
|
ingmarstein@1474
|
42 |
}
|
|
ingmarstein@1474
|
43 |
|
|
ingmarstein@1958
|
44 |
+ (BOOL) isKnownTicketVersion:(NSDictionary *)dict {
|
|
ingmarstein@2641
|
45 |
id version = getObjectForKey(dict, GROWL_TICKET_VERSION);
|
|
boredzo@1678
|
46 |
return version && ([version intValue] == 1);
|
|
boredzo@1415
|
47 |
}
|
|
boredzo@1415
|
48 |
|
|
boredzo@1479
|
49 |
#pragma mark -
|
|
boredzo@1479
|
50 |
|
|
ingmarstein@1958
|
51 |
+ (id) ticketWithDictionary:(NSDictionary *)ticketDict {
|
|
ingmarstein@1507
|
52 |
return [[[GrowlApplicationTicket alloc] initWithDictionary:ticketDict] autorelease];
|
|
boredzo@1479
|
53 |
}
|
|
boredzo@1479
|
54 |
|
|
ingmarstein@1958
|
55 |
- (id) initWithDictionary:(NSDictionary *)ticketDict {
|
|
ingmarstein@1483
|
56 |
if (!ticketDict) {
|
|
boredzo@1479
|
57 |
[self release];
|
|
boredzo@1479
|
58 |
NSParameterAssert(ticketDict != nil);
|
|
boredzo@1479
|
59 |
return nil;
|
|
boredzo@1479
|
60 |
}
|
|
evands@3512
|
61 |
if ((self = [self init])) {
|
|
evands@3320
|
62 |
synchronizeOnChanges = NO;
|
|
evands@3320
|
63 |
|
|
ingmarstein@2641
|
64 |
appName = [getObjectForKey(ticketDict, GROWL_APP_NAME) retain];
|
|
ingmarstein@3035
|
65 |
appId = [getObjectForKey(ticketDict, GROWL_APP_ID) retain];
|
|
boredzo@1479
|
66 |
|
|
evands@2923
|
67 |
humanReadableNames = [[ticketDict objectForKey:GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES] retain];
|
|
evands@2923
|
68 |
notificationDescriptions = [[ticketDict objectForKey:GROWL_NOTIFICATIONS_DESCRIPTIONS] retain];
|
|
ingmarstein@2943
|
69 |
|
|
boredzo@1479
|
70 |
//Get all the notification names and the data about them
|
|
ingmarstein@3022
|
71 |
allNotificationNames = [ticketDict objectForKey:GROWL_NOTIFICATIONS_ALL];
|
|
boredzo@1479
|
72 |
NSAssert1(allNotificationNames, @"Ticket dictionaries must contain a list of all their notifications (application name: %@)", appName);
|
|
evands@2923
|
73 |
|
|
evands@2923
|
74 |
NSArray *inDefaults = [ticketDict objectForKey:GROWL_NOTIFICATIONS_DEFAULT];
|
|
ingmarstein@1610
|
75 |
if (!inDefaults) inDefaults = allNotificationNames;
|
|
boredzo@1479
|
76 |
|
|
boredzo@1385
|
77 |
NSEnumerator *notificationsEnum = [allNotificationNames objectEnumerator];
|
|
ingmarstein@1610
|
78 |
NSMutableDictionary *allNotificationsTemp = [[NSMutableDictionary alloc] initWithCapacity:[allNotificationNames count]];
|
|
ingmarstein@3022
|
79 |
NSMutableArray *allNamesTemp = [[NSMutableArray alloc] initWithCapacity:[allNotificationNames count]];
|
|
boredzo@1385
|
80 |
id obj;
|
|
ingmarstein@1579
|
81 |
while ((obj = [notificationsEnum nextObject])) {
|
|
ingmarstein@1610
|
82 |
NSString *name;
|
|
boredzo@2501
|
83 |
GrowlNotificationTicket *notification;
|
|
boredzo@1479
|
84 |
if ([obj isKindOfClass:[NSString class]]) {
|
|
ingmarstein@1610
|
85 |
name = obj;
|
|
boredzo@2501
|
86 |
notification = [[GrowlNotificationTicket alloc] initWithName:obj];
|
|
boredzo@1479
|
87 |
} else {
|
|
ingmarstein@1610
|
88 |
name = [obj objectForKey:@"Name"];
|
|
boredzo@2501
|
89 |
notification = [[GrowlNotificationTicket alloc] initWithDictionary:obj];
|
|
ingmarstein@1610
|
90 |
}
|
|
ingmarstein@3022
|
91 |
[allNamesTemp addObject:name];
|
|
ingmarstein@1720
|
92 |
[notification setTicket:self];
|
|
ingmarstein@2943
|
93 |
|
|
evands@2920
|
94 |
//Set the human readable name if we were supplied one
|
|
evands@2920
|
95 |
[notification setHumanReadableName:[humanReadableNames objectForKey:name]];
|
|
evands@2923
|
96 |
[notification setNotificationDescription:[notificationDescriptions objectForKey:name]];
|
|
evands@2920
|
97 |
|
|
ingmarstein@1610
|
98 |
[allNotificationsTemp setObject:notification forKey:name];
|
|
ingmarstein@1610
|
99 |
[notification release];
|
|
ingmarstein@1610
|
100 |
}
|
|
ingmarstein@1610
|
101 |
allNotifications = allNotificationsTemp;
|
|
ingmarstein@3022
|
102 |
allNotificationNames = allNamesTemp;
|
|
boredzo@1479
|
103 |
|
|
ingmarstein@1748
|
104 |
BOOL doLookup = YES;
|
|
boredzo@1479
|
105 |
NSString *fullPath = nil;
|
|
ingmarstein@2641
|
106 |
id location = getObjectForKey(ticketDict, GROWL_APP_LOCATION);
|
|
ingmarstein@1483
|
107 |
if (location) {
|
|
ingmarstein@1483
|
108 |
if ([location isKindOfClass:[NSDictionary class]]) {
|
|
ingmarstein@2641
|
109 |
NSDictionary *file_data = getObjectForKey((NSDictionary *)location, @"file-data");
|
|
ingmarstein@3035
|
110 |
CFURLRef url = (CFURLRef)createFileURLWithDockDescription(file_data);
|
|
ingmarstein@3035
|
111 |
if (url) {
|
|
ingmarstein@3035
|
112 |
fullPath = [(NSString *)CFURLCopyPath(url) autorelease];
|
|
ingmarstein@3035
|
113 |
CFRelease(url);
|
|
ingmarstein@3035
|
114 |
}
|
|
ingmarstein@1483
|
115 |
} else if ([location isKindOfClass:[NSString class]]) {
|
|
boredzo@1479
|
116 |
fullPath = location;
|
|
ingmarstein@2639
|
117 |
if (![[NSFileManager defaultManager] fileExistsAtPath:fullPath])
|
|
boredzo@1479
|
118 |
fullPath = nil;
|
|
ingmarstein@1748
|
119 |
} else if ([location isKindOfClass:[NSNumber class]]) {
|
|
ingmarstein@1748
|
120 |
doLookup = [location boolValue];
|
|
ingmarstein@1483
|
121 |
}
|
|
ingmarstein@1483
|
122 |
}
|
|
ingmarstein@3035
|
123 |
if (!fullPath && doLookup) {
|
|
ingmarstein@3035
|
124 |
if (appId) {
|
|
ingmarstein@3035
|
125 |
CFURLRef appURL = NULL;
|
|
ingmarstein@3035
|
126 |
OSStatus err = LSFindApplicationForInfo(kLSUnknownCreator,
|
|
ingmarstein@3035
|
127 |
(CFStringRef)appId,
|
|
ingmarstein@3035
|
128 |
/*inName*/ NULL,
|
|
ingmarstein@3035
|
129 |
/*outAppRef*/ NULL,
|
|
ingmarstein@3035
|
130 |
&appURL);
|
|
ingmarstein@3035
|
131 |
if (err == noErr) {
|
|
ingmarstein@3035
|
132 |
fullPath = [(NSString *)CFURLCopyPath(appURL) autorelease];
|
|
ingmarstein@3035
|
133 |
CFRelease(appURL);
|
|
ingmarstein@3035
|
134 |
}
|
|
ingmarstein@3035
|
135 |
}
|
|
ingmarstein@3035
|
136 |
if (!fullPath)
|
|
ingmarstein@3035
|
137 |
fullPath = [[NSWorkspace sharedWorkspace] fullPathForApplication:appName];
|
|
ingmarstein@3035
|
138 |
}
|
|
boredzo@1479
|
139 |
appPath = [fullPath retain];
|
|
ingmarstein@1984
|
140 |
// NSLog(@"got appPath: %@", appPath);
|
|
boredzo@1479
|
141 |
|
|
ingmarstein@2641
|
142 |
[self setIcon:getObjectForKey(ticketDict, GROWL_APP_ICON)];
|
|
ingmarstein@2641
|
143 |
|
|
ingmarstein@2641
|
144 |
id value = getObjectForKey(ticketDict, UseDefaultsKey);
|
|
ingmarstein@2470
|
145 |
if (value)
|
|
ingmarstein@1558
|
146 |
useDefaults = [value boolValue];
|
|
ingmarstein@2470
|
147 |
else
|
|
ingmarstein@1556
|
148 |
useDefaults = YES;
|
|
boredzo@1479
|
149 |
|
|
ingmarstein@2641
|
150 |
value = getObjectForKey(ticketDict, TicketEnabledKey);
|
|
ingmarstein@2470
|
151 |
if (value)
|
|
ingmarstein@1558
|
152 |
ticketEnabled = [value boolValue];
|
|
ingmarstein@2470
|
153 |
else
|
|
boredzo@1479
|
154 |
ticketEnabled = YES;
|
|
boredzo@1479
|
155 |
|
|
ingmarstein@2636
|
156 |
displayPluginName = [[ticketDict objectForKey:GrowlDisplayPluginKey] copy];
|
|
ingmarstein@1610
|
157 |
|
|
ingmarstein@2641
|
158 |
value = getObjectForKey(ticketDict, ClickHandlersEnabledKey);
|
|
ingmarstein@2470
|
159 |
if (value)
|
|
ingmarstein@1958
|
160 |
clickHandlersEnabled = [value boolValue];
|
|
ingmarstein@2470
|
161 |
else
|
|
ingmarstein@1958
|
162 |
clickHandlersEnabled = YES;
|
|
bgannin@3317
|
163 |
|
|
bgannin@3317
|
164 |
value = getObjectForKey(ticketDict, PositionTypeKey);
|
|
bgannin@3317
|
165 |
if (value)
|
|
bgannin@3317
|
166 |
positionType = [value intValue];
|
|
bgannin@3317
|
167 |
else
|
|
bgannin@3317
|
168 |
positionType = 0;
|
|
bgannin@3317
|
169 |
|
|
bgannin@3317
|
170 |
value = getObjectForKey(ticketDict, GROWL_POSITION_PREFERENCE_KEY);
|
|
bgannin@3317
|
171 |
if (value)
|
|
bgannin@3317
|
172 |
selectedCustomPosition = [value intValue];
|
|
bgannin@3317
|
173 |
else
|
|
bgannin@3317
|
174 |
selectedCustomPosition = 0;
|
|
ingmarstein@1958
|
175 |
|
|
ingmarstein@1610
|
176 |
[self setDefaultNotifications:inDefaults];
|
|
evands@3318
|
177 |
|
|
ingmarstein@3022
|
178 |
changed = YES;
|
|
evands@3320
|
179 |
synchronizeOnChanges = YES;
|
|
boredzo@1479
|
180 |
}
|
|
boredzo@1385
|
181 |
return self;
|
|
boredzo@1385
|
182 |
}
|
|
boredzo@1385
|
183 |
|
|
boredzo@1385
|
184 |
- (void) dealloc {
|
|
ingmarstein@3022
|
185 |
[appName release];
|
|
ingmarstein@3035
|
186 |
[appId release];
|
|
ingmarstein@3022
|
187 |
[appPath release];
|
|
ingmarstein@3022
|
188 |
[icon release];
|
|
ingmarstein@3022
|
189 |
[iconData release];
|
|
ingmarstein@3022
|
190 |
[allNotifications release];
|
|
ingmarstein@3022
|
191 |
[defaultNotifications release];
|
|
ingmarstein@3022
|
192 |
[humanReadableNames release];
|
|
evands@2923
|
193 |
[notificationDescriptions release];
|
|
ingmarstein@3022
|
194 |
[allNotificationNames release];
|
|
ingmarstein@3022
|
195 |
[displayPluginName release];
|
|
ingmarstein@1628
|
196 |
|
|
boredzo@1385
|
197 |
[super dealloc];
|
|
boredzo@1385
|
198 |
}
|
|
boredzo@1385
|
199 |
|
|
boredzo@1385
|
200 |
#pragma mark -
|
|
boredzo@1479
|
201 |
|
|
boredzo@1479
|
202 |
- (id) initTicketFromPath:(NSString *) ticketPath {
|
|
ingmarstein@2652
|
203 |
CFURLRef ticketURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)ticketPath, kCFURLPOSIXPathStyle, false);
|
|
ingmarstein@2673
|
204 |
NSDictionary *ticketDict = (NSDictionary *)createPropertyListFromURL((NSURL *)ticketURL, kCFPropertyListImmutable, NULL, NULL);
|
|
ingmarstein@2652
|
205 |
CFRelease(ticketURL);
|
|
ingmarstein@1483
|
206 |
if (!ticketDict) {
|
|
boredzo@1479
|
207 |
NSLog(@"Tried to init a ticket from this file, but it isn't a ticket file: %@", ticketPath);
|
|
boredzo@1479
|
208 |
[self release];
|
|
boredzo@1479
|
209 |
return nil;
|
|
boredzo@1479
|
210 |
}
|
|
evands@3318
|
211 |
|
|
ingmarstein@1637
|
212 |
self = [self initWithDictionary:ticketDict];
|
|
ingmarstein@1637
|
213 |
[ticketDict release];
|
|
ingmarstein@1637
|
214 |
return self;
|
|
boredzo@1385
|
215 |
}
|
|
boredzo@1385
|
216 |
|
|
boredzo@1385
|
217 |
- (id) initTicketForApplication: (NSString *) inApp {
|
|
boredzo@2677
|
218 |
return [self initTicketFromPath:[[[[GrowlPathUtilities growlSupportDirectory]
|
|
ingmarstein@1790
|
219 |
stringByAppendingPathComponent:@"Tickets"]
|
|
ingmarstein@1790
|
220 |
stringByAppendingPathComponent:inApp]
|
|
ingmarstein@1790
|
221 |
stringByAppendingPathExtension:@"growlTicket"]];
|
|
boredzo@1385
|
222 |
}
|
|
boredzo@1385
|
223 |
|
|
boredzo@1385
|
224 |
- (NSString *) path {
|
|
boredzo@2677
|
225 |
NSString *destDir = [GrowlPathUtilities growlSupportDirectory];
|
|
boredzo@1385
|
226 |
destDir = [destDir stringByAppendingPathComponent:@"Tickets"];
|
|
boredzo@1385
|
227 |
destDir = [destDir stringByAppendingPathComponent:[appName stringByAppendingPathExtension:@"growlTicket"]];
|
|
boredzo@1385
|
228 |
return destDir;
|
|
boredzo@1385
|
229 |
}
|
|
boredzo@1385
|
230 |
|
|
boredzo@1385
|
231 |
- (void) saveTicket {
|
|
boredzo@2677
|
232 |
NSString *destDir = [GrowlPathUtilities growlSupportDirectory];
|
|
boredzo@1385
|
233 |
destDir = [destDir stringByAppendingPathComponent:@"Tickets"];
|
|
boredzo@1385
|
234 |
|
|
boredzo@1385
|
235 |
[self saveTicketToPath:destDir];
|
|
boredzo@1385
|
236 |
}
|
|
boredzo@1385
|
237 |
|
|
boredzo@1385
|
238 |
- (void) saveTicketToPath:(NSString *)destDir {
|
|
boredzo@1385
|
239 |
// Save a Plist file of this object to configure the prefs of apps that aren't running
|
|
boredzo@1385
|
240 |
// construct a dictionary of our state data then save that dictionary to a file.
|
|
boredzo@1385
|
241 |
NSString *savePath = [destDir stringByAppendingPathComponent:[appName stringByAppendingPathExtension:@"growlTicket"]];
|
|
ingmarstein@1610
|
242 |
NSMutableArray *saveNotifications = [[NSMutableArray alloc] initWithCapacity:[allNotifications count]];
|
|
boredzo@1385
|
243 |
NSEnumerator *notificationEnum = [allNotifications objectEnumerator];
|
|
boredzo@2501
|
244 |
GrowlNotificationTicket *obj;
|
|
ingmarstein@2450
|
245 |
while ((obj = [notificationEnum nextObject]))
|
|
ingmarstein@2450
|
246 |
[saveNotifications addObject:[obj dictionaryRepresentation]];
|
|
boredzo@1385
|
247 |
|
|
boredzo@1479
|
248 |
NSDictionary *file_data = nil;
|
|
ingmarstein@1507
|
249 |
if (appPath) {
|
|
ingmarstein@2080
|
250 |
NSURL *url = [[NSURL alloc] initFileURLWithPath:appPath];
|
|
ingmarstein@2639
|
251 |
file_data = createDockDescriptionWithURL(url);
|
|
ingmarstein@2080
|
252 |
[url release];
|
|
ingmarstein@1507
|
253 |
}
|
|
boredzo@1479
|
254 |
|
|
boredzo@1479
|
255 |
id location = file_data ? [NSDictionary dictionaryWithObject:file_data forKey:@"file-data"] : appPath;
|
|
ingmarstein@2450
|
256 |
if (!location)
|
|
ingmarstein@1748
|
257 |
location = [NSNumber numberWithBool:NO];
|
|
ingmarstein@2639
|
258 |
[file_data release];
|
|
boredzo@1479
|
259 |
|
|
ingmarstein@1643
|
260 |
NSNumber *useDefaultsValue = [[NSNumber alloc] initWithBool:useDefaults];
|
|
ingmarstein@1643
|
261 |
NSNumber *ticketEnabledValue = [[NSNumber alloc] initWithBool:ticketEnabled];
|
|
ingmarstein@1958
|
262 |
NSNumber *clickHandlersEnabledValue = [[NSNumber alloc] initWithBool:clickHandlersEnabled];
|
|
bgannin@3317
|
263 |
NSNumber *positionTypeValue = [[NSNumber alloc] initWithInt:positionType];
|
|
bgannin@3317
|
264 |
NSNumber *selectedCustomPositionValue = [[NSNumber alloc] initWithInt:selectedCustomPosition];
|
|
ingmarstein@3022
|
265 |
NSData *theIconData = iconData;
|
|
ingmarstein@3022
|
266 |
if (!theIconData) {
|
|
ingmarstein@3022
|
267 |
NSImage *theIcon = [self icon];
|
|
ingmarstein@3022
|
268 |
theIconData = theIcon ? [theIcon TIFFRepresentation] : [NSData data];
|
|
ingmarstein@3022
|
269 |
}
|
|
ingmarstein@1600
|
270 |
NSMutableDictionary *saveDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
|
|
bgannin@3317
|
271 |
appName, GROWL_APP_NAME,
|
|
bgannin@3317
|
272 |
saveNotifications, GROWL_NOTIFICATIONS_ALL,
|
|
bgannin@3317
|
273 |
defaultNotifications, GROWL_NOTIFICATIONS_DEFAULT,
|
|
bgannin@3317
|
274 |
theIconData, GROWL_APP_ICON,
|
|
bgannin@3317
|
275 |
useDefaultsValue, UseDefaultsKey,
|
|
bgannin@3317
|
276 |
ticketEnabledValue, TicketEnabledKey,
|
|
bgannin@3317
|
277 |
clickHandlersEnabledValue, ClickHandlersEnabledKey,
|
|
bgannin@3317
|
278 |
positionTypeValue, PositionTypeKey,
|
|
bgannin@3317
|
279 |
selectedCustomPositionValue, GROWL_POSITION_PREFERENCE_KEY,
|
|
bgannin@3317
|
280 |
location, GROWL_APP_LOCATION,
|
|
boredzo@1385
|
281 |
nil];
|
|
bgannin@3317
|
282 |
[useDefaultsValue release];
|
|
bgannin@3317
|
283 |
[ticketEnabledValue release];
|
|
bgannin@3317
|
284 |
[clickHandlersEnabledValue release];
|
|
bgannin@3317
|
285 |
[positionTypeValue release];
|
|
bgannin@3317
|
286 |
[selectedCustomPositionValue release];
|
|
bgannin@3317
|
287 |
[saveNotifications release];
|
|
bgannin@3317
|
288 |
|
|
ingmarstein@2290
|
289 |
if (displayPluginName)
|
|
ingmarstein@1507
|
290 |
[saveDict setObject:displayPluginName forKey:GrowlDisplayPluginKey];
|
|
boredzo@1479
|
291 |
|
|
evands@2920
|
292 |
if (humanReadableNames)
|
|
evands@2920
|
293 |
[saveDict setObject:humanReadableNames forKey:GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES];
|
|
ingmarstein@2943
|
294 |
|
|
evands@2923
|
295 |
if (notificationDescriptions)
|
|
evands@2923
|
296 |
[saveDict setObject:notificationDescriptions forKey:GROWL_NOTIFICATIONS_DESCRIPTIONS];
|
|
ingmarstein@2943
|
297 |
|
|
ingmarstein@3035
|
298 |
if (appId)
|
|
ingmarstein@3035
|
299 |
[saveDict setObject:appId forKey:GROWL_APP_ID];
|
|
ingmarstein@3035
|
300 |
|
|
ingmarstein@1508
|
301 |
NSData *plistData;
|
|
ingmarstein@1508
|
302 |
NSString *error;
|
|
ingmarstein@1508
|
303 |
plistData = [NSPropertyListSerialization dataFromPropertyList:saveDict
|
|
ingmarstein@1508
|
304 |
format:NSPropertyListBinaryFormat_v1_0
|
|
ingmarstein@1508
|
305 |
errorDescription:&error];
|
|
ingmarstein@2595
|
306 |
if (plistData)
|
|
ingmarstein@1508
|
307 |
[plistData writeToFile:savePath atomically:YES];
|
|
ingmarstein@2595
|
308 |
else
|
|
ingmarstein@1508
|
309 |
NSLog(@"Error writing ticket for application %@: %@", appName, error);
|
|
ingmarstein@1600
|
310 |
[saveDict release];
|
|
ingmarstein@3022
|
311 |
|
|
ingmarstein@3022
|
312 |
changed = NO;
|
|
boredzo@1385
|
313 |
}
|
|
boredzo@1385
|
314 |
|
|
evands@3318
|
315 |
- (void) doSynchronize {
|
|
evands@3318
|
316 |
[self saveTicket];
|
|
evands@3318
|
317 |
|
|
evands@3318
|
318 |
NSNumber *pid = [[NSNumber alloc] initWithInt:[[NSProcessInfo processInfo] processIdentifier]];
|
|
evands@3318
|
319 |
NSDictionary *userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:
|
|
evands@3318
|
320 |
appName, @"TicketName",
|
|
evands@3318
|
321 |
pid, @"pid",
|
|
evands@3318
|
322 |
nil];
|
|
evands@3318
|
323 |
[pid release];
|
|
evands@3318
|
324 |
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:GrowlPreferencesChanged
|
|
evands@3318
|
325 |
object:@"GrowlTicketChanged"
|
|
evands@3318
|
326 |
userInfo:userInfo];
|
|
evands@3318
|
327 |
[userInfo release];
|
|
evands@3318
|
328 |
}
|
|
evands@3318
|
329 |
|
|
ingmarstein@1722
|
330 |
- (void) synchronize {
|
|
evands@3318
|
331 |
if (synchronizeOnChanges) {
|
|
evands@3318
|
332 |
//Coalesce a series of changes into a single message; this makes mass changes (such as registration) much faster.
|
|
evands@3318
|
333 |
[NSObject cancelPreviousPerformRequestsWithTarget:self
|
|
evands@3318
|
334 |
selector:@selector(doSynchronize)
|
|
evands@3318
|
335 |
object:nil];
|
|
evands@3318
|
336 |
[self performSelector:@selector(doSynchronize)
|
|
evands@3318
|
337 |
withObject:nil
|
|
evands@3318
|
338 |
afterDelay:0.5];
|
|
evands@3318
|
339 |
}
|
|
ingmarstein@1722
|
340 |
}
|
|
ingmarstein@1722
|
341 |
|
|
boredzo@1385
|
342 |
#pragma mark -
|
|
boredzo@1385
|
343 |
|
|
boredzo@1385
|
344 |
- (NSImage *) icon {
|
|
ingmarstein@2637
|
345 |
if (icon)
|
|
boredzo@1385
|
346 |
return icon;
|
|
ingmarstein@2637
|
347 |
if (iconData) {
|
|
ingmarstein@2637
|
348 |
icon = [[NSImage alloc] initWithData:iconData];
|
|
ingmarstein@2637
|
349 |
[iconData release];
|
|
ingmarstein@2637
|
350 |
iconData = nil;
|
|
ingmarstein@2637
|
351 |
}
|
|
ingmarstein@2637
|
352 |
if (!icon && appPath)
|
|
ingmarstein@2637
|
353 |
icon = [[[NSWorkspace sharedWorkspace] iconForFile:appPath] retain];
|
|
ingmarstein@2637
|
354 |
if (!icon) {
|
|
ingmarstein@2638
|
355 |
icon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericApplicationIcon)] retain];
|
|
Rudy@4246
|
356 |
[icon setSize:NSMakeSize(128.0, 128.0)];
|
|
ingmarstein@2637
|
357 |
}
|
|
ingmarstein@2637
|
358 |
return icon;
|
|
ingmarstein@2637
|
359 |
}
|
|
ingmarstein@2637
|
360 |
|
|
ingmarstein@2637
|
361 |
- (void) setIcon:(NSImage *)inIcon {
|
|
ingmarstein@1722
|
362 |
if (icon != inIcon) {
|
|
boredzo@3073
|
363 |
if ([inIcon isEqual:icon] || [inIcon isEqual:iconData])
|
|
ingmarstein@3022
|
364 |
return;
|
|
ingmarstein@3022
|
365 |
changed = YES;
|
|
ingmarstein@2637
|
366 |
[icon release];
|
|
ingmarstein@2637
|
367 |
[iconData release];
|
|
ingmarstein@2637
|
368 |
if (inIcon) {
|
|
ingmarstein@2637
|
369 |
if ([inIcon isKindOfClass:[NSImage class]]) {
|
|
ingmarstein@2637
|
370 |
icon = [inIcon copy];
|
|
ingmarstein@2637
|
371 |
iconData = nil;
|
|
ingmarstein@2637
|
372 |
} else {
|
|
ingmarstein@2637
|
373 |
icon = nil;
|
|
ingmarstein@2637
|
374 |
iconData = (NSData *)[inIcon retain];
|
|
ingmarstein@2637
|
375 |
}
|
|
ingmarstein@2637
|
376 |
} else {
|
|
ingmarstein@2637
|
377 |
icon = nil;
|
|
ingmarstein@2637
|
378 |
iconData = nil;
|
|
ingmarstein@2637
|
379 |
}
|
|
boredzo@1385
|
380 |
}
|
|
boredzo@1385
|
381 |
}
|
|
boredzo@1385
|
382 |
|
|
boredzo@1385
|
383 |
- (NSString *) applicationName {
|
|
boredzo@1385
|
384 |
return appName;
|
|
boredzo@1385
|
385 |
}
|
|
boredzo@1385
|
386 |
|
|
boredzo@1385
|
387 |
- (BOOL) ticketEnabled {
|
|
boredzo@1385
|
388 |
return ticketEnabled;
|
|
boredzo@1385
|
389 |
}
|
|
boredzo@1385
|
390 |
|
|
ingmarstein@1720
|
391 |
- (void) setTicketEnabled:(BOOL)inEnabled {
|
|
evands@3318
|
392 |
if (ticketEnabled != inEnabled) {
|
|
evands@3318
|
393 |
ticketEnabled = inEnabled;
|
|
evands@3318
|
394 |
[self synchronize];
|
|
evands@3318
|
395 |
}
|
|
boredzo@1385
|
396 |
}
|
|
boredzo@1385
|
397 |
|
|
ingmarstein@1958
|
398 |
- (BOOL) clickHandlersEnabled {
|
|
ingmarstein@1958
|
399 |
return clickHandlersEnabled;
|
|
ingmarstein@1958
|
400 |
}
|
|
ingmarstein@1958
|
401 |
|
|
ingmarstein@1958
|
402 |
- (void) setClickHandlersEnabled:(BOOL)inEnabled {
|
|
evands@3318
|
403 |
if (clickHandlersEnabled != inEnabled) {
|
|
evands@3318
|
404 |
clickHandlersEnabled = inEnabled;
|
|
evands@3318
|
405 |
|
|
evands@3318
|
406 |
[self synchronize];
|
|
evands@3318
|
407 |
}
|
|
ingmarstein@1958
|
408 |
}
|
|
ingmarstein@1958
|
409 |
|
|
bgannin@3317
|
410 |
- (int) positionType {
|
|
bgannin@3317
|
411 |
return positionType;
|
|
bgannin@3317
|
412 |
}
|
|
bgannin@3317
|
413 |
|
|
bgannin@3317
|
414 |
- (void) setPositionType:(int)inPositionType {
|
|
bgannin@3317
|
415 |
positionType = inPositionType;
|
|
bgannin@3317
|
416 |
[self synchronize];
|
|
bgannin@3317
|
417 |
}
|
|
bgannin@3317
|
418 |
|
|
bgannin@3317
|
419 |
- (int) selectedPosition {
|
|
bgannin@3317
|
420 |
return selectedCustomPosition;
|
|
bgannin@3317
|
421 |
}
|
|
bgannin@3317
|
422 |
|
|
bgannin@3317
|
423 |
- (void) setSelectedPosition:(int)inPosition {
|
|
bgannin@3317
|
424 |
selectedCustomPosition = inPosition;
|
|
bgannin@3317
|
425 |
[self synchronize];
|
|
bgannin@3317
|
426 |
}
|
|
bgannin@3317
|
427 |
|
|
ingmarstein@1720
|
428 |
- (BOOL) useDefaults {
|
|
ingmarstein@1720
|
429 |
return useDefaults;
|
|
ingmarstein@1720
|
430 |
}
|
|
ingmarstein@1720
|
431 |
|
|
ingmarstein@1720
|
432 |
- (void) setUseDefaults:(BOOL)flag {
|
|
ingmarstein@1720
|
433 |
useDefaults = flag;
|
|
ingmarstein@1720
|
434 |
}
|
|
ingmarstein@1720
|
435 |
|
|
ingmarstein@3022
|
436 |
- (BOOL) hasChanged {
|
|
ingmarstein@3022
|
437 |
return changed;
|
|
ingmarstein@3022
|
438 |
}
|
|
ingmarstein@3022
|
439 |
|
|
ingmarstein@3022
|
440 |
- (void) setHasChanged:(BOOL)flag {
|
|
ingmarstein@3022
|
441 |
changed = flag;
|
|
ingmarstein@3022
|
442 |
}
|
|
ingmarstein@3022
|
443 |
|
|
ingmarstein@1720
|
444 |
- (NSString *) displayPluginName {
|
|
ingmarstein@1628
|
445 |
return displayPluginName;
|
|
ingmarstein@1628
|
446 |
}
|
|
ingmarstein@1628
|
447 |
|
|
ingmarstein@2695
|
448 |
- (GrowlDisplayPlugin *) displayPlugin {
|
|
ingmarstein@2595
|
449 |
if (!displayPlugin && displayPluginName)
|
|
eridius@2773
|
450 |
displayPlugin = (GrowlDisplayPlugin *)[[[GrowlPluginController sharedController] displayPluginDictionaryWithName:displayPluginName author:nil version:nil type:nil] pluginInstance];
|
|
boredzo@1385
|
451 |
return displayPlugin;
|
|
boredzo@1385
|
452 |
}
|
|
boredzo@1385
|
453 |
|
|
ingmarstein@1720
|
454 |
- (void) setDisplayPluginName: (NSString *)name {
|
|
evands@3318
|
455 |
if (![displayPluginName isEqualToString:name]) {
|
|
evands@3318
|
456 |
[displayPluginName release];
|
|
evands@3318
|
457 |
displayPluginName = [name copy];
|
|
evands@3318
|
458 |
displayPlugin = nil;
|
|
evands@3318
|
459 |
|
|
evands@3318
|
460 |
[self synchronize];
|
|
evands@3318
|
461 |
}
|
|
boredzo@1385
|
462 |
}
|
|
boredzo@1385
|
463 |
|
|
boredzo@1385
|
464 |
#pragma mark -
|
|
boredzo@1385
|
465 |
|
|
boredzo@1385
|
466 |
- (NSString *) description {
|
|
boredzo@1385
|
467 |
return [NSString stringWithFormat:@"<GrowlApplicationTicket: %p>{\n\tApplicationName: \"%@\"\n\ticon: %@\n\tAll Notifications: %@\n\tDefault Notifications: %@\n\tAllowed Notifications: %@\n\tUse Defaults: %@\n}",
|
|
boredzo@1385
|
468 |
self, appName, icon, allNotifications, defaultNotifications, [self allowedNotifications], ( useDefaults ? @"YES" : @"NO" )];
|
|
boredzo@1385
|
469 |
}
|
|
boredzo@1385
|
470 |
|
|
boredzo@1385
|
471 |
#pragma mark -
|
|
boredzo@1385
|
472 |
|
|
evands@2923
|
473 |
- (void) reregisterWithAllNotifications:(NSArray *)inAllNotes defaults:(id)inDefaults icon:(NSImage *)inIcon {
|
|
boredzo@1385
|
474 |
if (!useDefaults) {
|
|
boredzo@1479
|
475 |
/*We want to respect the user's preferences, but if the application has
|
|
boredzo@1479
|
476 |
* added new notifications since it last registered, we want to enable those
|
|
boredzo@1479
|
477 |
* if the application says to.
|
|
boredzo@1479
|
478 |
*/
|
|
boredzo@1385
|
479 |
NSEnumerator *enumerator;
|
|
ingmarstein@1584
|
480 |
NSMutableDictionary *allNotesCopy = [allNotifications mutableCopy];
|
|
boredzo@1385
|
481 |
|
|
ingmarstein@1579
|
482 |
if ([inDefaults respondsToSelector:@selector(objectEnumerator)] ) {
|
|
ingmarstein@1579
|
483 |
enumerator = [inDefaults objectEnumerator];
|
|
ingmarstein@1579
|
484 |
Class NSNumberClass = [NSNumber class];
|
|
Rudy@4246
|
485 |
NSUInteger numAllNotifications = [inAllNotes count];
|
|
ingmarstein@1579
|
486 |
id obj;
|
|
ingmarstein@1579
|
487 |
while ((obj = [enumerator nextObject])) {
|
|
ingmarstein@1579
|
488 |
NSString *note;
|
|
ingmarstein@1579
|
489 |
if ([obj isKindOfClass:NSNumberClass]) {
|
|
ingmarstein@1579
|
490 |
//it's an index into the all-notifications list
|
|
ingmarstein@1579
|
491 |
unsigned notificationIndex = [obj unsignedIntValue];
|
|
ingmarstein@1579
|
492 |
if (notificationIndex >= numAllNotifications) {
|
|
ingmarstein@1579
|
493 |
NSLog(@"WARNING: application %@ tried to allow notification at index %u by default, but there is no such notification in its list of %u", appName, notificationIndex, numAllNotifications);
|
|
ingmarstein@1580
|
494 |
note = nil;
|
|
ingmarstein@1579
|
495 |
} else {
|
|
ingmarstein@1579
|
496 |
note = [inAllNotes objectAtIndex:notificationIndex];
|
|
ingmarstein@1579
|
497 |
}
|
|
ingmarstein@1579
|
498 |
} else {
|
|
ingmarstein@1579
|
499 |
//it's probably a notification name
|
|
ingmarstein@1579
|
500 |
note = obj;
|
|
ingmarstein@1579
|
501 |
}
|
|
ingmarstein@2943
|
502 |
|
|
evands@2920
|
503 |
if (note && ![allNotesCopy objectForKey:note]) {
|
|
evands@2920
|
504 |
GrowlNotificationTicket *ticket = [GrowlNotificationTicket notificationWithName:note];
|
|
evands@2923
|
505 |
[ticket setHumanReadableName:[humanReadableNames objectForKey:note]];
|
|
evands@2923
|
506 |
[ticket setNotificationDescription:[notificationDescriptions objectForKey:note]];
|
|
evands@2920
|
507 |
[allNotesCopy setObject:ticket forKey:note];
|
|
evands@2920
|
508 |
}
|
|
evands@2920
|
509 |
}
|
|
evands@2920
|
510 |
|
|
ingmarstein@1579
|
511 |
} else if ([inDefaults isKindOfClass:[NSIndexSet class]]) {
|
|
Rudy@4246
|
512 |
NSUInteger notificationIndex;
|
|
Rudy@4246
|
513 |
NSUInteger numAllNotifications = [inAllNotes count];
|
|
ingmarstein@1579
|
514 |
NSIndexSet *iset = (NSIndexSet *)inDefaults;
|
|
ingmarstein@1642
|
515 |
for (notificationIndex = [iset firstIndex]; notificationIndex != NSNotFound; notificationIndex = [iset indexGreaterThanIndex:notificationIndex]) {
|
|
ingmarstein@1579
|
516 |
if (notificationIndex >= numAllNotifications) {
|
|
ingmarstein@1579
|
517 |
NSLog(@"WARNING: application %@ tried to allow notification at index %u by default, but there is no such notification in its list of %u", appName, notificationIndex, numAllNotifications);
|
|
ingmarstein@1579
|
518 |
// index sets are sorted, so we can stop here
|
|
ingmarstein@1579
|
519 |
break;
|
|
ingmarstein@1579
|
520 |
} else {
|
|
ingmarstein@1579
|
521 |
NSString *note = [inAllNotes objectAtIndex:notificationIndex];
|
|
evands@2920
|
522 |
if (![allNotesCopy objectForKey:note]) {
|
|
evands@2920
|
523 |
GrowlNotificationTicket *ticket = [GrowlNotificationTicket notificationWithName:note];
|
|
evands@2923
|
524 |
[ticket setHumanReadableName:[humanReadableNames objectForKey:note]];
|
|
evands@2923
|
525 |
[ticket setNotificationDescription:[notificationDescriptions objectForKey:note]];
|
|
evands@2920
|
526 |
[allNotesCopy setObject:ticket forKey:note];
|
|
ingmarstein@2943
|
527 |
}
|
|
ingmarstein@1579
|
528 |
}
|
|
ingmarstein@1579
|
529 |
}
|
|
evands@2920
|
530 |
|
|
ingmarstein@1579
|
531 |
} else {
|
|
ingmarstein@2587
|
532 |
if (inDefaults)
|
|
ingmarstein@1579
|
533 |
NSLog(@"WARNING: application %@ passed an invalid object for the default notifications: %@.", appName, inDefaults);
|
|
ingmarstein@1579
|
534 |
}
|
|
ingmarstein@1579
|
535 |
|
|
boredzo@3073
|
536 |
if (![allNotifications isEqual:allNotesCopy]) {
|
|
ingmarstein@3022
|
537 |
[allNotifications release];
|
|
ingmarstein@3022
|
538 |
allNotifications = allNotesCopy;
|
|
ingmarstein@3022
|
539 |
changed = YES;
|
|
ingmarstein@3022
|
540 |
} else {
|
|
ingmarstein@3022
|
541 |
[allNotesCopy release];
|
|
ingmarstein@3022
|
542 |
}
|
|
boredzo@1385
|
543 |
}
|
|
boredzo@1385
|
544 |
|
|
boredzo@1385
|
545 |
//ALWAYS set all notifications list first, to enable handling of numeric indices in the default notifications list!
|
|
boredzo@1385
|
546 |
[self setAllNotifications:inAllNotes];
|
|
boredzo@1385
|
547 |
[self setDefaultNotifications:inDefaults];
|
|
boredzo@1479
|
548 |
|
|
boredzo@1479
|
549 |
[self setIcon:inIcon];
|
|
boredzo@1479
|
550 |
}
|
|
ingmarstein@1507
|
551 |
|
|
ingmarstein@2637
|
552 |
- (void) reregisterWithDictionary:(NSDictionary *)dict {
|
|
boredzo@1479
|
553 |
NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
|
|
boredzo@1479
|
554 |
|
|
ingmarstein@2638
|
555 |
NSImage *appIcon = [dict objectForKey:GROWL_APP_ICON];
|
|
ingmarstein@3035
|
556 |
NSString *bundleId = [dict objectForKey:GROWL_APP_ID];
|
|
ingmarstein@3035
|
557 |
|
|
ingmarstein@3035
|
558 |
if (bundleId != appId && ![bundleId isEqualToString:appId]) {
|
|
ingmarstein@3035
|
559 |
[appId release];
|
|
ingmarstein@3035
|
560 |
appId = [bundleId retain];
|
|
ingmarstein@3035
|
561 |
changed = YES;
|
|
ingmarstein@3035
|
562 |
}
|
|
boredzo@1479
|
563 |
|
|
boredzo@1479
|
564 |
//XXX - should assimilate reregisterWithAllNotifications:defaults:icon: here
|
|
ingmarstein@3022
|
565 |
NSArray *all = [dict objectForKey:GROWL_NOTIFICATIONS_ALL];
|
|
ingmarstein@3022
|
566 |
NSArray *defaults = [dict objectForKey:GROWL_NOTIFICATIONS_DEFAULT];
|
|
ingmarstein@3022
|
567 |
|
|
ingmarstein@3022
|
568 |
NSDictionary *newNames = [dict objectForKey:GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES];
|
|
boredzo@3073
|
569 |
if (newNames != humanReadableNames && ![newNames isEqual:humanReadableNames]) {
|
|
ingmarstein@3022
|
570 |
[humanReadableNames release];
|
|
ingmarstein@3022
|
571 |
humanReadableNames = [newNames retain];
|
|
ingmarstein@3022
|
572 |
changed = YES;
|
|
ingmarstein@3022
|
573 |
}
|
|
ingmarstein@3022
|
574 |
|
|
ingmarstein@3022
|
575 |
NSDictionary *newDescriptions = [dict objectForKey:GROWL_NOTIFICATIONS_DESCRIPTIONS];
|
|
boredzo@3073
|
576 |
if (newDescriptions != notificationDescriptions && ![newDescriptions isEqual:notificationDescriptions]) {
|
|
ingmarstein@3022
|
577 |
[notificationDescriptions release];
|
|
ingmarstein@3022
|
578 |
notificationDescriptions = [newDescriptions retain];
|
|
ingmarstein@3022
|
579 |
changed = YES;
|
|
ingmarstein@3022
|
580 |
}
|
|
evands@2924
|
581 |
|
|
ingmarstein@1777
|
582 |
if (!defaults) defaults = all;
|
|
boredzo@1678
|
583 |
[self reregisterWithAllNotifications:all
|
|
boredzo@1678
|
584 |
defaults:defaults
|
|
ingmarstein@2638
|
585 |
icon:appIcon];
|
|
boredzo@1479
|
586 |
|
|
boredzo@1479
|
587 |
NSString *fullPath = nil;
|
|
boredzo@1479
|
588 |
id location = [dict objectForKey:GROWL_APP_LOCATION];
|
|
ingmarstein@1483
|
589 |
if (location) {
|
|
ingmarstein@1483
|
590 |
if ([location isKindOfClass:[NSDictionary class]]) {
|
|
boredzo@1479
|
591 |
NSDictionary *file_data = [location objectForKey:@"file-data"];
|
|
ingmarstein@3035
|
592 |
CFURLRef url = (CFURLRef)createFileURLWithDockDescription(file_data);
|
|
ingmarstein@3035
|
593 |
if (url) {
|
|
ingmarstein@3035
|
594 |
fullPath = [(NSString *)CFURLCopyPath(url) autorelease];
|
|
Rudy@4261
|
595 |
if(fullPath)
|
|
Rudy@4261
|
596 |
CFMakeCollectable(fullPath);
|
|
ingmarstein@3035
|
597 |
CFRelease(url);
|
|
ingmarstein@3035
|
598 |
}
|
|
ingmarstein@1483
|
599 |
} else if ([location isKindOfClass:[NSString class]]) {
|
|
boredzo@1479
|
600 |
fullPath = location;
|
|
ingmarstein@2587
|
601 |
if (![[NSFileManager defaultManager] fileExistsAtPath:fullPath])
|
|
boredzo@1479
|
602 |
fullPath = nil;
|
|
ingmarstein@1483
|
603 |
}
|
|
ingmarstein@1748
|
604 |
/* Don't handle the NSNumber case here, the app might have moved and we
|
|
ingmarstein@1748
|
605 |
* use the re-registration to update our stored appPath.
|
|
ingmarstein@1748
|
606 |
*/
|
|
ingmarstein@1483
|
607 |
}
|
|
ingmarstein@3035
|
608 |
if (!fullPath) {
|
|
ingmarstein@3035
|
609 |
if (appId) {
|
|
ingmarstein@3035
|
610 |
CFURLRef appURL = NULL;
|
|
ingmarstein@3035
|
611 |
OSStatus err = LSFindApplicationForInfo(kLSUnknownCreator,
|
|
ingmarstein@3035
|
612 |
(CFStringRef)appId,
|
|
ingmarstein@3035
|
613 |
/*inName*/ NULL,
|
|
ingmarstein@3035
|
614 |
/*outAppRef*/ NULL,
|
|
ingmarstein@3035
|
615 |
&appURL);
|
|
ingmarstein@3035
|
616 |
if (err == noErr) {
|
|
ingmarstein@3035
|
617 |
fullPath = [(NSString *)CFURLCopyPath(appURL) autorelease];
|
|
Rudy@4261
|
618 |
if(fullPath)
|
|
Rudy@4261
|
619 |
CFMakeCollectable(fullPath);
|
|
ingmarstein@3035
|
620 |
CFRelease(appURL);
|
|
ingmarstein@3035
|
621 |
}
|
|
ingmarstein@3035
|
622 |
}
|
|
ingmarstein@3035
|
623 |
if (!fullPath)
|
|
ingmarstein@3035
|
624 |
fullPath = [workspace fullPathForApplication:appName];
|
|
ingmarstein@3035
|
625 |
}
|
|
ingmarstein@3022
|
626 |
if (fullPath != appPath && ![fullPath isEqualToString:appPath]) {
|
|
ingmarstein@3022
|
627 |
[appPath release];
|
|
ingmarstein@3022
|
628 |
appPath = [fullPath retain];
|
|
ingmarstein@3022
|
629 |
changed = YES;
|
|
ingmarstein@3022
|
630 |
}
|
|
boredzo@1385
|
631 |
}
|
|
boredzo@1385
|
632 |
|
|
boredzo@1385
|
633 |
- (NSArray *) allNotifications {
|
|
boredzo@1385
|
634 |
return [[[allNotifications allKeys] retain] autorelease];
|
|
boredzo@1385
|
635 |
}
|
|
boredzo@1385
|
636 |
|
|
ingmarstein@3022
|
637 |
- (void) setAllNotifications:(NSArray *)inArray {
|
|
boredzo@1385
|
638 |
if (allNotificationNames != inArray) {
|
|
ingmarstein@3022
|
639 |
if ([inArray isEqualToArray:allNotificationNames])
|
|
ingmarstein@3022
|
640 |
return;
|
|
ingmarstein@3022
|
641 |
changed = YES;
|
|
ingmarstein@1600
|
642 |
[allNotificationNames release];
|
|
ingmarstein@1600
|
643 |
allNotificationNames = [inArray retain];
|
|
ingmarstein@1600
|
644 |
|
|
boredzo@1385
|
645 |
//We want to keep all of the old notification settings and create entries for the new ones
|
|
ingmarstein@1600
|
646 |
NSEnumerator *newEnum = [inArray objectEnumerator];
|
|
ingmarstein@1600
|
647 |
NSMutableDictionary *tmp = [[NSMutableDictionary alloc] initWithCapacity:[inArray count]];
|
|
boredzo@1385
|
648 |
id key, obj;
|
|
ingmarstein@1579
|
649 |
while ((key = [newEnum nextObject])) {
|
|
boredzo@1385
|
650 |
obj = [allNotifications objectForKey:key];
|
|
ingmarstein@1579
|
651 |
if (obj) {
|
|
boredzo@1385
|
652 |
[tmp setObject:obj forKey:key];
|
|
boredzo@1385
|
653 |
} else {
|
|
boredzo@2501
|
654 |
GrowlNotificationTicket *notification = [[GrowlNotificationTicket alloc] initWithName:key];
|
|
evands@2920
|
655 |
[notification setHumanReadableName:[humanReadableNames objectForKey:key]];
|
|
evands@2923
|
656 |
[notification setNotificationDescription:[notificationDescriptions objectForKey:key]];
|
|
ingmarstein@1610
|
657 |
[tmp setObject:notification forKey:key];
|
|
ingmarstein@1610
|
658 |
[notification release];
|
|
boredzo@1385
|
659 |
}
|
|
boredzo@1385
|
660 |
}
|
|
boredzo@1385
|
661 |
[allNotifications release];
|
|
ingmarstein@1600
|
662 |
allNotifications = tmp;
|
|
ingmarstein@1600
|
663 |
|
|
boredzo@1385
|
664 |
// And then make sure the list of default notifications also doesn't have any straglers...
|
|
ingmarstein@1600
|
665 |
NSMutableSet *cur = [[NSMutableSet alloc] initWithArray:defaultNotifications];
|
|
ingmarstein@1600
|
666 |
NSSet *new = [[NSSet alloc] initWithArray:allNotificationNames];
|
|
boredzo@1385
|
667 |
[cur intersectSet:new];
|
|
ingmarstein@1600
|
668 |
[defaultNotifications release];
|
|
boredzo@1385
|
669 |
defaultNotifications = [[cur allObjects] retain];
|
|
ingmarstein@1600
|
670 |
[cur release];
|
|
ingmarstein@1600
|
671 |
[new release];
|
|
boredzo@1385
|
672 |
}
|
|
boredzo@1385
|
673 |
}
|
|
boredzo@1385
|
674 |
|
|
boredzo@1385
|
675 |
- (NSArray *) defaultNotifications {
|
|
boredzo@1385
|
676 |
return [[defaultNotifications retain] autorelease];
|
|
boredzo@1385
|
677 |
}
|
|
boredzo@1385
|
678 |
|
|
ingmarstein@3022
|
679 |
- (void) setDefaultNotifications:(id)inObject {
|
|
boredzo@1385
|
680 |
if (!allNotifications) {
|
|
boredzo@1385
|
681 |
/*WARNING: if you try to pass an array containing numeric indices, and
|
|
boredzo@1385
|
682 |
* the all-notifications list has not been supplied yet, the indices
|
|
boredzo@1385
|
683 |
* WILL NOT be dereferenced. ALWAYS set the all-notifications list FIRST.
|
|
boredzo@1385
|
684 |
*/
|
|
boredzo@3073
|
685 |
if (![defaultNotifications isEqual:inObject]) {
|
|
ingmarstein@3022
|
686 |
[defaultNotifications release];
|
|
ingmarstein@3022
|
687 |
defaultNotifications = [inObject retain];
|
|
ingmarstein@3022
|
688 |
changed = YES;
|
|
ingmarstein@3022
|
689 |
}
|
|
boredzo@1385
|
690 |
} else if ([inObject respondsToSelector:@selector(objectEnumerator)] ) {
|
|
boredzo@1385
|
691 |
NSEnumerator *mightBeIndicesEnum = [inObject objectEnumerator];
|
|
boredzo@1385
|
692 |
NSNumber *num;
|
|
Rudy@4246
|
693 |
NSUInteger numDefaultNotifications;
|
|
Rudy@4246
|
694 |
NSUInteger numAllNotifications = [allNotificationNames count];
|
|
ingmarstein@3022
|
695 |
if ([inObject respondsToSelector:@selector(count)])
|
|
boredzo@1385
|
696 |
numDefaultNotifications = [inObject count];
|
|
ingmarstein@3022
|
697 |
else
|
|
boredzo@1385
|
698 |
numDefaultNotifications = numAllNotifications;
|
|
boredzo@1385
|
699 |
NSMutableArray *mDefaultNotifications = [[NSMutableArray alloc] initWithCapacity:numDefaultNotifications];
|
|
boredzo@1385
|
700 |
Class NSNumberClass = [NSNumber class];
|
|
boredzo@1385
|
701 |
while ((num = [mightBeIndicesEnum nextObject])) {
|
|
boredzo@1385
|
702 |
if ([num isKindOfClass:NSNumberClass]) {
|
|
boredzo@1385
|
703 |
//it's an index into the all-notifications list
|
|
boredzo@1385
|
704 |
unsigned notificationIndex = [num unsignedIntValue];
|
|
ingmarstein@2587
|
705 |
if (notificationIndex >= numAllNotifications)
|
|
boredzo@1385
|
706 |
NSLog(@"WARNING: application %@ tried to allow notification at index %u by default, but there is no such notification in its list of %u", appName, notificationIndex, numAllNotifications);
|
|
ingmarstein@2587
|
707 |
else
|
|
boredzo@1385
|
708 |
[mDefaultNotifications addObject:[allNotificationNames objectAtIndex:notificationIndex]];
|
|
boredzo@1385
|
709 |
} else {
|
|
boredzo@1385
|
710 |
//it's probably a notification name
|
|
boredzo@1385
|
711 |
[mDefaultNotifications addObject:num];
|
|
boredzo@1385
|
712 |
}
|
|
boredzo@1385
|
713 |
}
|
|
ingmarstein@3022
|
714 |
if (![defaultNotifications isEqualToArray:mDefaultNotifications]) {
|
|
ingmarstein@3022
|
715 |
[defaultNotifications release];
|
|
ingmarstein@3022
|
716 |
defaultNotifications = mDefaultNotifications;
|
|
ingmarstein@3022
|
717 |
changed = YES;
|
|
ingmarstein@3022
|
718 |
} else {
|
|
ingmarstein@3022
|
719 |
[mDefaultNotifications release];
|
|
ingmarstein@3022
|
720 |
}
|
|
boredzo@1385
|
721 |
} else if ([inObject isKindOfClass:[NSIndexSet class]]) {
|
|
Rudy@4246
|
722 |
NSUInteger notificationIndex;
|
|
Rudy@4246
|
723 |
NSUInteger numAllNotifications = [allNotificationNames count];
|
|
boredzo@1385
|
724 |
NSIndexSet *iset = (NSIndexSet *)inObject;
|
|
boredzo@1385
|
725 |
NSMutableArray *mDefaultNotifications = [[NSMutableArray alloc] initWithCapacity:[iset count]];
|
|
ingmarstein@1642
|
726 |
for (notificationIndex = [iset firstIndex]; notificationIndex != NSNotFound; notificationIndex = [iset indexGreaterThanIndex:notificationIndex]) {
|
|
boredzo@1385
|
727 |
if (notificationIndex >= numAllNotifications) {
|
|
boredzo@1385
|
728 |
NSLog(@"WARNING: application %@ tried to allow notification at index %u by default, but there is no such notification in its list of %u", appName, notificationIndex, numAllNotifications);
|
|
boredzo@1385
|
729 |
// index sets are sorted, so we can stop here
|
|
boredzo@1385
|
730 |
break;
|
|
boredzo@1385
|
731 |
} else {
|
|
boredzo@1385
|
732 |
[mDefaultNotifications addObject:[allNotificationNames objectAtIndex:notificationIndex]];
|
|
boredzo@1385
|
733 |
}
|
|
boredzo@1385
|
734 |
}
|
|
ingmarstein@3022
|
735 |
if (![defaultNotifications isEqualToArray:mDefaultNotifications]) {
|
|
ingmarstein@3022
|
736 |
[defaultNotifications release];
|
|
ingmarstein@3022
|
737 |
defaultNotifications = mDefaultNotifications;
|
|
ingmarstein@3022
|
738 |
changed = YES;
|
|
ingmarstein@3022
|
739 |
} else {
|
|
ingmarstein@3022
|
740 |
[mDefaultNotifications release];
|
|
ingmarstein@3022
|
741 |
}
|
|
boredzo@1385
|
742 |
} else {
|
|
ingmarstein@2567
|
743 |
if (inObject)
|
|
ingmarstein@1474
|
744 |
NSLog(@"WARNING: application %@ passed an invalid object for the default notifications: %@.", appName, inObject);
|
|
ingmarstein@3022
|
745 |
if (![defaultNotifications isEqualToArray:allNotificationNames]) {
|
|
ingmarstein@3022
|
746 |
[defaultNotifications release];
|
|
ingmarstein@3022
|
747 |
defaultNotifications = [allNotificationNames retain];
|
|
ingmarstein@3022
|
748 |
changed = YES;
|
|
ingmarstein@3022
|
749 |
}
|
|
boredzo@1385
|
750 |
}
|
|
boredzo@1385
|
751 |
|
|
ingmarstein@2567
|
752 |
if (useDefaults)
|
|
ingmarstein@1610
|
753 |
[self setAllowedNotificationsToDefault];
|
|
boredzo@1385
|
754 |
}
|
|
boredzo@1385
|
755 |
|
|
boredzo@1385
|
756 |
- (NSArray *) allowedNotifications {
|
|
boredzo@1385
|
757 |
NSMutableArray* allowed = [NSMutableArray array];
|
|
boredzo@1385
|
758 |
NSEnumerator *notificationEnum = [allNotifications objectEnumerator];
|
|
ingmarstein@2567
|
759 |
GrowlNotificationTicket *obj;
|
|
ingmarstein@2567
|
760 |
while ((obj = [notificationEnum nextObject]))
|
|
ingmarstein@2567
|
761 |
if ([obj enabled])
|
|
boredzo@1385
|
762 |
[allowed addObject:[obj name]];
|
|
boredzo@1385
|
763 |
return allowed;
|
|
boredzo@1385
|
764 |
}
|
|
boredzo@1385
|
765 |
|
|
boredzo@1385
|
766 |
- (void) setAllowedNotifications:(NSArray *) inArray {
|
|
ingmarstein@2567
|
767 |
NSSet *allowed = [[NSSet alloc] initWithArray:inArray];
|
|
ingmarstein@2567
|
768 |
NSEnumerator *notificationEnum = [allNotifications objectEnumerator];
|
|
ingmarstein@2567
|
769 |
GrowlNotificationTicket *obj;
|
|
ingmarstein@2450
|
770 |
while ((obj = [notificationEnum nextObject]))
|
|
ingmarstein@2567
|
771 |
[obj setEnabled:[allowed containsObject:[obj name]]];
|
|
ingmarstein@2567
|
772 |
[allowed release];
|
|
ingmarstein@2567
|
773 |
|
|
boredzo@1385
|
774 |
useDefaults = NO;
|
|
boredzo@1385
|
775 |
}
|
|
boredzo@1385
|
776 |
|
|
boredzo@1385
|
777 |
- (void) setAllowedNotificationsToDefault {
|
|
boredzo@1385
|
778 |
[self setAllowedNotifications:defaultNotifications];
|
|
boredzo@1385
|
779 |
useDefaults = YES;
|
|
boredzo@1385
|
780 |
}
|
|
boredzo@1385
|
781 |
|
|
boredzo@1385
|
782 |
- (BOOL) isNotificationAllowed:(NSString *) name {
|
|
ingmarstein@1720
|
783 |
return ticketEnabled && [[allNotifications objectForKey:name] enabled];
|
|
ingmarstein@1720
|
784 |
}
|
|
ingmarstein@1720
|
785 |
|
|
ingmarstein@1720
|
786 |
- (NSComparisonResult) caseInsensitiveCompare:(GrowlApplicationTicket *)aTicket {
|
|
ingmarstein@1720
|
787 |
return [appName caseInsensitiveCompare:[aTicket applicationName]];
|
|
boredzo@1385
|
788 |
}
|
|
boredzo@1385
|
789 |
|
|
boredzo@1385
|
790 |
#pragma mark Notification Accessors
|
|
ingmarstein@1720
|
791 |
- (NSArray *) notifications {
|
|
ingmarstein@1720
|
792 |
return [allNotifications allValues];
|
|
ingmarstein@1720
|
793 |
}
|
|
ingmarstein@1720
|
794 |
|
|
boredzo@2501
|
795 |
- (GrowlNotificationTicket *) notificationTicketForName:(NSString *)name {
|
|
ingmarstein@1720
|
796 |
return [allNotifications objectForKey:name];
|
|
ingmarstein@1583
|
797 |
}
|
|
boredzo@1385
|
798 |
@end
|