Core/Source/GrowlPreferencesController.m
author Peter Hosey <hg@boredzo.org>
Mon Aug 03 11:55:33 2009 -0700 (2009-08-03)
changeset 4273 b846a7b597a6
parent 4270 5c1d8f5e64c2
child 4329 7d3e76457212
permissions -rw-r--r--
I forgot to remove this import of LoginItemsAE.h. How did this build before?!
boredzo@2434
     1
//
boredzo@2434
     2
//  GrowlPreferencesController.m
boredzo@2434
     3
//  Growl
boredzo@2434
     4
//
boredzo@2434
     5
//  Created by Nelson Elhage on 8/24/04.
boredzo@2434
     6
//  Renamed from GrowlPreferences.m by Mac-arena the Bored Zo on 2005-06-27.
ingmarstein@3040
     7
//  Copyright 2004-2006 The Growl Project. All rights reserved.
boredzo@2434
     8
//
boredzo@2434
     9
// This file is under the BSD License, refer to License.txt for details
boredzo@2434
    10
boredzo@2434
    11
boredzo@2434
    12
#import "GrowlPreferencesController.h"
boredzo@2434
    13
#import "GrowlDefinesInternal.h"
boredzo@2434
    14
#import "GrowlDefines.h"
boredzo@2461
    15
#import "GrowlPathUtilities.h"
ingmarstein@2517
    16
#import "NSStringAdditions.h"
ingmarstein@2639
    17
#include "CFURLAdditions.h"
ingmarstein@2641
    18
#include "CFDictionaryAdditions.h"
ingmarstein@2639
    19
#include <Security/SecKeychain.h>
ingmarstein@2639
    20
#include <Security/SecKeychainItem.h>
boredzo@2434
    21
ingmarstein@2517
    22
#define keychainServiceName "Growl"
ingmarstein@2517
    23
#define keychainAccountName "Growl"
ingmarstein@2517
    24
ingmarstein@2602
    25
CFTypeRef GrowlPreferencesController_objectForKey(CFTypeRef key) {
ingmarstein@2602
    26
	return [[GrowlPreferencesController sharedController] objectForKey:(id)key];
ingmarstein@2602
    27
}
ingmarstein@2602
    28
Rudy@4246
    29
CFIndex GrowlPreferencesController_integerForKey(CFTypeRef key) {
ingmarstein@2646
    30
	Boolean keyExistsAndHasValidFormat;
boredzo@4041
    31
	return CFPreferencesGetAppIntegerValue((CFStringRef)key, (CFStringRef)GROWL_HELPERAPP_BUNDLE_IDENTIFIER, &keyExistsAndHasValidFormat);
ingmarstein@2602
    32
}
ingmarstein@2602
    33
ingmarstein@2602
    34
Boolean GrowlPreferencesController_boolForKey(CFTypeRef key) {
ingmarstein@2646
    35
	Boolean keyExistsAndHasValidFormat;
boredzo@4041
    36
	return CFPreferencesGetAppBooleanValue((CFStringRef)key, (CFStringRef)GROWL_HELPERAPP_BUNDLE_IDENTIFIER, &keyExistsAndHasValidFormat);
ingmarstein@2602
    37
}
ingmarstein@2602
    38
Rudy@4246
    39
unsigned short GrowlPreferencesController_unsignedShortForKey(CFTypeRef key)
Rudy@4246
    40
{
Rudy@4246
    41
	CFIndex theIndex = GrowlPreferencesController_integerForKey(key);
Rudy@4246
    42
	
Rudy@4246
    43
	if (theIndex > USHRT_MAX)
Rudy@4246
    44
		return USHRT_MAX;
Rudy@4246
    45
	else if (theIndex < 0)
Rudy@4246
    46
		return 0;
Rudy@4246
    47
	return (unsigned short)index;
Rudy@4246
    48
}
Rudy@4246
    49
boredzo@2434
    50
@implementation GrowlPreferencesController
boredzo@2434
    51
boredzo@2496
    52
+ (GrowlPreferencesController *) sharedController {
ofri@2581
    53
	return [self sharedInstance];
ofri@2581
    54
}
ofri@2581
    55
ofri@2581
    56
- (id) initSingleton {
ofri@2581
    57
	if ((self = [super initSingleton])) {
evands@2514
    58
		[[NSDistributedNotificationCenter defaultCenter] addObserver:self
evands@2514
    59
															selector:@selector(growlPreferencesChanged:)
evands@2514
    60
																name:GrowlPreferencesChanged
ingmarstein@2591
    61
															  object:nil];
hg@4270
    62
		loginItems = LSSharedFileListCreate(kCFAllocatorDefault, kLSSharedFileListSessionLoginItems, /*options*/ NULL);
boredzo@2434
    63
	}
boredzo@2434
    64
	return self;
boredzo@2434
    65
}
boredzo@2434
    66
