2 // GrowlApplicationTicket.m
5 // Created by Karl Adam on Tue Apr 27 2004.
6 // Copyright 2004-2006 The Growl Project. All rights reserved.
8 // This file is under the BSD License, refer to License.txt for details
11 #import "GrowlApplicationTicket.h"
12 #import "GrowlNotificationTicket.h"
13 #import "GrowlDefines.h"
14 #import "GrowlDisplayPlugin.h"
15 #import "NSWorkspaceAdditions.h"
16 #import "GrowlPathUtilities.h"
17 #include "CFGrowlAdditions.h"
18 #include "CFURLAdditions.h"
19 #include "CFDictionaryAdditions.h"
21 #define UseDefaultsKey @"useDefaults"
22 #define TicketEnabledKey @"ticketEnabled"
23 #define ClickHandlersEnabledKey @"clickHandlersEnabled"
24 #define PositionTypeKey @"positionType"
28 @implementation GrowlApplicationTicket
30 //these are specifically for auto-discovery tickets, hence the requirement of GROWL_TICKET_VERSION.
31 + (BOOL) isValidTicketDictionary:(NSDictionary *)dict {
35 NSNumber *versionNum = getObjectForKey(dict, GROWL_TICKET_VERSION);
36 if ([versionNum intValue] == 1) {
37 return getObjectForKey(dict, GROWL_NOTIFICATIONS_ALL)
38 && getObjectForKey(dict, GROWL_APP_NAME);
44 + (BOOL) isKnownTicketVersion:(NSDictionary *)dict {
45 id version = getObjectForKey(dict, GROWL_TICKET_VERSION);
46 return version && ([version intValue] == 1);
51 + (id) ticketWithDictionary:(NSDictionary *)ticketDict {
52 return [[[GrowlApplicationTicket alloc] initWithDictionary:ticketDict] autorelease];
55 - (id) initWithDictionary:(NSDictionary *)ticketDict {
58 NSParameterAssert(ticketDict != nil);
61 if ((self = [self init])) {
62 synchronizeOnChanges = NO;
64 appName = [getObjectForKey(ticketDict, GROWL_APP_NAME) retain];
65 appId = [getObjectForKey(ticketDict, GROWL_APP_ID) retain];
67 humanReadableNames = [[ticketDict objectForKey:GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES] retain];
68 notificationDescriptions = [[ticketDict objectForKey:GROWL_NOTIFICATIONS_DESCRIPTIONS] retain];
70 //Get all the notification names and the data about them
71 allNotificationNames = [ticketDict objectForKey:GROWL_NOTIFICATIONS_ALL];
72 NSAssert1(allNotificationNames, @"Ticket dictionaries must contain a list of all their notifications (application name: %@)", appName);
74 NSArray *inDefaults = [ticketDict objectForKey:GROWL_NOTIFICATIONS_DEFAULT];
75 if (!inDefaults) inDefaults = allNotificationNames;
77 NSEnumerator *notificationsEnum = [allNotificationNames objectEnumerator];
78 NSMutableDictionary *allNotificationsTemp = [[NSMutableDictionary alloc] initWithCapacity:[allNotificationNames count]];
79 NSMutableArray *allNamesTemp = [[NSMutableArray alloc] initWithCapacity:[allNotificationNames count]];
81 while ((obj = [notificationsEnum nextObject])) {
83 GrowlNotificationTicket *notification;
84 if ([obj isKindOfClass:[NSString class]]) {
86 notification = [[GrowlNotificationTicket alloc] initWithName:obj];
88 name = [obj objectForKey:@"Name"];
89 notification = [[GrowlNotificationTicket alloc] initWithDictionary:obj];
91 [allNamesTemp addObject:name];
92 [notification setTicket:self];
94 //Set the human readable name if we were supplied one
95 [notification setHumanReadableName:[humanReadableNames objectForKey:name]];
96 [notification setNotificationDescription:[notificationDescriptions objectForKey:name]];
98 [allNotificationsTemp setObject:notification forKey:name];
99 [notification release];
101 allNotifications = allNotificationsTemp;
102 allNotificationNames = allNamesTemp;
105 NSString *fullPath = nil;
106 id location = getObjectForKey(ticketDict, GROWL_APP_LOCATION);
108 if ([location isKindOfClass:[NSDictionary class]]) {
109 NSDictionary *file_data = getObjectForKey((NSDictionary *)location, @"file-data");
110 CFURLRef url = (CFURLRef)createFileURLWithDockDescription(file_data);
112 fullPath = [(NSString *)CFURLCopyPath(url) autorelease];
115 } else if ([location isKindOfClass:[NSString class]]) {
117 if (![[NSFileManager defaultManager] fileExistsAtPath:fullPath])
119 } else if ([location isKindOfClass:[NSNumber class]]) {
120 doLookup = [location boolValue];
123 if (!fullPath && doLookup) {
125 CFURLRef appURL = NULL;
126 OSStatus err = LSFindApplicationForInfo(kLSUnknownCreator,
132 fullPath = [(NSString *)CFURLCopyPath(appURL) autorelease];
137 fullPath = [[NSWorkspace sharedWorkspace] fullPathForApplication:appName];
139 appPath = [fullPath retain];
140 // NSLog(@"got appPath: %@", appPath);
142 [self setIcon:getObjectForKey(ticketDict, GROWL_APP_ICON)];
144 id value = getObjectForKey(ticketDict, UseDefaultsKey);
146 useDefaults = [value boolValue];
150 value = getObjectForKey(ticketDict, TicketEnabledKey);
152 ticketEnabled = [value boolValue];
156 displayPluginName = [[ticketDict objectForKey:GrowlDisplayPluginKey] copy];
158 value = getObjectForKey(ticketDict, ClickHandlersEnabledKey);
160 clickHandlersEnabled = [value boolValue];
162 clickHandlersEnabled = YES;
164 value = getObjectForKey(ticketDict, PositionTypeKey);
166 positionType = [value intValue];
170 value = getObjectForKey(ticketDict, GROWL_POSITION_PREFERENCE_KEY);
172 selectedCustomPosition = [value intValue];
174 selectedCustomPosition = 0;
176 [self setDefaultNotifications:inDefaults];
179 synchronizeOnChanges = YES;
190 [allNotifications release];
191 [defaultNotifications release];
192 [humanReadableNames release];
193 [notificationDescriptions release];
194 [allNotificationNames release];
195 [displayPluginName release];
202 - (id) initTicketFromPath:(NSString *) ticketPath {
203 CFURLRef ticketURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)ticketPath, kCFURLPOSIXPathStyle, false);
204 NSDictionary *ticketDict = (NSDictionary *)createPropertyListFromURL((NSURL *)ticketURL, kCFPropertyListImmutable, NULL, NULL);
205 CFRelease(ticketURL);
207 NSLog(@"Tried to init a ticket from this file, but it isn't a ticket file: %@", ticketPath);
212 self = [self initWithDictionary:ticketDict];
213 [ticketDict release];
217 - (id) initTicketForApplication: (NSString *) inApp {
218 return [self initTicketFromPath:[[[[GrowlPathUtilities growlSupportDirectory]
219 stringByAppendingPathComponent:@"Tickets"]
220 stringByAppendingPathComponent:inApp]
221 stringByAppendingPathExtension:@"growlTicket"]];
224 - (NSString *) path {
225 NSString *destDir = [GrowlPathUtilities growlSupportDirectory];
226 destDir = [destDir stringByAppendingPathComponent:@"Tickets"];
227 destDir = [destDir stringByAppendingPathComponent:[appName stringByAppendingPathExtension:@"growlTicket"]];
231 - (void) saveTicket {
232 NSString *destDir = [GrowlPathUtilities growlSupportDirectory];
233 destDir = [destDir stringByAppendingPathComponent:@"Tickets"];
235 [self saveTicketToPath:destDir];
238 - (void) saveTicketToPath:(NSString *)destDir {
239 // Save a Plist file of this object to configure the prefs of apps that aren't running
240 // construct a dictionary of our state data then save that dictionary to a file.
241 NSString *savePath = [destDir stringByAppendingPathComponent:[appName stringByAppendingPathExtension:@"growlTicket"]];
242 NSMutableArray *saveNotifications = [[NSMutableArray alloc] initWithCapacity:[allNotifications count]];
243 NSEnumerator *notificationEnum = [allNotifications objectEnumerator];
244 GrowlNotificationTicket *obj;
245 while ((obj = [notificationEnum nextObject]))
246 [saveNotifications addObject:[obj dictionaryRepresentation]];
248 NSDictionary *file_data = nil;
250 NSURL *url = [[NSURL alloc] initFileURLWithPath:appPath];
251 file_data = createDockDescriptionWithURL(url);
255 id location = file_data ? [NSDictionary dictionaryWithObject:file_data forKey:@"file-data"] : appPath;
257 location = [NSNumber numberWithBool:NO];
260 NSNumber *useDefaultsValue = [[NSNumber alloc] initWithBool:useDefaults];
261 NSNumber *ticketEnabledValue = [[NSNumber alloc] initWithBool:ticketEnabled];
262 NSNumber *clickHandlersEnabledValue = [[NSNumber alloc] initWithBool:clickHandlersEnabled];
263 NSNumber *positionTypeValue = [[NSNumber alloc] initWithInt:positionType];
264 NSNumber *selectedCustomPositionValue = [[NSNumber alloc] initWithInt:selectedCustomPosition];
265 NSData *theIconData = iconData;
267 NSImage *theIcon = [self icon];
268 theIconData = theIcon ? [theIcon TIFFRepresentation] : [NSData data];
270 NSMutableDictionary *saveDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
271 appName, GROWL_APP_NAME,
272 saveNotifications, GROWL_NOTIFICATIONS_ALL,
273 defaultNotifications, GROWL_NOTIFICATIONS_DEFAULT,
274 theIconData, GROWL_APP_ICON,
275 useDefaultsValue, UseDefaultsKey,
276 ticketEnabledValue, TicketEnabledKey,
277 clickHandlersEnabledValue, ClickHandlersEnabledKey,
278 positionTypeValue, PositionTypeKey,
279 selectedCustomPositionValue, GROWL_POSITION_PREFERENCE_KEY,
280 location, GROWL_APP_LOCATION,
282 [useDefaultsValue release];
283 [ticketEnabledValue release];
284 [clickHandlersEnabledValue release];
285 [positionTypeValue release];
286 [selectedCustomPositionValue release];
287 [saveNotifications release];
289 if (displayPluginName)
290 [saveDict setObject:displayPluginName forKey:GrowlDisplayPluginKey];
292 if (humanReadableNames)
293 [saveDict setObject:humanReadableNames forKey:GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES];
295 if (notificationDescriptions)
296 [saveDict setObject:notificationDescriptions forKey:GROWL_NOTIFICATIONS_DESCRIPTIONS];
299 [saveDict setObject:appId forKey:GROWL_APP_ID];
303 plistData = [NSPropertyListSerialization dataFromPropertyList:saveDict
304 format:NSPropertyListBinaryFormat_v1_0
305 errorDescription:&error];
307 [plistData writeToFile:savePath atomically:YES];
309 NSLog(@"Error writing ticket for application %@: %@", appName, error);
315 - (void) doSynchronize {
318 NSNumber *pid = [[NSNumber alloc] initWithInt:[[NSProcessInfo processInfo] processIdentifier]];
319 NSDictionary *userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:
320 appName, @"TicketName",
324 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:GrowlPreferencesChanged
325 object:@"GrowlTicketChanged"
330 - (void) synchronize {
331 if (synchronizeOnChanges) {
332 //Coalesce a series of changes into a single message; this makes mass changes (such as registration) much faster.
333 [NSObject cancelPreviousPerformRequestsWithTarget:self
334 selector:@selector(doSynchronize)
336 [self performSelector:@selector(doSynchronize)
348 icon = [[NSImage alloc] initWithData:iconData];
352 if (!icon && appPath)
353 icon = [[[NSWorkspace sharedWorkspace] iconForFile:appPath] retain];
355 icon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericApplicationIcon)] retain];
356 [icon setSize:NSMakeSize(128.0, 128.0)];
361 - (void) setIcon:(NSImage *)inIcon {
362 if (icon != inIcon) {
363 if ([inIcon isEqual:icon] || [inIcon isEqual:iconData])
369 if ([inIcon isKindOfClass:[NSImage class]]) {
370 icon = [inIcon copy];
374 iconData = (NSData *)[inIcon retain];
383 - (NSString *) applicationName {
387 - (BOOL) ticketEnabled {
388 return ticketEnabled;
391 - (void) setTicketEnabled:(BOOL)inEnabled {
392 if (ticketEnabled != inEnabled) {
393 ticketEnabled = inEnabled;
398 - (BOOL) clickHandlersEnabled {
399 return clickHandlersEnabled;
402 - (void) setClickHandlersEnabled:(BOOL)inEnabled {
403 if (clickHandlersEnabled != inEnabled) {
404 clickHandlersEnabled = inEnabled;
410 - (int) positionType {
414 - (void) setPositionType:(int)inPositionType {
415 positionType = inPositionType;
419 - (int) selectedPosition {
420 return selectedCustomPosition;
423 - (void) setSelectedPosition:(int)inPosition {
424 selectedCustomPosition = inPosition;
428 - (BOOL) useDefaults {
432 - (void) setUseDefaults:(BOOL)flag {
436 - (BOOL) hasChanged {
440 - (void) setHasChanged:(BOOL)flag {
444 - (NSString *) displayPluginName {
445 return displayPluginName;
448 - (GrowlDisplayPlugin *) displayPlugin {
449 if (!displayPlugin && displayPluginName)
450 displayPlugin = (GrowlDisplayPlugin *)[[[GrowlPluginController sharedController] displayPluginDictionaryWithName:displayPluginName author:nil version:nil type:nil] pluginInstance];
451 return displayPlugin;
454 - (void) setDisplayPluginName: (NSString *)name {
455 if (![displayPluginName isEqualToString:name]) {
456 [displayPluginName release];
457 displayPluginName = [name copy];
466 - (NSString *) description {
467 return [NSString stringWithFormat:@"<GrowlApplicationTicket: %p>{\n\tApplicationName: \"%@\"\n\ticon: %@\n\tAll Notifications: %@\n\tDefault Notifications: %@\n\tAllowed Notifications: %@\n\tUse Defaults: %@\n}",
468 self, appName, icon, allNotifications, defaultNotifications, [self allowedNotifications], ( useDefaults ? @"YES" : @"NO" )];
473 - (void) reregisterWithAllNotifications:(NSArray *)inAllNotes defaults:(id)inDefaults icon:(NSImage *)inIcon {
475 /*We want to respect the user's preferences, but if the application has
476 * added new notifications since it last registered, we want to enable those
477 * if the application says to.
479 NSEnumerator *enumerator;
480 NSMutableDictionary *allNotesCopy = [allNotifications mutableCopy];
482 if ([inDefaults respondsToSelector:@selector(objectEnumerator)] ) {
483 enumerator = [inDefaults objectEnumerator];
484 Class NSNumberClass = [NSNumber class];
485 NSUInteger numAllNotifications = [inAllNotes count];
487 while ((obj = [enumerator nextObject])) {
489 if ([obj isKindOfClass:NSNumberClass]) {
490 //it's an index into the all-notifications list
491 unsigned notificationIndex = [obj unsignedIntValue];
492 if (notificationIndex >= numAllNotifications) {
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);
496 note = [inAllNotes objectAtIndex:notificationIndex];
499 //it's probably a notification name
503 if (note && ![allNotesCopy objectForKey:note]) {
504 GrowlNotificationTicket *ticket = [GrowlNotificationTicket notificationWithName:note];
505 [ticket setHumanReadableName:[humanReadableNames objectForKey:note]];
506 [ticket setNotificationDescription:[notificationDescriptions objectForKey:note]];
507 [allNotesCopy setObject:ticket forKey:note];
511 } else if ([inDefaults isKindOfClass:[NSIndexSet class]]) {
512 NSUInteger notificationIndex;
513 NSUInteger numAllNotifications = [inAllNotes count];
514 NSIndexSet *iset = (NSIndexSet *)inDefaults;
515 for (notificationIndex = [iset firstIndex]; notificationIndex != NSNotFound; notificationIndex = [iset indexGreaterThanIndex:notificationIndex]) {
516 if (notificationIndex >= numAllNotifications) {
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);
518 // index sets are sorted, so we can stop here
521 NSString *note = [inAllNotes objectAtIndex:notificationIndex];
522 if (![allNotesCopy objectForKey:note]) {
523 GrowlNotificationTicket *ticket = [GrowlNotificationTicket notificationWithName:note];
524 [ticket setHumanReadableName:[humanReadableNames objectForKey:note]];
525 [ticket setNotificationDescription:[notificationDescriptions objectForKey:note]];
526 [allNotesCopy setObject:ticket forKey:note];
533 NSLog(@"WARNING: application %@ passed an invalid object for the default notifications: %@.", appName, inDefaults);
536 if (![allNotifications isEqual:allNotesCopy]) {
537 [allNotifications release];
538 allNotifications = allNotesCopy;
541 [allNotesCopy release];
545 //ALWAYS set all notifications list first, to enable handling of numeric indices in the default notifications list!
546 [self setAllNotifications:inAllNotes];
547 [self setDefaultNotifications:inDefaults];
549 [self setIcon:inIcon];
552 - (void) reregisterWithDictionary:(NSDictionary *)dict {
553 NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
555 NSImage *appIcon = [dict objectForKey:GROWL_APP_ICON];
556 NSString *bundleId = [dict objectForKey:GROWL_APP_ID];
558 if (bundleId != appId && ![bundleId isEqualToString:appId]) {
560 appId = [bundleId retain];
564 //XXX - should assimilate reregisterWithAllNotifications:defaults:icon: here
565 NSArray *all = [dict objectForKey:GROWL_NOTIFICATIONS_ALL];
566 NSArray *defaults = [dict objectForKey:GROWL_NOTIFICATIONS_DEFAULT];
568 NSDictionary *newNames = [dict objectForKey:GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES];
569 if (newNames != humanReadableNames && ![newNames isEqual:humanReadableNames]) {
570 [humanReadableNames release];
571 humanReadableNames = [newNames retain];
575 NSDictionary *newDescriptions = [dict objectForKey:GROWL_NOTIFICATIONS_DESCRIPTIONS];
576 if (newDescriptions != notificationDescriptions && ![newDescriptions isEqual:notificationDescriptions]) {
577 [notificationDescriptions release];
578 notificationDescriptions = [newDescriptions retain];
582 if (!defaults) defaults = all;
583 [self reregisterWithAllNotifications:all
587 NSString *fullPath = nil;
588 id location = [dict objectForKey:GROWL_APP_LOCATION];
590 if ([location isKindOfClass:[NSDictionary class]]) {
591 NSDictionary *file_data = [location objectForKey:@"file-data"];
592 CFURLRef url = (CFURLRef)createFileURLWithDockDescription(file_data);
594 fullPath = [(NSString *)CFURLCopyPath(url) autorelease];
596 CFMakeCollectable(fullPath);
599 } else if ([location isKindOfClass:[NSString class]]) {
601 if (![[NSFileManager defaultManager] fileExistsAtPath:fullPath])
604 /* Don't handle the NSNumber case here, the app might have moved and we
605 * use the re-registration to update our stored appPath.
610 CFURLRef appURL = NULL;
611 OSStatus err = LSFindApplicationForInfo(kLSUnknownCreator,
617 fullPath = [(NSString *)CFURLCopyPath(appURL) autorelease];
619 CFMakeCollectable(fullPath);
624 fullPath = [workspace fullPathForApplication:appName];
626 if (fullPath != appPath && ![fullPath isEqualToString:appPath]) {
628 appPath = [fullPath retain];
633 - (NSArray *) allNotifications {
634 return [[[allNotifications allKeys] retain] autorelease];
637 - (void) setAllNotifications:(NSArray *)inArray {
638 if (allNotificationNames != inArray) {
639 if ([inArray isEqualToArray:allNotificationNames])
642 [allNotificationNames release];
643 allNotificationNames = [inArray retain];
645 //We want to keep all of the old notification settings and create entries for the new ones
646 NSEnumerator *newEnum = [inArray objectEnumerator];
647 NSMutableDictionary *tmp = [[NSMutableDictionary alloc] initWithCapacity:[inArray count]];
649 while ((key = [newEnum nextObject])) {
650 obj = [allNotifications objectForKey:key];
652 [tmp setObject:obj forKey:key];
654 GrowlNotificationTicket *notification = [[GrowlNotificationTicket alloc] initWithName:key];
655 [notification setHumanReadableName:[humanReadableNames objectForKey:key]];
656 [notification setNotificationDescription:[notificationDescriptions objectForKey:key]];
657 [tmp setObject:notification forKey:key];
658 [notification release];
661 [allNotifications release];
662 allNotifications = tmp;
664 // And then make sure the list of default notifications also doesn't have any straglers...
665 NSMutableSet *cur = [[NSMutableSet alloc] initWithArray:defaultNotifications];
666 NSSet *new = [[NSSet alloc] initWithArray:allNotificationNames];
667 [cur intersectSet:new];
668 [defaultNotifications release];
669 defaultNotifications = [[cur allObjects] retain];
675 - (NSArray *) defaultNotifications {
676 return [[defaultNotifications retain] autorelease];
679 - (void) setDefaultNotifications:(id)inObject {
680 if (!allNotifications) {
681 /*WARNING: if you try to pass an array containing numeric indices, and
682 * the all-notifications list has not been supplied yet, the indices
683 * WILL NOT be dereferenced. ALWAYS set the all-notifications list FIRST.
685 if (![defaultNotifications isEqual:inObject]) {
686 [defaultNotifications release];
687 defaultNotifications = [inObject retain];
690 } else if ([inObject respondsToSelector:@selector(objectEnumerator)] ) {
691 NSEnumerator *mightBeIndicesEnum = [inObject objectEnumerator];
693 NSUInteger numDefaultNotifications;
694 NSUInteger numAllNotifications = [allNotificationNames count];
695 if ([inObject respondsToSelector:@selector(count)])
696 numDefaultNotifications = [inObject count];
698 numDefaultNotifications = numAllNotifications;
699 NSMutableArray *mDefaultNotifications = [[NSMutableArray alloc] initWithCapacity:numDefaultNotifications];
700 Class NSNumberClass = [NSNumber class];
701 while ((num = [mightBeIndicesEnum nextObject])) {
702 if ([num isKindOfClass:NSNumberClass]) {
703 //it's an index into the all-notifications list
704 unsigned notificationIndex = [num unsignedIntValue];
705 if (notificationIndex >= numAllNotifications)
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);
708 [mDefaultNotifications addObject:[allNotificationNames objectAtIndex:notificationIndex]];
710 //it's probably a notification name
711 [mDefaultNotifications addObject:num];
714 if (![defaultNotifications isEqualToArray:mDefaultNotifications]) {
715 [defaultNotifications release];
716 defaultNotifications = mDefaultNotifications;
719 [mDefaultNotifications release];
721 } else if ([inObject isKindOfClass:[NSIndexSet class]]) {
722 NSUInteger notificationIndex;
723 NSUInteger numAllNotifications = [allNotificationNames count];
724 NSIndexSet *iset = (NSIndexSet *)inObject;
725 NSMutableArray *mDefaultNotifications = [[NSMutableArray alloc] initWithCapacity:[iset count]];
726 for (notificationIndex = [iset firstIndex]; notificationIndex != NSNotFound; notificationIndex = [iset indexGreaterThanIndex:notificationIndex]) {
727 if (notificationIndex >= numAllNotifications) {
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);
729 // index sets are sorted, so we can stop here
732 [mDefaultNotifications addObject:[allNotificationNames objectAtIndex:notificationIndex]];
735 if (![defaultNotifications isEqualToArray:mDefaultNotifications]) {
736 [defaultNotifications release];
737 defaultNotifications = mDefaultNotifications;
740 [mDefaultNotifications release];
744 NSLog(@"WARNING: application %@ passed an invalid object for the default notifications: %@.", appName, inObject);
745 if (![defaultNotifications isEqualToArray:allNotificationNames]) {
746 [defaultNotifications release];
747 defaultNotifications = [allNotificationNames retain];
753 [self setAllowedNotificationsToDefault];
756 - (NSArray *) allowedNotifications {
757 NSMutableArray* allowed = [NSMutableArray array];
758 NSEnumerator *notificationEnum = [allNotifications objectEnumerator];
759 GrowlNotificationTicket *obj;
760 while ((obj = [notificationEnum nextObject]))
762 [allowed addObject:[obj name]];
766 - (void) setAllowedNotifications:(NSArray *) inArray {
767 NSSet *allowed = [[NSSet alloc] initWithArray:inArray];
768 NSEnumerator *notificationEnum = [allNotifications objectEnumerator];
769 GrowlNotificationTicket *obj;
770 while ((obj = [notificationEnum nextObject]))
771 [obj setEnabled:[allowed containsObject:[obj name]]];
777 - (void) setAllowedNotificationsToDefault {
778 [self setAllowedNotifications:defaultNotifications];
782 - (BOOL) isNotificationAllowed:(NSString *) name {
783 return ticketEnabled && [[allNotifications objectForKey:name] enabled];
786 - (NSComparisonResult) caseInsensitiveCompare:(GrowlApplicationTicket *)aTicket {
787 return [appName caseInsensitiveCompare:[aTicket applicationName]];
790 #pragma mark Notification Accessors
791 - (NSArray *) notifications {
792 return [allNotifications allValues];
795 - (GrowlNotificationTicket *) notificationTicketForName:(NSString *)name {
796 return [allNotifications objectForKey:name];