Add methods to manage the user's preferred applications for URL

schemes to NSWorkspace.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@31523 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Wolfgang Lux 2010-10-18 09:04:18 +00:00
parent eb812a70c0
commit 782128949e
3 changed files with 284 additions and 9 deletions

View file

@ -1,3 +1,13 @@
2010-10-18 Wolfgang Lux <wolfgang.lux@gmail.com>
* Headers/AppKit/NSWorkspace.h:
* Source/NSWorkspace.m (urlPrefsPath, urlPreferences,
+initialize, -openURL:, -findApplications, -infoForScheme:,
-getBestAppInRole:forScheme:, -setBestApp:inRole:forScheme:,
-_scheme:role:app:):
Add methods to manage the user's preferred applications for URL
schemes to NSWorkspace.
2010-10-17 Fred Kiefer <FredKiefer@gmx.de>
* Source/NSMenuView.m: Revert the WinUX theme specific patch Doug

View file

@ -221,6 +221,12 @@ typedef void NSAppleEventDescriptor;
inRole: (NSString*)role
forExtension: (NSString*)ext;
- (void) setBestIcon: (NSString*)iconPath forExtension: (NSString*)ext;
- (NSDictionary*) infoForScheme: (NSString*)scheme;
- (NSString*) getBestAppInRole: (NSString*)role
forScheme: (NSString*)scheme;
- (void) setBestApp: (NSString*)appName
inRole: (NSString*)role
forScheme: (NSString*)scheme;
@end
#endif

View file