ofri@2581
    67
- (void) destroy {
evands@2514
    68
	[[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
hg@4270
    69
	CFRelease(loginItems);
ingmarstein@2591
    70
ofri@2581
    71
	[super destroy];
boredzo@2434
    72
}
boredzo@2434
    73
boredzo@2434
    74
#pragma mark -
boredzo@2434
    75
boredzo@2434
    76
- (void) registerDefaults:(NSDictionary *)inDefaults {
ingmarstein@2561
    77
	NSUserDefaults *helperAppDefaults = [[NSUserDefaults alloc] init];
boredzo@4041
    78
	[helperAppDefaults addSuiteNamed:GROWL_HELPERAPP_BUNDLE_IDENTIFIER];
boredzo@4041
    79
	NSDictionary *existing = [helperAppDefaults persistentDomainForName:GROWL_HELPERAPP_BUNDLE_IDENTIFIER];
boredzo@2434
    80
	if (existing) {
boredzo@2434
    81
		NSMutableDictionary *domain = [inDefaults mutableCopy];
boredzo@2434
    82
		[domain addEntriesFromDictionary:existing];
boredzo@4041
    83
		[helperAppDefaults setPersistentDomain:domain forName:GROWL_HELPERAPP_BUNDLE_IDENTIFIER];
boredzo@2434
    84
		[domain release];
boredzo@2434
    85
	} else {
boredzo@4041
    86
		[helperAppDefaults setPersistentDomain:inDefaults forName:GROWL_HELPERAPP_BUNDLE_IDENTIFIER];
boredzo@2434
    87
	}
ingmarstein@2561
    88
	[helperAppDefaults release];
ingmarstein@2561
    89
	SYNCHRONIZE_GROWL_PREFS();
boredzo@2434
    90
}
boredzo@2434
    91
boredzo@2434
    92
- (id) objectForKey:(NSString *)key {
boredzo@4041
    93
	id value = (id)CFPreferencesCopyAppValue((CFStringRef)key, (CFStringRef)GROWL_HELPERAPP_BUNDLE_IDENTIFIER);
Rudy@4261
    94
	if(value)
Rudy@4261
    95
		CFMakeCollectable(value);
ingmarstein@2561
    96
	return [value autorelease];
boredzo@2434
    97
}
boredzo@2434
    98
boredzo@2434
    99
- (void) setObject:(id)object forKey:(NSString *)key {
boredzo@2494
   100
	CFPreferencesSetAppValue((CFStringRef)key,
boredzo@2494
   101
							 (CFPropertyListRef)object,
boredzo@4041
   102
							 (CFStringRef)GROWL_HELPERAPP_BUNDLE_IDENTIFIER);
boredzo@2434
   103
ingmarstein@2561
   104
	SYNCHRONIZE_GROWL_PREFS();
boredzo@2434
   105
ingmarstein@2672
   106
	int pid = getpid();
ingmarstein@2672
   107
	CFNumberRef pidValue = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid);
ingmarstein@2672
   108
	CFStringRef pidKey = CFSTR("pid");
ingmarstein@2672
   109
	CFDictionaryRef userInfo = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&pidKey, (const void **)&pidValue, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
ingmarstein@2672
   110
	CFRelease(pidValue);
ingmarstein@2672
   111
	CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(),
ingmarstein@2672
   112
										 (CFStringRef)GrowlPreferencesChanged,
ingmarstein@2672
   113
										 /*object*/ key,
ingmarstein@2672
   114
										 /*userInfo*/ userInfo,
ingmarstein@2672
   115
										 /*deliverImmediately*/ false);
ingmarstein@2672
   116
	CFRelease(userInfo);
boredzo@2434
   117
}
boredzo@2434
   118
boredzo@2434
   119
- (BOOL) boolForKey:(NSString *)key {
ingmarstein@2646
   120
	return GrowlPreferencesController_boolForKey((CFTypeRef)key);
boredzo@2434
   121
}
boredzo@2434
   122
boredzo@2434
   123
- (void) setBool:(BOOL)value forKey:(NSString *)key {
boredzo@2434
   124
	NSNumber *object = [[NSNumber alloc] initWithBool:value];
boredzo@2434
   125
	[self setObject:object forKey:key];
boredzo@2434
   126
	[object release];
boredzo@2434
   127
}
boredzo@2434
   128
