Core/Source/GrowlApplicationTicket.m
author Rudy Richter
Sat Aug 01 20:50:32 2009 -0400 (2009-08-01)
changeset 4261 48b7c994f6c8
parent 4246 4f52d1d98978
child 4489 9c0b9f927d0e
permissions -rw-r--r--
PrefPane: clang warnings and setup for Sparkle
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