@ -400,6 +400,9 @@ static id GSLaunched(NSNotification *notification, BOOL active)
- (BOOL) _extension: (NSString*)ext
role: (NSString*)role
app: (NSString**)app;
- (BOOL) _scheme: (NSString*)scheme
role: (NSString*)role
app: (NSString**)app;
// application communication
- (BOOL) _launchApplication: (NSString*)appName
@ -512,6 +515,9 @@ static NSDictionary *applications = nil;
static NSString *extPrefPath = nil;
static NSDictionary *extPreferences = nil;
static NSString *urlPrefPath = nil;
static NSDictionary *urlPreferences = nil;
// FIXME: Won't work for MINGW32
static NSString *_rootPath = @"/";
@ -562,6 +568,23 @@ static NSString *_rootPath = @"/";
}
}
/*
* Load URL scheme preferences.
*/
urlPrefPath = [service
stringByAppendingPathComponent: @".GNUstepURLPrefs"];
RETAIN(urlPrefPath);
if ([mgr isReadableFileAtPath: urlPrefPath] == YES)
{
data = [NSData dataWithContentsOfFile: urlPrefPath];
if (data)
{
dict = [NSDeserializer deserializePropertyListFromData: data
mutableContainers: NO];
urlPreferences = RETAIN(dict);
}
}
/*
* Load cached application information.
*/
@ -811,18 +834,10 @@ static NSString *_rootPath = @"/";
}
else
{
NSDictionary *map;
NSString *appName;
NSPasteboard *pb;
/* Look up an application to handle this URL scheme.
* We get a dictionary containing all apps for the scheme.
* FIXME ... we just use the last app in the dictionary,
* but we really ought to have some way of saying which we want.
*/
map = [applications objectForKey: @"GSSchemesMap"];
map = [map objectForKey: [[url scheme] lowercaseString]];
appName = [[map allKeys] lastObject];
appName = [self getBestAppInRole: nil forScheme: [url scheme]];
if (appName != nil)
{
id app;
@ -1341,6 +1356,17 @@ inFileViewerRootedAtPath: (NSString*)rootFullpath
}
}
if ([mgr isReadableFileAtPath: urlPrefPath] == YES)
{
data = [NSData dataWithContentsOfFile: urlPrefPath];
if (data)
{
dict = [NSDeserializer deserializePropertyListFromData: data
mutableContainers: NO];
ASSIGN(urlPreferences, dict);
}
}
if ([mgr isReadableFileAtPath: appListPath] == YES)
{
data = [NSData dataWithContentsOfFile: appListPath];
@ -2140,6 +2166,94 @@ inFileViewerRootedAtPath: (NSString*)rootFullpath
[data writeToFile: extPrefPath atomically: YES];
}
/**
* Gets the applications cache (generated by the make_services tool)
* and looks up the special entry that contains a dictionary of all
* URL schemes recognised by GNUstep applications. Then finds the
* dictionary of applications that can handle our scheme and returns
* it.
*/
- (NSDictionary*) infoForScheme: (NSString*)scheme
{
NSDictionary *map;
scheme = [scheme lowercaseString];
map = [applications objectForKey: @"GSSchemesMap"];
return [map objectForKey: scheme];
}
/**
* Returns the 'best' application to open a file with the specified URL
* scheme using the given role. If the role is nil then apps which can
* edit are preferred but viewers are also acceptable. Uses a user preferred
* app or picks any good match.
*/
- (NSString*) getBestAppInRole: (NSString*)role
forScheme: (NSString*)scheme
{
NSString *appName = nil;
if ([self _scheme: scheme role: role app: &appName] == NO)
{
appName = nil;
}
return appName;
}
/**
* Sets up a user preference for which app should be used to open files
* of the specified URL scheme
*/
- (void) setBestApp: (NSString*)appName
inRole: (NSString*)role
forScheme: (NSString*)scheme
{
NSMutableDictionary *map;
NSMutableDictionary *inf;
NSData *data;
scheme = [scheme lowercaseString];
if (urlPreferences != nil)
map = [urlPreferences mutableCopy];
else
map = [NSMutableDictionary new];
inf = [[map objectForKey: scheme] mutableCopy];
if (inf == nil)
{
inf = [NSMutableDictionary new];
}
if (appName == nil)
{
if (role == nil)
{
NSString *iconPath = [inf objectForKey: @"Icon"];
RETAIN(iconPath);
[inf removeAllObjects];
if (iconPath)
{
[inf setObject: iconPath forKey: @"Icon"];
RELEASE(iconPath);
}
}
else
{
[inf removeObjectForKey: role];
}
}
else
{
[inf setObject: appName forKey: (role ? (id)role : (id)@"Editor")];
}
[map setObject: inf forKey: scheme];
RELEASE(inf);
RELEASE(urlPreferences);
urlPreferences = map;
data = [NSSerializer serializePropertyList: urlPreferences];
[data writeToFile: urlPrefPath atomically: YES];
}
@end
@implementation NSWorkspace (Private)
@ -2507,6 +2621,151 @@ inFileViewerRootedAtPath: (NSString*)rootFullpath
}
}
- (BOOL) _scheme: (NSString*)scheme
role: (NSString*)role
app: (NSString**)app
{
NSEnumerator *enumerator;
NSString *appName = nil;
NSDictionary *apps = [self infoForScheme: scheme];
NSDictionary *prefs;
NSDictionary *info;
scheme = [scheme lowercaseString];
/*
* Look for the name of the preferred app in this role.
* A 'nil' roll is a wildcard - find the preferred Editor or Viewer.
*/
prefs = [urlPreferences objectForKey: scheme];
if (role == nil || [role isEqualToString: @"Editor"])
{
appName = [prefs objectForKey: @"Editor"];
if (appName != nil)
{
info = [apps objectForKey: appName];
if (info != nil)
{
if (app != 0)
{
*app = appName;
}
return YES;
}
else if ([self locateApplicationBinary: appName] != nil)
{
/*
* Return the preferred application even though it doesn't
* say it opens this type of file ... preferences overrule.
*/
if (app != 0)
{
*app = appName;
}
return YES;
}
}
}
if (role == nil || [role isEqualToString: @"Viewer"])
{
appName = [prefs objectForKey: @"Viewer"];
if (appName != nil)
{
info = [apps objectForKey: appName];
if (info != nil)
{
if (app != 0)
{
*app = appName;
}
return YES;
}
else if ([self locateApplicationBinary: appName] != nil)
{
/*
* Return the preferred application even though it doesn't
* say it opens this type of file ... preferences overrule.
*/
if (app != 0)
{
*app = appName;
}
return YES;
}
}
}
/*
* Go through the dictionary of apps that know about this file type and
* determine the best application to open the file by examining the
* type information for each app.
* The 'NSRole' field specifies what the app can do with the file - if it
* is missing, we assume an 'Editor' role.
*/
if (apps == nil || [apps count] == 0)
{
return NO;
}
enumerator = [apps keyEnumerator];
if (role == nil)
{
BOOL found = NO;
/*
* If the requested role is 'nil', we can accept an app that is either
* an Editor (preferred) or a Viewer, or unknown.
*/
while ((appName = [enumerator nextObject]) != nil)
{
NSString *str;
info = [apps objectForKey: appName];
str = [info objectForKey: @"NSRole"];
/* NB. If str is nil or an empty string, there is no role set,
* and we treat this as an Editor since the role is unrestricted.
*/
if ([str length] == 0 || [str isEqualToString: @"Editor"])
{
if (app != 0)
{
*app = appName;
}
return YES;
}
if ([str isEqualToString: @"Viewer"])
{
if (app != 0)
{
*app = appName;
}
found = YES;
}
}
return found;
}
else
{
while ((appName = [enumerator nextObject]) != nil)
{
NSString *str;
info = [apps objectForKey: appName];
str = [info objectForKey: @"NSRole"];
if ((str == nil && [role isEqualToString: @"Editor"])
|| [str isEqualToString: role])
{
if (app != 0)
{
*app = appName;
}
return YES;
}
}
return NO;
}
}
/**
* Launch an application locally (ie without reference to the workspace
* manager application). We should only call this method when we want