Rudy@4246
   129
- (CFIndex) integerForKey:(NSString *)key {
ingmarstein@2646
   130
	return GrowlPreferencesController_integerForKey((CFTypeRef)key);
boredzo@2434
   131
}
boredzo@2434
   132
Rudy@4246
   133
- (void) setInteger:(CFIndex)value forKey:(NSString *)key {
Rudy@4246
   134
#ifdef __LP64__
Rudy@4246
   135
	NSNumber *object = [[NSNumber alloc] initWithInteger:value];
Rudy@4246
   136
#else
boredzo@2434
   137
	NSNumber *object = [[NSNumber alloc] initWithInt:value];
Rudy@4246
   138
#endif
boredzo@2434
   139
	[self setObject:object forKey:key];
boredzo@2434
   140
	[object release];
boredzo@2434
   141
}
boredzo@2434
   142
Rudy@4246
   143
- (unsigned short)unsignedShortForKey:(NSString *)key
Rudy@4246
   144
{
Rudy@4246
   145
	return GrowlPreferencesController_unsignedShortForKey((CFTypeRef)key);
Rudy@4246
   146
}
Rudy@4246
   147
Rudy@4246
   148
Rudy@4246
   149
- (void)setUnsignedShort:(unsigned short)theShort forKey:(NSString *)key
Rudy@4246
   150
{
Rudy@4246
   151
	[self setObject:[NSNumber numberWithUnsignedShort:theShort] forKey:key];
Rudy@4246
   152
}
Rudy@4246
   153
boredzo@2434
   154
- (void) synchronize {
boredzo@2434
   155
	SYNCHRONIZE_GROWL_PREFS();
boredzo@2434
   156
}
boredzo@2434
   157
boredzo@2434
   158
#pragma mark -
boredzo@2434
   159
#pragma mark Start-at-login control
boredzo@2434
   160
boredzo@2496
   161
- (BOOL) shouldStartGrowlAtLogin {
ingmarstein@3011
   162
	Boolean    foundIt = false;
boredzo@2434
   163
boredzo@2434
   164
	//get the prefpane bundle and find GHA within it.
evands@3619
   165
	NSString *pathToGHA      = [[NSBundle bundleWithIdentifier:GROWL_PREFPANE_BUNDLE_IDENTIFIER] pathForResource:@"GrowlHelperApp" ofType:@"app"];
ingmarstein@2721
   166
	//get the file url to GHA.
ingmarstein@2721
   167
	CFURLRef urlToGHA = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)pathToGHA, kCFURLPOSIXPathStyle, true);
ingmarstein@2721
   168
hg@4270
   169
	UInt32 seed = 0U;
hg@4270
   170
	NSArray *currentLoginItems = [NSMakeCollectable(LSSharedFileListCopySnapshot(loginItems, &seed)) autorelease];
hg@4270
   171
	for (id itemObject in currentLoginItems) {
hg@4270
   172
		LSSharedFileListItemRef item = (LSSharedFileListItemRef)itemObject;
hg@4270
   173
hg@4270
   174
		UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
hg@4270
   175
		CFURLRef URL = NULL;
hg@4270
   176
		OSStatus err = LSSharedFileListItemResolve(item, resolutionFlags, &URL, /*outRef*/ NULL);
hg@4270
   177
		if (err == noErr) {
hg@4270
   178
			foundIt = CFEqual(URL, urlToGHA);
hg@4270
   179
			CFRelease(URL);
hg@4270
   180
ingmarstein@3011
   181
			if (foundIt)
ingmarstein@3011
   182
				break;
ingmarstein@3011
   183
		}
ingmarstein@3011
   184
	}
ingmarstein@2721
   185
ingmarstein@2721
   186
	CFRelease(urlToGHA);
boredzo@2434
   187
boredzo@2434
   188
	return foundIt;
boredzo@2434
   189
}
boredzo@2434
   190
boredzo@2496
   191
- (void) setShouldStartGrowlAtLogin:(BOOL)flag {
boredzo@2434
   192
	//get the prefpane bundle and find GHA within it.
evands@3619
   193
	NSString *pathToGHA = [[NSBundle bundleWithIdentifier:GROWL_PREFPANE_BUNDLE_IDENTIFIER] pathForResource:@"GrowlHelperApp" ofType:@"app"];
boredzo@2434
   194
	[self setStartAtLogin:pathToGHA enabled:flag];
boredzo@2434
   195
}
boredzo@2434
   196
evands@3616
   197
