diff --git a/ChangeLog b/ChangeLog index f3200dcfc..a49289871 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2009-02-11 Richard Frith-Macdonald + + * Tools/make_services.m: Check applications for CFBundleURLTypes + (see http://developer.apple.com/documentation/MacOSX/conceptual/BPRuntimeConfig/Articles/PListKeys.html) + * Source/NSWorkspace.m: ([-openURL:]) try to open non-file URLs using + an app found based on CFBundleURLTypes, and if that fails try to use + any available 'OpenURL' service. + 2009-02-11 Richard Frith-Macdonald * Source/GSTheme.m: Fix typo in include diff --git a/Source/NSWorkspace.m b/Source/NSWorkspace.m index 470d9081c..cb73e11ae 100644 --- a/Source/NSWorkspace.m +++ b/Source/NSWorkspace.m @@ -65,6 +65,7 @@ #include "AppKit/NSWorkspace.h" #include "AppKit/NSApplication.h" #include "AppKit/NSImage.h" +#include "AppKit/NSPasteboard.h" #include "AppKit/NSView.h" #include "AppKit/NSPanel.h" #include "AppKit/NSWindow.h" @@ -803,7 +804,55 @@ static NSString *_rootPath = @"/"; } else { - return NO; + NSDictionary *map; + NSString *appName; + + /* Look up an application to handle this URL scheme. + */ + map = [applications objectForKey: @"GSSchemesMap"]; + appName = [map objectForKey: [[url scheme] lowercaseString]]; + if (appName != nil) + { + NSString *urlString = [url absoluteString]; + id app; + + /* Now try to get the application to open the URL. + */ + app = [self _connectApplication: appName]; + if (app == nil) + { + NSArray *args; + + args = [NSArray arrayWithObjects: @"-GSFilePath", urlString, nil]; + return [self _launchApplication: appName arguments: args]; + } + else + { + NS_DURING + { + [app application: NSApp openFile: urlString]; + } + NS_HANDLER + { + NSWarnLog(@"Failed to contact '%@' to open file", appName); + return NO; + } + NS_ENDHANDLER + } + [NSApp deactivate]; + return YES; + } + else + { + NSPasteboard *pb; + + /* No application found to open the URL. + * Try any OpenURL service available. + */ + pb = [NSPasteboard pasteboardWithUniqueName]; + [url writeToPasteboard: pb]; + return NSPerformService(@"OpenURL", pb); + } } } diff --git a/Tools/make_services.m b/Tools/make_services.m index beadaf3aa..a1dc0ae58 100644 --- a/Tools/make_services.m +++ b/Tools/make_services.m @@ -57,6 +57,7 @@ static NSMutableDictionary *printMap; static NSMutableDictionary *spellMap; static NSMutableDictionary *applicationMap; static NSMutableDictionary *extensionsMap; +static NSMutableDictionary *schemesMap; static Class aClass; static Class dClass; @@ -132,6 +133,7 @@ main(int argc, char** argv, char **env_c) spellMap = [NSMutableDictionary dictionaryWithCapacity: 8]; applicationMap = [NSMutableDictionary dictionaryWithCapacity: 64]; extensionsMap = [NSMutableDictionary dictionaryWithCapacity: 64]; + schemesMap = [NSMutableDictionary dictionaryWithCapacity: 64]; env = [proc environment]; args = [proc arguments]; @@ -280,6 +282,7 @@ main(int argc, char** argv, char **env_c) oldMap = nil; } [applicationMap setObject: extensionsMap forKey: @"GSExtensionsMap"]; + [applicationMap setObject: schemesMap forKey: @"GSSchemesMap"]; if ([applicationMap isEqual: oldMap] == NO) { data = [NSSerializer serializePropertyList: applicationMap]; @@ -296,6 +299,95 @@ main(int argc, char** argv, char **env_c) exit(EXIT_SUCCESS); } +/* + * Load information about the shemes of URLs that an application supports. + * For each scheme found, produce a dictionary, keyed by app name, that + * contains dictionaries giving scheme info for that extension. + * NB. in order to make schemes case-insensiteve - we always convert + * to lowercase. + */ +static void addSchemesForApplication(NSDictionary *info, NSString *app) +{ + unsigned int i; + id o0; + NSArray *a0; + + + o0 = [info objectForKey: @"CFBundleURLTypes"]; + + if (o0) + { + if ([o0 isKindOfClass: aClass] == NO) + { + if (verbose > 0) + NSLog(@"bad app CFBundleURLTypes (not an array) - %@", app); + return; + } + a0 = (NSArray*)o0; + i = [a0 count]; + while (i-- > 0) + { + NSDictionary *t; + NSArray *a1; + id o1 = [a0 objectAtIndex: i]; + unsigned int j; + + if ([o1 isKindOfClass: dClass] == NO) + { + if (verbose > 0) + NSLog(@"bad app CFBundleURLTypes (type not a dictionary) - %@", + app); + return; + } + /* + * Set 't' to the dictionary defining a particular file type. + */ + t = (NSDictionary*)o1; + + if (o1 == nil) + { + o1 = [t objectForKey: @"CFBundleURLSchemes"]; + } + if (o1 == nil) + { + continue; + } + if ([o1 isKindOfClass: aClass] == NO) + { + if (verbose > 0) + NSLog(@"bad app CFBundleURLTypes (schemes not an array) - %@", + app); + return; + } + a1 = (NSArray*)o1; + j = [a1 count]; + while (j-- > 0) + { + NSString *e; + NSMutableDictionary *d; + + e = [[a1 objectAtIndex: j] lowercaseString]; + if ([e length] == 0) + { + if (verbose > 0) + NSLog(@"Illegal (nul) scheme ignored for - %@", app); + return; + } + d = [schemesMap objectForKey: e]; + if (d == nil) + { + d = [NSMutableDictionary dictionaryWithCapacity: 1]; + [schemesMap setObject: d forKey: e]; + } + if ([d objectForKey: app] == nil) + { + [d setObject: t forKey: app]; + } + } + } + } +} + /* * Load information about the types of files that an application supports. * For each extension found, produce a dictionary, keyed by app name, that @@ -345,7 +437,7 @@ static void addExtensionsForApplication(NSDictionary *info, NSString *app) t = (NSDictionary*)o1; o1 = [t objectForKey: @"NSUnixExtensions"]; - if(o1 == nil) + if (o1 == nil) { o1 = [t objectForKey: @"CFBundleTypeExtensions"]; } @@ -391,7 +483,7 @@ static void addExtensionsForApplication(NSDictionary *info, NSString *app) NSDictionary *extensions; o0 = [info objectForKey: @"NSExtensions"]; - if(o0 == nil) + if (o0 == nil) { o0 = [info objectForKey: @"CFBundleTypeExtensions"]; } @@ -511,6 +603,7 @@ scanDirectory(NSMutableDictionary *services, NSString *path) } addExtensionsForApplication(info, name); + addSchemesForApplication(info, name); } else if (verbose > 0) { @@ -653,6 +746,7 @@ scanApplications(NSMutableDictionary *services, NSString *path) } addExtensionsForApplication(info, name); + addSchemesForApplication(info, name); } else if (verbose > 0) {