root/branches/perian-1.1/CPFPerianPrefPaneController.m

Revision 923, 31.7 kB (checked in by astrange, 2 months ago)

Merge trunk to the branch.
Disable ssse3 qpel stuff (https://roundup.mplayerhq.hu/roundup/ffmpeg/issue463)

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