- (void) setStartAtLogin:(NSString *)path enabled:(BOOL)enabled {
ingmarstein@2721
   198
	OSStatus status;
hg@4270
   199
	CFURLRef URLToToggle = (CFURLRef)[NSURL fileURLWithPath:path];
hg@4270
   200
	LSSharedFileListItemRef existingItem = NULL;
hg@4270
   201
hg@4270
   202
	UInt32 seed = 0U;
hg@4270
   203
	NSArray *currentLoginItems = [NSMakeCollectable(LSSharedFileListCopySnapshot(loginItems, &seed)) autorelease];
hg@4270
   204
	for (id itemObject in currentLoginItems) {
hg@4270
   205
		LSSharedFileListItemRef item = (LSSharedFileListItemRef)itemObject;
hg@4270
   206
hg@4270
   207
		UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
hg@4270
   208
		CFURLRef URL = NULL;
hg@4270
   209
		OSStatus err = LSSharedFileListItemResolve(item, resolutionFlags, &URL, /*outRef*/ NULL);
hg@4270
   210
		if (err == noErr) {
hg@4270
   211
			Boolean foundIt = CFEqual(URL, URLToToggle);
hg@4270
   212
			CFRelease(URL);
hg@4270
   213
hg@4270
   214
			if (foundIt) {
hg@4270
   215
				existingItem = item;
evands@3616
   216
				break;
evands@3616
   217
			}
evands@3616
   218
		}
evands@3616
   219
	}
evands@3616
   220
hg@4270
   221
	if (enabled && (existingItem == NULL)) {
hg@4270
   222
		NSString *displayName = [[NSFileManager defaultManager] displayNameAtPath:path];
hg@4270
   223
		IconRef icon = NULL;
hg@4270
   224
		FSRef ref;
hg@4270
   225
		Boolean gotRef = CFURLGetFSRef(URLToToggle, &ref);
hg@4270
   226
		if (gotRef) {
hg@4270
   227
			status = GetIconRefFromFileInfo(&ref,
hg@4270
   228
											/*fileNameLength*/ 0, /*fileName*/ NULL,
hg@4270
   229
											kFSCatInfoNone, /*catalogInfo*/ NULL,
hg@4270
   230
											kIconServicesNormalUsageFlag,
hg@4270
   231
											&icon,
hg@4270
   232
											/*outLabel*/ NULL);
hg@4270
   233
			if (status != noErr)
hg@4270
   234
				icon = NULL;
hg@4270
   235
		}
hg@4270
   236
hg@4270
   237
		LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, (CFStringRef)displayName, icon, URLToToggle, /*propertiesToSet*/ NULL, /*propertiesToClear*/ NULL);
hg@4270
   238
	} else if (!enabled && (existingItem != NULL))
hg@4270
   239
		LSSharedFileListItemRemove(loginItems, existingItem);
boredzo@2434
   240
}
boredzo@2434
   241
boredzo@2434
   242
#pragma mark -
boredzo@2496
   243
#pragma mark GrowlMenu running state
boredzo@2496
   244
boredzo@2496
   245
- (void) enableGrowlMenu {
ingmarstein@2517
   246
	NSBundle *bundle = [NSBundle bundleForClass:[GrowlPreferencesController class]];
ingmarstein@2517
   247
	NSString *growlMenuPath = [bundle pathForResource:@"GrowlMenu" ofType:@"app"];
boredzo@2496
   248
	NSURL *growlMenuURL = [NSURL fileURLWithPath:growlMenuPath];
boredzo@2496
   249
	[[NSWorkspace sharedWorkspace] openURLs:[NSArray arrayWithObject:growlMenuURL]
boredzo@2496
   250
	                withAppBundleIdentifier:nil
boredzo@2496
   251
	                                options:NSWorkspaceLaunchWithoutAddingToRecents | NSWorkspaceLaunchWithoutActivation | NSWorkspaceLaunchAsync
boredzo@2496
   252
	         additionalEventParamDescriptor:nil
boredzo@2496
   253
	                      launchIdentifiers:NULL];
boredzo@2496
   254
}
boredzo@2496
   255
boredzo@2496
   256
- (void) disableGrowlMenu {
boredzo@2496
   257
	// Ask GrowlMenu to shutdown via the DNC
ingmarstein@2672
   258
	CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(),
ingmarstein@2672
   259
										 CFSTR("GrowlMenuShutdown"),
ingmarstein@2672
   260
										 /*object*/ NULL,
ingmarstein@2672
   261
										 /*userInfo*/ NULL,
ingmarstein@2672
   262
										 /*deliverImmediately*/ false);
boredzo@2496
   263
}
boredzo@2496
   264
boredzo@2496
   265
