root/branches/perian-1.0/CPFPerianPrefPaneController.m

Revision 574, 26.9 kB (checked in by gbooker, 1 year ago)

My multi-channel output test. This should work, but that is the point of a beta (to test it).

Line 
1 #import "CPFPerianPrefPaneController.h"
2 #import "UpdateCheckerAppDelegate.h"
3 #include <sys/stat.h>
4
5 #define AC3DynamicRangeKey CFSTR("dynamicRange")
6 #define LastInstalledVersionKey CFSTR("LastInstalledVersion")
7 #define AC3TwoChannelModeKey CFSTR("twoChannelMode")
8
9 //Old
10 #define AC3StereoOverDolbyKey CFSTR("useStereoOverDolby")
11 #define AC3ProLogicIIKey CFSTR("useDolbyProLogicII")
12
13 //A52 Constants
14 #define A52_STEREO 2
15 #define A52_DOLBY 10
16 #define A52_CHANNEL_MASK 15
17 #define A52_LFE 16
18 #define A52_ADJUST_LEVEL 32
19 #define A52_USE_DPLII 64
20
21 @interface NSString (VersionStringCompare)
22 - (BOOL)isVersionStringOlderThan:(NSString *)older;
23 @end
24
25 @implementation NSString (VersionStringCompare)
26 - (BOOL)isVersionStringOlderThan:(NSString *)older
27 {
28         if([self compare:older] == NSOrderedAscending)
29                 return TRUE;
30         if([self hasPrefix:older] && [self length] > [older length] && [self characterAtIndex:[older length]] == 'b')
31                 //1.0b1 < 1.0, so check for it.
32                 return TRUE;
33         return FALSE;
34 }
35 @end
36
37 @interface CPFPerianPrefPaneController(_private)
38 - (void)setAC3DynamicRange:(float)newVal;
39 - (void)saveAC3DynamicRange:(float)newVal;
40 @end
41
42 @implementation CPFPerianPrefPaneController
43
44 #pragma mark Preferences Functions
45
46 - (BOOL)getBoolFromKey:(CFStringRef)key forAppID:(CFStringRef)appID withDefault:(BOOL)defaultValue
47 {
48         CFPropertyListRef value;
49         BOOL ret = defaultValue;
50        
51         value = CFPreferencesCopyAppValue(key, appID);
52         if(value && CFGetTypeID(value) == CFBooleanGetTypeID())
53                 ret = CFBooleanGetValue(value);
54        
55         if(value)
56                 CFRelease(value);
57        
58         return ret;
59 }
60
61 - (void)setKey:(CFStringRef)key forAppID:(CFStringRef)appID fromBool:(BOOL)value
62 {
63         CFPreferencesSetAppValue(key, value ? kCFBooleanTrue : kCFBooleanFalse, appID);
64 }
65
66 - (float)getFloatFromKey:(CFStringRef)key forAppID:(CFStringRef)appID withDefault:(float)defaultValue
67 {
68         CFPropertyListRef value;
69         float ret = defaultValue;
70        
71         value = CFPreferencesCopyAppValue(key, appID);
72         if(value && CFGetTypeID(value) == CFNumberGetTypeID())
73                 CFNumberGetValue(value, kCFNumberFloatType, &ret);
74        
75         if(value)
76                 CFRelease(value);
77        
78         return ret;
79 }
80
81 - (void)setKey:(CFStringRef)key forAppID:(CFStringRef)appID fromFloat:(float)value
82 {
83         CFNumberRef numRef = CFNumberCreate(NULL, kCFNumberFloatType, &value);
84         CFPreferencesSetAppValue(key, numRef, appID);
85         CFRelease(numRef);
86 }
87
88 - (int)getIntFromKey:(CFStringRef)key forAppID:(CFStringRef)appID withDefault:(int)defaultValue
89 {
90         CFPropertyListRef value;
91         int ret = defaultValue;
92        
93         value = CFPreferencesCopyAppValue(key, appID);
94         if(value && CFGetTypeID(value) == CFNumberGetTypeID())
95                 CFNumberGetValue(value, kCFNumberIntType, &ret);
96        
97         if(value)
98                 CFRelease(value);
99        
100         return ret;
101 }
102
103 - (void)setKey:(CFStringRef)key forAppID:(CFStringRef)appID fromInt:(int)value
104 {
105         CFNumberRef numRef = CFNumberCreate(NULL, kCFNumberIntType, &value);
106         CFPreferencesSetAppValue(key, numRef, appID);
107         CFRelease(numRef);
108 }
109
110 - (NSString *)getStringFromKey:(CFStringRef)key forAppID:(CFStringRef)appID
111 {
112         CFPropertyListRef value;
113         NSString *ret = nil;
114        
115         value = CFPreferencesCopyAppValue(key, appID);
116         if(value && CFGetTypeID(value) == CFStringGetTypeID())
117                 ret = [NSString stringWithString:(NSString *)value];
118        
119         if(value)
120                 CFRelease(value);
121        
122         return ret;
123 }
124
125 - (void)setKey:(CFStringRef)key forAppID:(CFStringRef)appID fromString:(NSString *)value
126 {
127         CFPreferencesSetAppValue(key, value, appID);
128 }
129
130 #pragma mark Private Functions
131
132 - (NSString *)installationBasePath:(BOOL)userInstallation
133 {
134         if(userInstallation)
135                 return NSHomeDirectory();
136         return [NSString stringWithString:@"/"];
137 }
138
139 - (NSString *)quickTimeComponentDir:(BOOL)userInstallation
140 {
141         return [[self installationBasePath:userInstallation] stringByAppendingPathComponent:@"Library/QuickTime"];
142 }
143
144 - (NSString *)coreAudioComponentDir:(BOOL)userInstallation
145 {
146         return [[self installationBasePath:userInstallation] stringByAppendingPathComponent:@"Library/Audio/Plug-Ins/Components"];
147 }
148
149 - (NSString *)frameworkComponentDir:(BOOL)userInstallation
150 {
151         return [[self installationBasePath:userInstallation] stringByAppendingPathComponent:@"Library/Frameworks"];
152 }
153
154 - (NSString *)basePathForType:(ComponentType)type user:(BOOL)userInstallation
155 {
156         NSString *path = nil;
157        
158         switch(type)
159         {
160                 case ComponentTypeCoreAudio:
161                         path = [self coreAudioComponentDir:userInstallation];
162                         break;
163                 case ComponentTypeQuickTime:
164                         path = [self quickTimeComponentDir:userInstallation];
165                         break;
166                 case ComponentTypeFramework:
167                         path = [self frameworkComponentDir:userInstallation];
168                         break;
169         }
170         return path;
171 }
172
173 - (InstallStatus)installStatusForComponent:(NSString *)component type:(ComponentType)type withMyVersion:(NSString *)myVersion
174 {
175         NSString *path = nil;
176         InstallStatus ret = InstallStatusNotInstalled;
177        
178         path = [[self basePathForType:type user:userInstalled] stringByAppendingPathComponent:component];
179        
180         NSDictionary *infoDict = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@"Contents/Info.plist"]];
181         if(infoDict != nil)
182         {
183                 NSString *currentVersion = [infoDict objectForKey:BundleVersionKey];
184                 if([currentVersion isVersionStringOlderThan:myVersion])
185                         ret = InstallStatusOutdated;
186                 else
187                         ret = InstallStatusInstalled;
188         }
189        
190         /* Check other installation type */
191         path = [[self basePathForType:type user:!userInstalled] stringByAppendingPathComponent:component];
192        
193         infoDict = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@"Contents/Info.plist"]];
194         if(infoDict == nil)
195                 /* Above result is all there is */
196                 return ret;
197        
198         return setWrongLocationInstalled(ret);
199 }
200
201 - (void)setInstalledVersionString
202 {
203         NSString *path = [[self basePathForType:ComponentTypeQuickTime user:userInstalled] stringByAppendingPathComponent:@"Perian.component"];
204         NSString *currentVersion = @"-";
205         NSDictionary *infoDict = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@"Contents/Info.plist"]];
206         if (infoDict != nil)
207                 currentVersion = [infoDict objectForKey:BundleVersionKey];
208         [textField_currentVersion setStringValue:[NSLocalizedString(@"Installed Version: ", @"") stringByAppendingString:currentVersion]];
209 }
210
211 #pragma mark Preference Pane Support
212
213 - (id)initWithBundle:(NSBundle *)bundle
214 {
215         if ( ( self = [super initWithBundle:bundle] ) != nil ) {
216                 perianForumURL = [[NSURL alloc] initWithString:@"http://forums.cocoaforge.com/index.php?c=12"];
217                 perianDonateURL = [[NSURL alloc] initWithString:@"http://perian.org"];
218                 perianWebSiteURL = [[NSURL alloc] initWithString:@"http://perian.org"];
219                
220                 perianAppID = CFSTR("org.perian.Perian");
221                 a52AppID = CFSTR("com.cod3r.a52codec");
222                
223                 NSString *myPath = [[self bundle] bundlePath];
224                
225                 if([myPath hasPrefix:@"/Library"])
226                         userInstalled = NO;
227                 else
228                         userInstalled = YES;
229         }
230        
231         return self;
232 }
233
234 - (NSDictionary *)myInfoDict;
235 {
236         return [NSDictionary dictionaryWithContentsOfFile:[[[self bundle] bundlePath] stringByAppendingPathComponent:@"Contents/Info.plist"]];
237 }
238
239 - (void)checkForInstallation
240 {
241         NSDictionary *infoDict = [self myInfoDict];
242         NSString *myVersion = [infoDict objectForKey:BundleVersionKey];
243        
244         [self setInstalledVersionString];
245         installStatus = [self installStatusForComponent:@"Perian.component" type:ComponentTypeQuickTime withMyVersion:myVersion];
246         if(currentInstallStatus(installStatus) == InstallStatusNotInstalled)
247         {
248                 [textField_installStatus setStringValue:NSLocalizedString(@"Perian is not Installed", @"")];
249                 [button_install setTitle:NSLocalizedString(@"Install Perian", @"")];
250         }
251         else if(currentInstallStatus(installStatus) == InstallStatusOutdated)
252         {
253                 [textField_installStatus setStringValue:NSLocalizedString(@"Perian is Installed, but Outdated", @"")];
254                 [button_install setTitle:NSLocalizedString(@"Update Perian", @"")];
255         }
256         else
257         {
258                 //Perian is fine, but check components
259                 NSDictionary *myComponentsInfo = [infoDict objectForKey:ComponentInfoDictionaryKey];
260                 if(myComponentsInfo != nil)
261                 {
262                         NSEnumerator *componentEnum = [myComponentsInfo objectEnumerator];
263                         NSDictionary *componentInfo = nil;
264                         while((componentInfo = [componentEnum nextObject]) != nil)
265                         {
266                                 InstallStatus tstatus = [self installStatusForComponent:[componentInfo objectForKey:ComponentNameKey] type:[[componentInfo objectForKey:ComponentTypeKey] intValue] withMyVersion:[componentInfo objectForKey:BundleVersionKey]];
267                                 if(tstatus < installStatus)
268                                         installStatus = tstatus;
269                         }
270                         switch(installStatus)
271                         {
272                                 case InstallStatusInstalledInWrongLocation:
273                                 case InstallStatusNotInstalled:
274                                         [textField_installStatus setStringValue:NSLocalizedString(@"Perian is Installed, but parts are Not Installed", @"")];
275                                         [button_install setTitle:NSLocalizedString(@"Install Perian", @"")];
276                                         break;
277                                 case InstallStatusOutdatedWithAnotherInWrongLocation:
278                                 case InstallStatusOutdated:
279                                         [textField_installStatus setStringValue:NSLocalizedString(@"Perian is Installed, but parts are Outdated", @"")];
280                                         [button_install setTitle:NSLocalizedString(@"Update Perian", @"")];
281                                         break;
282                                 case InstallStatusInstalledInBothLocations:
283                                         [textField_installStatus setStringValue:NSLocalizedString(@"Perian is Installed Twice", @"")];
284                                         [button_install setTitle:NSLocalizedString(@"Correct Installation", @"")];
285                                         break;
286                                 case InstallStatusInstalled:
287                                         [textField_installStatus setStringValue:NSLocalizedString(@"Perian is Installed", @"")];
288                                         [button_install setTitle:NSLocalizedString(@"Remove Perian", @"")];
289                                         break;
290                         }
291                 }
292                 else if(isWrongLocationInstalled(installStatus))
293                 {
294                         [textField_installStatus setStringValue:NSLocalizedString(@"Perian is Installed Twice", @"")];
295                         [button_install setTitle:NSLocalizedString(@"Correct Installation", @"")];
296                 }
297                 else
298                 {
299                         [textField_installStatus setStringValue:NSLocalizedString(@"Perian is Installed", @"")];
300                         [button_install setTitle:NSLocalizedString(@"Remove Perian", @"")];
301                 }
302                
303         }
304 }
305
306 - (int)upgradeA52Prefs
307 {
308         int twoChannelMode;
309         if([self getBoolFromKey:AC3StereoOverDolbyKey forAppID:a52AppID withDefault:NO])
310                 twoChannelMode = A52_STEREO;
311         else if([self getBoolFromKey:AC3ProLogicIIKey forAppID:a52AppID withDefault:NO])
312                 twoChannelMode = A52_DOLBY | A52_USE_DPLII;
313         else
314                 twoChannelMode = A52_DOLBY;
315        
316         [self setKey:AC3TwoChannelModeKey forAppID:a52AppID fromInt:twoChannelMode];
317         return twoChannelMode;
318 }
319
320 - (void)didSelect
321 {
322         /* General */
323         [self checkForInstallation];
324         NSString *lastInstVersion = [self getStringFromKey:LastInstalledVersionKey forAppID:perianAppID];
325         NSString *myVersion = [[self myInfoDict] objectForKey:BundleVersionKey];
326        
327         NSAttributedString              *about;
328     about = [[[NSAttributedString alloc] initWithPath:[[self bundle] pathForResource:@"Read Me" ofType:@"rtf"]
329                                                                          documentAttributes:nil] autorelease];
330         [[textView_about textStorage] setAttributedString:about];
331         [[textView_about enclosingScrollView] setLineScroll:0];
332         [[textView_about enclosingScrollView] setPageScroll:0];
333        
334         if((lastInstVersion == nil || [lastInstVersion isVersionStringOlderThan:myVersion]) && installStatus != InstallStatusInstalled)
335         {
336                 /*Check for temp after an update */
337                 BOOL isDir = NO;
338                 NSString *tempPrefPane = [NSTemporaryDirectory() stringByAppendingPathComponent:@"PerianPane.prefPane"];
339                 int tag;
340                
341                 if([[NSFileManager defaultManager] fileExistsAtPath:tempPrefPane isDirectory:&isDir] && isDir)
342                         [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation
343                                                                                                                  source:[tempPrefPane stringByDeletingLastPathComponent]
344                                                                                                         destination:@""
345                                                                                                                   files:[NSArray arrayWithObject:[tempPrefPane lastPathComponent]]
346                                                                                                                         tag:&tag];
347                
348                 [self installUninstall:nil];
349                 [self setKey:LastInstalledVersionKey forAppID:perianAppID fromString:myVersion];
350         }
351        
352         NSDate *updateDate = (NSDate *)CFPreferencesCopyAppValue((CFStringRef)NEXT_RUN_KEY, perianAppID);
353         if([updateDate timeIntervalSinceNow] > 1000000000) //futureDate
354                 [button_autoUpdateCheck setIntValue:0];
355         else
356                 [button_autoUpdateCheck setIntValue:1];
357         [updateDate release];
358        
359         /* A52 Prefs */
360         int twoChannelMode = [self getIntFromKey:AC3TwoChannelModeKey forAppID:a52AppID withDefault:0xffffffff];
361         if(twoChannelMode != 0xffffffff)
362         {
363                 /* sanity checks */
364                 if(twoChannelMode & A52_CHANNEL_MASK & 0xf7 != 2)
365                 {
366                         /* matches 2 and 10, which is Stereo and Dolby */
367                         twoChannelMode = A52_DOLBY;
368                 }
369                 twoChannelMode &= ~A52_ADJUST_LEVEL & ~A52_LFE;         
370         }
371         else
372                 twoChannelMode = [self upgradeA52Prefs];
373         CFPreferencesSetAppValue(AC3StereoOverDolbyKey, NULL, a52AppID);
374         CFPreferencesSetAppValue(AC3ProLogicIIKey, NULL, a52AppID);
375         switch(twoChannelMode)
376         {
377                 case A52_STEREO:
378                         [popup_outputMode selectItemAtIndex:0];
379                         break;
380                 case A52_DOLBY:
381                         [popup_outputMode selectItemAtIndex:1];
382                         break;
383                 case A52_DOLBY | A52_USE_DPLII:
384                         [popup_outputMode selectItemAtIndex:2];
385                         break;
386                 default:
387                         [popup_outputMode selectItemAtIndex:3];
388                         break;                 
389         }
390        
391         [self setAC3DynamicRange:[self getFloatFromKey:AC3DynamicRangeKey forAppID:a52AppID withDefault:1.0]];
392 }
393
394 - (void)didUnselect
395 {
396         CFPreferencesAppSynchronize(perianAppID);
397         CFPreferencesAppSynchronize(a52AppID);
398 }
399
400 - (void) dealloc {
401         [perianForumURL release];
402         [perianDonateURL release];
403         [perianWebSiteURL release];
404         if(auth != nil)
405                 AuthorizationFree(auth, 0);
406         [errorString release];
407         [super dealloc];
408 }
409
410 #pragma mark Install/Uninstall
411
412 /* Shamelessly ripped from Sparkle */
413 - (BOOL)_extractArchivePath:archivePath toDestination:(NSString *)destination finalPath:(NSString *)finalPath
414 {
415         BOOL ret = NO, oldExist = NO;
416         struct stat sb;
417        
418         if(stat([finalPath fileSystemRepresentation], &sb) == 0)
419                 oldExist = YES;
420        
421         char *buf = NULL;
422         if(oldExist)
423                 asprintf(&buf,
424                                  "mv -f \"$DST_COMPONENT\" \"$TMP_PATH\" && "
425                                  "ditto -x -k --rsrc \"$SRC_ARCHIVE\" \"$DST_PATH\" && "
426                                  "rm -rf \"$TMP_PATH\"");
427         else
428                 asprintf(&buf,
429                                  "mkdir -p \"$DST_PATH\" && "
430                                  "ditto -x -k --rsrc \"$SRC_ARCHIVE\" \"$DST_PATH\"");
431         if(!buf)
432         {
433                 [errorString appendFormat:NSLocalizedString(@"Could not allocate memory for extraction command\n", @"")];
434                 return FALSE;
435         }
436        
437         setenv("SRC_ARCHIVE", [archivePath fileSystemRepresentation], 1);
438         setenv("DST_PATH", [destination fileSystemRepresentation], 1);
439         setenv("DST_COMPONENT", [finalPath fileSystemRepresentation], 1);
440         setenv("TMP_PATH", [[finalPath stringByAppendingPathExtension:@"old"] fileSystemRepresentation], 1);
441
442         int status = system(buf);
443         if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
444                 ret = YES;
445         else
446                 [errorString appendFormat:NSLocalizedString(@"Extraction for %@ failed\n", @""), archivePath];
447
448         free(buf);
449         unsetenv("SRC_ARCHIVE");
450         unsetenv("$DST_COMPONENT");
451         unsetenv("TMP_PATH");
452         unsetenv("DST_PATH");
453         return ret;
454 }
455
456 - (BOOL)_authenticatedExtractArchivePath:(NSString *)archivePath toDestination:(NSString *)destination finalPath:(NSString *)finalPath
457 {
458         BOOL ret = NO, oldExist = NO;
459         struct stat sb;
460         if(stat([finalPath fileSystemRepresentation], &sb) == 0)
461                 oldExist = YES;
462        
463         char *buf = NULL;
464         if(oldExist)
465                 asprintf(&buf,
466                                  "mv -f \"$DST_COMPONENT\" \"$TMP_PATH\" && "
467                                  "ditto -x -k --rsrc \"$SRC_ARCHIVE\" \"$DST_PATH\" && "
468                                  "rm -rf \"$TMP_PATH\" && "
469                                  "chown -R %d:%d \"$DST_COMPONENT\"",
470                                  sb.st_uid, sb.st_gid);
471         else
472                 asprintf(&buf,
473                                  "mkdir -p \"$DST_PATH\" && "
474                                  "ditto -x -k --rsrc \"$SRC_ARCHIVE\" \"$DST_PATH\" && "
475                                  "chown -R %d:%d \"$DST_COMPONENT\"",
476                                  sb.st_uid, sb.st_gid);
477         if(!buf)
478         {
479                 [errorString appendFormat:NSLocalizedString(@"Could not allocate memory for extraction command\n", @"")];
480                 return FALSE;
481         }
482        
483         setenv("SRC_ARCHIVE", [archivePath fileSystemRepresentation], 1);
484         setenv("DST_COMPONENT", [finalPath fileSystemRepresentation], 1);
485         setenv("TMP_PATH", [[finalPath stringByAppendingPathExtension:@"old"] fileSystemRepresentation], 1);
486         setenv("DST_PATH", [destination fileSystemRepresentation], 1);
487        
488         char* arguments[] = { "-c", buf, NULL };
489         if(AuthorizationExecuteWithPrivileges(auth, "/bin/sh", kAuthorizationFlagDefaults, arguments, NULL) == errAuthorizationSuccess)
490         {
491                 int status;
492                 int pid = wait(&status);
493                 if(pid != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0)
494                         ret = YES;
495                 else
496                         [errorString appendFormat:NSLocalizedString(@"Extraction for %@ failed\n", @""), archivePath];
497         }
498         else
499                 [errorString appendFormat:NSLocalizedString(@"Authentication failed for extraction for %@\n", @""), archivePath];
500        
501         free(buf);
502         unsetenv("SRC_ARCHIVE");
503         unsetenv("$DST_COMPONENT");
504         unsetenv("TMP_PATH");
505         unsetenv("DST_PATH");
506         return ret;
507 }
508
509 - (BOOL)_authenticatedRemove:(NSString *)componentPath
510 {
511         BOOL ret = NO;
512         struct stat sb;
513         if(stat([componentPath fileSystemRepresentation], &sb) != 0)
514                 /* No error, just forget it */
515                 return FALSE;
516        
517         char *buf = NULL;
518         asprintf(&buf,
519                          "rm -rf \"$COMP_PATH\"");
520         if(!buf)
521         {
522                 [errorString appendFormat:NSLocalizedString(@"Could not allocate memory for removal command\n", @"")];
523                 return FALSE;
524         }
525        
526         setenv("COMP_PATH", [componentPath fileSystemRepresentation], 1);
527        
528         char* arguments[] = { "-c", buf, NULL };
529         if(AuthorizationExecuteWithPrivileges(auth, "/bin/sh", kAuthorizationFlagDefaults, arguments, NULL) == errAuthorizationSuccess)
530         {
531                 int status;
532                 int pid = wait(&status);
533                 if(pid != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0)
534                         ret = YES;
535                 else
536                         [errorString appendFormat:NSLocalizedString(@"Removal for %@ failed\n", @""), componentPath];
537         }
538         else
539                 [errorString appendFormat:NSLocalizedString(@"Authentication failed for removal for %@\n", @""), componentPath];
540         free(buf);
541         unsetenv("COMP_PATH");
542         return ret;
543 }
544
545
546 - (BOOL)installArchive:(NSString *)archivePath forPiece:(NSString *)component type:(ComponentType)type withMyVersion:(NSString *)myVersion
547 {
548         NSString *containingDir = [self basePathForType:type user:userInstalled];
549         BOOL ret = YES;
550
551         InstallStatus pieceStatus = [self installStatusForComponent:component type:type withMyVersion:myVersion];
552         if(!userInstalled && currentInstallStatus(pieceStatus) != InstallStatusInstalled)
553         {
554                 BOOL result = [self _authenticatedExtractArchivePath:archivePath toDestination:containingDir finalPath:[containingDir stringByAppendingPathComponent:component]];
555                 if(result == NO)
556                         ret = NO;
557         }
558         else
559         {
560                 //Not authenticated
561                 if(currentInstallStatus(pieceStatus) == InstallStatusOutdated)
562                 {
563                         //Remove the old one here
564                         int tag = 0;
565                         BOOL result = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:containingDir destination:@"" files:[NSArray arrayWithObject:component] tag:&tag];
566                         if(result == NO)
567                                 ret = NO;
568                 }
569                 if(currentInstallStatus(pieceStatus) != InstallStatusInstalled)
570                 {
571                         //Decompress and install new one
572                         BOOL result = [self _extractArchivePath:archivePath toDestination:containingDir finalPath:[containingDir stringByAppendingPathComponent:component]];
573                         if(result == NO)
574                                 ret = NO;
575                 }               
576         }
577         if(ret != NO && isWrongLocationInstalled(pieceStatus) != 0)
578         {
579                 /* Let's try and remove the wrong one, if we can, but only if install succeeded */
580                 containingDir = [self basePathForType:type user:!userInstalled];
581
582                 if(userInstalled)
583                         [self _authenticatedRemove:[containingDir stringByAppendingPathComponent:component]];
584                 else
585                 {
586                         int tag = 0;
587                         [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:containingDir destination:@"" files:[NSArray arrayWithObject:component] tag:&tag];
588                 }
589         }
590         return ret;
591 }
592
593 - (void)install:(id)sender
594 {
595         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
596         NSDictionary *infoDict = [self myInfoDict];
597         NSDictionary *myComponentsInfo = [infoDict objectForKey:ComponentInfoDictionaryKey];
598         NSString *componentPath = [[[self bundle] resourcePath] stringByAppendingPathComponent:@"Components"];
599         NSString *coreAudioComponentPath = [componentPath stringByAppendingPathComponent:@"CoreAudio"];
600         NSString *quickTimeComponentPath = [componentPath stringByAppendingPathComponent:@"QuickTime"];
601         NSString *frameworkComponentPath = [componentPath stringByAppendingPathComponent:@"Frameworks"];
602
603         [errorString release];
604         errorString = [[NSMutableString alloc] init];
605         /* This doesn't ask the user, so create it anyway.  If we don't need it, no problem */
606         if(AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &auth) != errAuthorizationSuccess)
607                 /* Oh well, hope we don't need it */
608                 auth = nil;
609        
610         [self installArchive:[componentPath stringByAppendingPathComponent:@"Perian.zip"] forPiece:@"Perian.component" type:ComponentTypeQuickTime withMyVersion:[infoDict objectForKey:BundleVersionKey]];
611        
612         NSEnumerator *componentEnum = [myComponentsInfo objectEnumerator];
613         NSDictionary *myComponent = nil;
614         while((myComponent = [componentEnum nextObject]) != nil)
615         {
616                 NSString *archivePath = nil;
617                 ComponentType type = [[myComponent objectForKey:ComponentTypeKey] intValue];
618                 switch(type)
619                 {
620                         case ComponentTypeCoreAudio:
621                                 archivePath = [coreAudioComponentPath stringByAppendingPathComponent:[myComponent objectForKey:ComponentArchiveNameKey]];
622                                 break;
623                         case ComponentTypeQuickTime:
624                                 archivePath = [quickTimeComponentPath stringByAppendingPathComponent:[myComponent objectForKey:ComponentArchiveNameKey]];
625                                 break;
626                         case ComponentTypeFramework:
627                                 archivePath = [frameworkComponentPath stringByAppendingPathComponent:[myComponent objectForKey:ComponentArchiveNameKey]];
628                                 break;
629                 }
630                 [self installArchive:archivePath forPiece:[myComponent objectForKey:ComponentNameKey] type:type withMyVersion:[myComponent objectForKey:BundleVersionKey]];
631         }
632         if(auth != nil)
633         {
634                 AuthorizationFree(auth, 0);
635                 auth = nil;
636         }
637         [self performSelectorOnMainThread:@selector(installComplete:) withObject:nil waitUntilDone:NO];
638         [pool release];
639 }
640
641 - (void)uninstall:(id)sender
642 {
643         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
644         NSDictionary *infoDict = [self myInfoDict];
645         NSDictionary *myComponentsInfo = [infoDict objectForKey:ComponentInfoDictionaryKey];
646
647         [errorString release];
648         errorString = [[NSMutableString alloc] init];
649         /* This doesn't ask the user, so create it anyway.  If we don't need it, no problem */
650         if(AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &auth) != errAuthorizationSuccess)
651                 /* Oh well, hope we don't need it */
652                 auth = nil;
653        
654         int tag = 0;
655         BOOL result = NO;
656         if(auth != nil && !userInstalled)
657                 [self _authenticatedRemove:[[self quickTimeComponentDir:userInstalled] stringByAppendingPathComponent:@"Perian.component"]];
658         else
659                 result = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:[self quickTimeComponentDir:userInstalled] destination:@"" files:[NSArray arrayWithObject:@"Perian.component"] tag:&tag];
660        
661         NSEnumerator *componentEnum = [myComponentsInfo objectEnumerator];
662         NSDictionary *myComponent = nil;
663         while((myComponent = [componentEnum nextObject]) != nil)
664         {
665                 ComponentType type = [[myComponent objectForKey:ComponentTypeKey] intValue];
666                 NSString *directory = [self basePathForType:type user:userInstalled];
667                 if(auth != nil && !userInstalled)
668                         [self _authenticatedRemove:[directory stringByAppendingPathComponent:[myComponent objectForKey:ComponentNameKey]]];
669                 else
670                         result = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:directory destination:@"" files:[NSArray arrayWithObject:[myComponent objectForKey:ComponentNameKey]] tag:&tag];
671         }
672         if(auth != nil)
673         {
674                 AuthorizationFree(auth, 0);
675                 auth = nil;
676         }
677        
678         [self performSelectorOnMainThread:@selector(installComplete:) withObject:nil waitUntilDone:NO];
679         [pool release];
680 }
681
682 - (IBAction)installUninstall:(id)sender
683 {
684         [progress_install startAnimation:sender];
685         if(installStatus == InstallStatusInstalled)
686                 [NSThread detachNewThreadSelector:@selector(uninstall:) toTarget:self withObject:nil];
687         else
688                 [NSThread detachNewThreadSelector:@selector(install:) toTarget:self withObject:nil];
689 }
690
691 - (void)installComplete:(id)sender
692 {
693         [progress_install stopAnimation:sender];
694         [self checkForInstallation];
695 }
696
697 #pragma mark Check Updates
698 - (IBAction)updateCheck:(id)sender
699 {
700         FSRef updateCheckRef;
701        
702         CFPreferencesSetAppValue((CFStringRef)NEXT_RUN_KEY, NULL, perianAppID);
703         CFPreferencesSetAppValue((CFStringRef)MANUAL_RUN_KEY, [NSNumber numberWithBool:YES], perianAppID);
704         CFPreferencesAppSynchronize(perianAppID);
705         OSStatus status = FSPathMakeRef((UInt8 *)[[[[self bundle] bundlePath] stringByAppendingPathComponent:@"Contents/Resources/PerianUpdateChecker.app"] fileSystemRepresentation], &updateCheckRef, NULL);
706         if(status != noErr)
707                 return;
708        
709         LSOpenFSRef(&updateCheckRef, NULL);
710 }
711
712 - (IBAction)setAutoUpdateCheck:(id)sender
713 {
714         CFStringRef key = (CFStringRef)NEXT_RUN_KEY;
715         if([button_autoUpdateCheck intValue])
716                 CFPreferencesSetAppValue(key, [NSDate dateWithTimeIntervalSinceNow:TIME_INTERVAL_TIL_NEXT_RUN], perianAppID);
717         else
718                 CFPreferencesSetAppValue(key, [NSDate distantFuture], perianAppID);
719 }
720
721
722 #pragma mark AC3
723 - (IBAction)setAC3DynamicRangePopup:(id)sender
724 {
725         int selected = [popup_ac3DynamicRangeType indexOfSelectedItem];
726         switch(selected)
727         {
728                 case 0:
729                         [self saveAC3DynamicRange:1.0];
730                         break;
731                 case 1:
732                         [self saveAC3DynamicRange:2.0];
733                         break;
734                 case 3:
735                         [NSApp beginSheet:window_dynRangeSheet modalForWindow:[[self mainView] window] modalDelegate:nil didEndSelector:nil contextInfo:NULL];
736                         break;
737                 default:
738                         break;
739         }
740 }
741
742 - (IBAction)set2ChannelModePopup:(id)sender;
743 {
744         int selected = [popup_outputMode indexOfSelectedItem];
745         switch(selected)
746         {
747                 case 0:
748                         [self setKey:AC3TwoChannelModeKey forAppID:a52AppID fromInt:A52_STEREO];
749                         break;
750                 case 1:
751                         [self setKey:AC3TwoChannelModeKey forAppID:a52AppID fromInt:A52_DOLBY];
752                         break;
753                 case 2:
754                         [self setKey:AC3TwoChannelModeKey forAppID:a52AppID fromInt:A52_DOLBY | A52_USE_DPLII];
755                         break;
756                 case 3:
757                         [self setKey:AC3TwoChannelModeKey forAppID:a52AppID fromInt:0];
758                         break;
759                 default:
760                         break;
761         }       
762 }
763
764 - (void)setAC3DynamicRange:(float)newVal
765 {
766         if(newVal > 4.0)
767                 newVal = 4.0;
768         if(newVal < 0.0)
769                 newVal = 0.0;
770        
771         nextDynValue = newVal;
772         [textField_ac3DynamicRangeValue setFloatValue:newVal];
773         [slider_ac3DynamicRangeSlider setFloatValue:newVal];
774         if(newVal == 1.0)
775                 [popup_ac3DynamicRangeType selectItemAtIndex:0];
776         else if(newVal == 2.0)
777                 [popup_ac3DynamicRangeType selectItemAtIndex:1];
778         else
779                 [popup_ac3DynamicRangeType selectItemAtIndex:3];
780 }
781
782 - (void)saveAC3DynamicRange:(float)newVal
783 {
784         [self setKey:AC3DynamicRangeKey forAppID:a52AppID fromFloat:newVal];
785         [self setAC3DynamicRange:newVal];
786 }
787
788 - (IBAction)setAC3DynamicRangeValue:(id)sender
789 {
790         float newVal = [textField_ac3DynamicRangeValue floatValue];
791        
792         [self setAC3DynamicRange:newVal];
793 }
794
795 - (IBAction)setAC3DynamicRangeSlider:(id)sender
796 {
797         float newVal = [slider_ac3DynamicRangeSlider floatValue];
798        
799         [self setAC3DynamicRange:newVal];
800 }
801
802 - (IBAction)cancelDynRangeSheet:(id)sender
803 {
804         [self setAC3DynamicRange:[self getFloatFromKey:AC3DynamicRangeKey forAppID:a52AppID withDefault:1.0]];
805         [NSApp endSheet:window_dynRangeSheet];
806         [window_dynRangeSheet orderOut:self];
807 }
808
809 - (IBAction)saveDynRangeSheet:(id)sender;
810 {
811         [NSApp endSheet:window_dynRangeSheet];
812         [self saveAC3DynamicRange:nextDynValue];
813         [window_dynRangeSheet orderOut:self];
814 }
815
816 #pragma mark About
817 - (IBAction)launchWebsite:(id)sender
818 {
819         [[NSWorkspace sharedWorkspace] openURL:perianWebSiteURL];
820 }
821
822 - (IBAction)launchDonate:(id)sender
823 {
824        
825         [[NSWorkspace sharedWorkspace] openURL:perianDonateURL];
826 }
827
828 - (IBAction)launchForum:(id)sender
829 {
830        
831         [[NSWorkspace sharedWorkspace] openURL:perianForumURL];
832        
833 }
834
835 @end
Note: See TracBrowser for help on using the browser.