#pragma mark -
boredzo@2434
   266
#pragma mark Growl running state
boredzo@2434
   267
boredzo@2434
   268
- (void) setGrowlRunning:(BOOL)flag noMatterWhat:(BOOL)nmw {
boredzo@2434
   269
	// Store the desired running-state of the helper app for use by GHA.
boredzo@2434
   270
	[self setBool:flag forKey:GrowlEnabledKey];
boredzo@2434
   271
boredzo@2434
   272
	//now launch or terminate as appropriate.
boredzo@2434
   273
	if (flag)
boredzo@2434
   274
		[self launchGrowl:nmw];
boredzo@2434
   275
	else
boredzo@2434
   276
		[self terminateGrowl];
boredzo@2434
   277
}
boredzo@2434
   278
boredzo@2434
   279
- (BOOL) isRunning:(NSString *)theBundleIdentifier {
boredzo@2434
   280
	BOOL isRunning = NO;
boredzo@2434
   281
	ProcessSerialNumber PSN = { kNoProcess, kNoProcess };
boredzo@2434
   282
boredzo@2434
   283
	while (GetNextProcess(&PSN) == noErr) {
boredzo@2434
   284
		NSDictionary *infoDict = (NSDictionary *)ProcessInformationCopyDictionary(&PSN, kProcessDictionaryIncludeAllInformationMask);
Rudy@4261
   285
		if(infoDict) {
Rudy@4261
   286
			NSString *bundleID = [infoDict objectForKey:(NSString *)kCFBundleIdentifierKey];
Rudy@4261
   287
			isRunning = bundleID && [bundleID isEqualToString:theBundleIdentifier];
Rudy@4261
   288
			CFMakeCollectable(infoDict);
Rudy@4261
   289
			[infoDict release];
Rudy@4261
   290
		}
boredzo@2434
   291
		if (isRunning)
boredzo@2434
   292
			break;
boredzo@2434
   293
	}
boredzo@2434
   294
boredzo@2434
   295
	return isRunning;
boredzo@2434
   296
}
boredzo@2434
   297
boredzo@2434
   298
- (BOOL) isGrowlRunning {
boredzo@2434
   299
	return [self isRunning:@"com.Growl.GrowlHelperApp"];
boredzo@2434
   300
}
boredzo@2434
   301
boredzo@2434
   302
- (void) launchGrowl:(BOOL)noMatterWhat {
boredzo@2677
   303
	NSString *helperPath = [[GrowlPathUtilities helperAppBundle] bundlePath];
boredzo@2496
   304
	NSURL *helperURL = [NSURL fileURLWithPath:helperPath];
boredzo@2496
   305
boredzo@2496
   306
	unsigned options = NSWorkspaceLaunchWithoutAddingToRecents | NSWorkspaceLaunchWithoutActivation | NSWorkspaceLaunchAsync;
boredzo@2496
   307
	if (noMatterWhat)
boredzo@2496
   308
		options |= NSWorkspaceLaunchNewInstance;
boredzo@2496
   309
	[[NSWorkspace sharedWorkspace] openURLs:[NSArray arrayWithObject:helperURL]
boredzo@2496
   310
	                withAppBundleIdentifier:nil
boredzo@2496
   311
	                                options:options
boredzo@2496
   312
	         additionalEventParamDescriptor:nil
boredzo@2496
   313
	                      launchIdentifiers:NULL];
boredzo@2434
   314
}
boredzo@2434
   315
boredzo@2434
   316
- (void) terminateGrowl {
boredzo@2434
   317
	// Ask the Growl Helper App to shutdown via the DNC
ingmarstein@2672
   318
	CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(),
ingmarstein@2672
   319
										 (CFStringRef)GROWL_SHUTDOWN,
ingmarstein@2672
   320
										 /*object*/ NULL,
ingmarstein@2672
   321
										 /*userInfo*/ NULL,
ingmarstein@2672
   322
										 /*deliverImmediately*/ false);
boredzo@2434
   323
}
boredzo@2434
   324
boredzo@2496
   325
#pragma mark -
boredzo@2496
   326
//Simplified accessors
boredzo@2496
   327
boredzo@2496
   328
#pragma mark UI
boredzo@2496
   329
Rudy@4246
   330
- (CFIndex)selectedPosition {
bgannin@3317
   331
	return [self integerForKey:GROWL_POSITION_PREFERENCE_KEY];
bgannin@3317
   332
}
bgannin@3317
   333
boredzo@2496
   334
- (BOOL) isBackgroundUpdateCheckEnabled {
boredzo@2496
   335
	return [self boolForKey:GrowlUpdateCheckKey];
boredzo@2496
   336
}
boredzo@2496
   337
- (void) setIsBackgroundUpdateCheckEnabled:(BOOL)flag {
boredzo@2496
   338
	[self setBool:flag forKey:GrowlUpdateCheckKey];
boredzo@2496
   339
}
boredzo@2496
   340
boredzo@2496
   341
- (NSString *) defaultDisplayPluginName {
boredzo@2496
   342
	return [self objectForKey:GrowlDisplayPluginKey];
boredzo@2496
   343
}
boredzo@2496
   344
- (void) setDefaultDisplayPluginName:(NSString *)name {
boredzo@2496
   345
	[self setObject:name forKey:GrowlDisplayPluginKey];
boredzo@2496
   346
}
boredzo@2496
   347
boredzo@2496
   348
- (BOOL) squelchMode {
boredzo@2496
   349
	return [self boolForKey:GrowlSquelchModeKey];
boredzo@2496
   350
}
boredzo@2496
   351
- (void) setSquelchMode:(BOOL)flag {
boredzo@2496
   352
	[self setBool:flag forKey:GrowlSquelchModeKey];
boredzo@2496
   353
}
boredzo@2496
   354
boredzo@2496
   355
- (BOOL) stickyWhenAway {
boredzo@2496
   356
	return [self boolForKey:GrowlStickyWhenAwayKey];
boredzo@2496
   357
}
boredzo@2496
   358
- (void) setStickyWhenAway:(BOOL)flag {
boredzo@2496
   359
	[self setBool:flag forKey:GrowlStickyWhenAwayKey];
boredzo@2496
   360
}
boredzo@2496
   361
rudy@2968
   362
- (NSNumber*) idleThreshold {
Rudy@4246
   363
#ifdef __LP64__
Rudy@4246
   364
	return [NSNumber numberWithInteger:[self integerForKey:GrowlStickyIdleThresholdKey]];
Rudy@4246
   365
#else
rudy@2968
   366
	return [NSNumber numberWithInt:[self integerForKey:GrowlStickyIdleThresholdKey]];
Rudy@4246
   367
#endif
rudy@2968
   368
}
rudy@2968
   369
rudy@2968
   370
- (void) setIdleThreshold:(NSNumber*)value {
rudy@2968
   371
	[self setInteger:[value intValue] forKey:GrowlStickyIdleThresholdKey];
rudy@2968
   372
}
boredzo@2496
   373
#pragma mark Status Item
boredzo@2496
   374
ingmarstein@2540
   375
- (BOOL) isGrowlMenuEnabled {
boredzo@2496
   376
	return [self boolForKey:GrowlMenuExtraKey];
boredzo@2496
   377
}
boredzo@2496
   378
boredzo@2496
   379
- (void) setGrowlMenuEnabled:(BOOL)state {
ingmarstein@2540
   380
	if (state != [self isGrowlMenuEnabled]) {
boredzo@2496
   381
		[self setBool:state forKey:GrowlMenuExtraKey];
boredzo@2496
   382
		if (state)
boredzo@2496
   383
			[self enableGrowlMenu];
boredzo@2496
   384
		else
boredzo@2496
   385
			[self disableGrowlMenu];
boredzo@2496
   386
	}
boredzo@2496
   387
}
boredzo@2496
   388
boredzo@2496
   389
#pragma mark Logging
boredzo@2496
   390
boredzo@2496
   391
- (BOOL) loggingEnabled {
ingmarstein@2635
   392
	return [self boolForKey:GrowlLoggingEnabledKey];
boredzo@2496
   393
}
boredzo@2496
   394
boredzo@2496
   395
- (void) setLoggingEnabled:(BOOL)flag {
ingmarstein@2635
   396
	[self setBool:flag forKey:GrowlLoggingEnabledKey];
ingmarstein@2635
   397
}
boredzo@2496
   398
boredzo@2496
   399
- (BOOL) isGrowlServerEnabled {
ingmarstein@2635
   400
	return [self boolForKey:GrowlStartServerKey];
boredzo@2496
   401
}
boredzo@2496
   402
boredzo@2496
   403
- (void) setGrowlServerEnabled:(BOOL)enabled {
ingmarstein@2635
   404
	[self setBool:enabled forKey:GrowlStartServerKey];
boredzo@2496
   405
}
boredzo@2496
   406
boredzo@2496
   407
#pragma mark Remote Growling
boredzo@2496
   408
boredzo@2496
   409
- (BOOL) isRemoteRegistrationAllowed {
ingmarstein@2635
   410
	return [self boolForKey:GrowlRemoteRegistrationKey];
boredzo@2496
   411
}
boredzo@2496
   412
boredzo@2496
   413
- (void) setRemoteRegistrationAllowed:(BOOL)flag {
ingmarstein@2635
   414
	[self setBool:flag forKey:GrowlRemoteRegistrationKey];
boredzo@2496
   415
}
boredzo@2496
   416
boredzo@2496
   417
- (NSString *) remotePassword {
ingmarstein@2634
   418
	unsigned char *password;
boredzo@2496
   419
	UInt32 passwordLength;
boredzo@2496
   420
	OSStatus status;
boredzo@2496
   421
	status = SecKeychainFindGenericPassword(NULL,
Rudy@4246
   422
											(UInt32)strlen(keychainServiceName), keychainServiceName,
Rudy@4246
   423
											(UInt32)strlen(keychainAccountName), keychainAccountName,
boredzo@2496
   424
											&passwordLength, (void **)&password, NULL);
boredzo@2496
   425
boredzo@2496
   426
	NSString *passwordString;
boredzo@2496
   427
	if (status == noErr) {
ingmarstein@2634
   428
		passwordString = (NSString *)CFStringCreateWithBytes(kCFAllocatorDefault, password, passwordLength, kCFStringEncodingUTF8, false);
Rudy@4261
   429
		if(passwordString) {
Rudy@4261
   430
			CFMakeCollectable(passwordString);
Rudy@4261
   431
			[passwordString autorelease];
Rudy@4261
   432
			SecKeychainItemFreeContent(NULL, password);
Rudy@4261
   433
		}
boredzo@2496
   434
	} else {
boredzo@2496
   435
		if (status != errSecItemNotFound)
boredzo@2496
   436
			NSLog(@"Failed to retrieve password from keychain. Error: %d", status);
boredzo@2496
   437
		passwordString = @"";
boredzo@2496
   438
	}
boredzo@2496
   439
boredzo@2496
   440
	return passwordString;
boredzo@2496
   441
}
boredzo@2496
   442
boredzo@2496
   443
- (void) setRemotePassword:(NSString *)value {
boredzo@2496
   444
	const char *password = value ? [value UTF8String] : "";
Rudy@4246
   445
	size_t length = strlen(password);
boredzo@2496
   446
	OSStatus status;
boredzo@2496
   447
	SecKeychainItemRef itemRef = nil;
boredzo@2496
   448
	status = SecKeychainFindGenericPassword(NULL,
Rudy@4246
   449
											(UInt32)strlen(keychainServiceName), keychainServiceName,
Rudy@4246
   450
											(UInt32)strlen(keychainAccountName), keychainAccountName,
boredzo@2496
   451
											NULL, NULL, &itemRef);
boredzo@2496
   452
	if (status == errSecItemNotFound) {
boredzo@2496
   453
		// add new item
boredzo@2496
   454
		status = SecKeychainAddGenericPassword(NULL,
Rudy@4246
   455
											   (UInt32)strlen(keychainServiceName), keychainServiceName,
Rudy@4246
   456
											   (UInt32)strlen(keychainAccountName), keychainAccountName,
Rudy@4246
   457
											   (UInt32)length, password, NULL);
boredzo@2496
   458
		if (status)
boredzo@2496
   459
			NSLog(@"Failed to add password to keychain.");
boredzo@2496
   460
	} else {
boredzo@2496
   461
		// change existing password
boredzo@2496
   462
		SecKeychainAttribute attrs[] = {
Rudy@4246
   463
			{ kSecAccountItemAttr, (UInt32)strlen(keychainAccountName), (char *)keychainAccountName },
Rudy@4246
   464
			{ kSecServiceItemAttr, (UInt32)strlen(keychainServiceName), (char *)keychainServiceName }
boredzo@2496
   465
		};
Rudy@4246
   466
		const SecKeychainAttributeList attributes = { (UInt32)sizeof(attrs) / (UInt32)sizeof(attrs[0]), attrs };
boredzo@2496
   467
		status = SecKeychainItemModifyAttributesAndData(itemRef,		// the item reference
boredzo@2496
   468
														&attributes,	// no change to attributes
Rudy@4246
   469
														(UInt32)length,			// length of password
boredzo@2496
   470
														password		// pointer to password data
boredzo@2496
   471
														);
boredzo@2496
   472
		if (itemRef)
boredzo@2496
   473
			CFRelease(itemRef);
boredzo@2496
   474
		if (status)
boredzo@2496
   475
			NSLog(@"Failed to change password in keychain.");
boredzo@2496
   476
	}
boredzo@2496
   477
}
boredzo@2496
   478
Rudy@4246
   479
- (unsigned short) UDPPort {
Rudy@4246
   480
	return [self unsignedShortForKey:GrowlUDPPortKey];
Rudy@4246
   481
}
Rudy@4246
   482
- (void) setUDPPort:(unsigned short)value {
Rudy@4246
   483
	[self setUnsignedShort:value forKey:GrowlUDPPortKey];
boredzo@2496
   484
}
boredzo@2496
   485
boredzo@2496
   486
- (BOOL) isForwardingEnabled {
ingmarstein@2635
   487
	return [self boolForKey:GrowlEnableForwardKey];
boredzo@2496
   488
}
boredzo@2496
   489
- (void) setForwardingEnabled:(BOOL)enabled {
ingmarstein@2635
   490
	[self setBool:enabled forKey:GrowlEnableForwardKey];
boredzo@2496
   491
}
boredzo@2496
   492
evands@2514
   493
#pragma mark -
evands@2514
   494
/*
evands@2514
   495
 * @brief Growl preferences changed
evands@2514
   496
 *
evands@2514
   497
 * Synchronize our NSUserDefaults to immediately get any changes from the disk
evands@2514
   498
 */
ingmarstein@2517
   499
- (void) growlPreferencesChanged:(NSNotification *)notification {
evands@4016
   500
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
evands@4016
   501
ingmarstein@2672
   502
	NSString *object = [notification object];
ingmarstein@2672
   503
//	NSLog(@"%s: %@\n", __func__, object);
ingmarstein@2672
   504
	SYNCHRONIZE_GROWL_PREFS();
ingmarstein@2672
   505
	if (!object || [object isEqualToString:GrowlDisplayPluginKey]) {
ingmarstein@2672
   506
		[self willChangeValueForKey:@"defaultDisplayPluginName"];
ingmarstein@2672
   507
		[self didChangeValueForKey:@"defaultDisplayPluginName"];
ingmarstein@2672
   508
	}
ingmarstein@2672
   509
	if (!object || [object isEqualToString:GrowlSquelchModeKey]) {
ingmarstein@2672
   510
		[self willChangeValueForKey:@"squelchMode"];
ingmarstein@2672
   511
		[self didChangeValueForKey:@"squelchMode"];
ingmarstein@2672
   512
	}
ingmarstein@2672
   513
	if (!object || [object isEqualToString:GrowlMenuExtraKey]) {
ingmarstein@2672
   514
		[self willChangeValueForKey:@"growlMenuEnabled"];
ingmarstein@2672
   515
		[self didChangeValueForKey:@"growlMenuEnabled"];
ingmarstein@2672
   516
	}
ingmarstein@2672
   517
	if (!object || [object isEqualToString:GrowlEnableForwardKey]) {
ingmarstein@2672
   518
		[self willChangeValueForKey:@"forwardingEnabled"];
ingmarstein@2672
   519
		[self didChangeValueForKey:@"forwardingEnabled"];
ingmarstein@2672
   520
	}
ingmarstein@2672
   521
	if (!object || [object isEqualToString:GrowlUpdateCheckKey]) {
ingmarstein@2672
   522
		[self willChangeValueForKey:@"backgroundUpdateCheckEnabled"];
ingmarstein@2672
   523
		[self didChangeValueForKey:@"backgroundUpdateCheckEnabled"];
ingmarstein@2672
   524
	}
ingmarstein@2672
   525
	if (!object || [object isEqualToString:GrowlStickyWhenAwayKey]) {
ingmarstein@2672
   526
		[self willChangeValueForKey:@"stickyWhenAway"];
ingmarstein@2672
   527
		[self didChangeValueForKey:@"stickyWhenAway"];
ingmarstein@2672
   528
	}
rudy@2968
   529
	if (!object || [object isEqualToString:GrowlStickyIdleThresholdKey]) {
rudy@2968
   530
		[self willChangeValueForKey:@"idleThreshold"];
rudy@2968
   531
		[self didChangeValueForKey:@"idleThreshold"];
rudy@2968
   532
	}
ingmarstein@2672
   533
	if (!object || [object isEqualToString:GrowlRemoteRegistrationKey]) {
ingmarstein@2672
   534
		[self willChangeValueForKey:@"remoteRegistrationAllowed"];
ingmarstein@2672
   535
		[self didChangeValueForKey:@"remoteRegistrationAllowed"];
ingmarstein@2672
   536
	}
evands@4016
   537
	
evands@4016
   538
	[pool release];
evands@2514
   539
}
boredzo@2496
   540
boredzo@2434
   541
@end