Files
textmate/Frameworks/OakAppKit/src/OakOpenWithMenu.mm
Allan Odgaard 9894969e67 Initial commit
2012-08-09 16:25:56 +02:00

131 lines
4.3 KiB
Plaintext

#import "OakOpenWithMenu.h"
#import "NSMenuItem Additions.h"
#import "NSMenu Additions.h"
#import <OakFoundation/NSString Additions.h>
#import <oak/oak.h>
#import <oak/CocoaSTL.h>
#import <text/ctype.h>
#import <io/path.h>
static std::vector< std::pair<NSString*, NSURL*> > ApplicationURLsForPaths (NSSet* paths)
{
std::vector< std::pair<NSString*, NSURL*> > res;
NSMutableSet* defaultApplicationURLs = [NSMutableSet set];
NSMutableSet* allApplicationURLs = [NSMutableSet set];
for(NSString* path in paths)
{
NSURL* fileURL = [NSURL fileURLWithPath:path];
NSArray* applicationURLs = [(NSArray*)LSCopyApplicationURLsForURL((CFURLRef)fileURL, kLSRolesAll) autorelease];
NSURL* defaultApplicationURL = nil;
if(noErr == LSGetApplicationForURL((CFURLRef)fileURL, kLSRolesAll, NULL, (CFURLRef*)&defaultApplicationURL))
{
if(![applicationURLs containsObject:defaultApplicationURL])
applicationURLs = [applicationURLs arrayByAddingObject:defaultApplicationURL];
[defaultApplicationURLs addObject:defaultApplicationURL];
}
if(allApplicationURLs.count == 0)
[allApplicationURLs setSet:[NSSet setWithArray:applicationURLs]];
else [allApplicationURLs intersectSet:[NSSet setWithArray:applicationURLs]];
}
if(allApplicationURLs.count == 0)
return res;
std::map<std::string, std::map<std::string, NSURL*>, text::less_t> apps;
for(NSURL* appURL in allApplicationURLs)
{
std::string const& appName = path::display_name(appURL.path.UTF8String);
NSBundle* appBundle = [NSBundle bundleWithPath:appURL.path];
NSString* appVersion = [appBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"] ?: ([appBundle objectForInfoDictionaryKey:@"CFBundleVersion"] ?: @"???");
apps[appName][appVersion.UTF8String] = appURL;
}
NSURL* defaultApplicationURL = defaultApplicationURLs.count == 1 ? defaultApplicationURLs.anyObject : nil;
iterate(appIter, apps)
{
riterate(versIter, appIter->second)
{
NSString* appName = [NSString stringWithCxxString:appIter->first];
NSURL* appURL = versIter->second;
if([defaultApplicationURL isEqual:appURL])
appName = [NSString stringWithFormat:@"%@ (default)", appName];
if(appIter->second.size() > 1) // we have more than one version
appName = [NSString stringWithFormat:@"%@ (%@)", appName, [NSString stringWithCxxString:versIter->first]];
res.insert(([defaultApplicationURL isEqual:appURL] ? res.begin() : res.end()), std::make_pair(appName, appURL));
}
}
return res;
}
static OakOpenWithMenu* SharedInstance;
@implementation OakOpenWithMenu
+ (id)sharedInstance
{
return SharedInstance ?: [[self new] autorelease];
}
- (id)init
{
if(SharedInstance)
[self release];
else self = SharedInstance = [[super init] retain];
return SharedInstance;
}
+ (void)addOpenWithMenuForPaths:(NSSet*)paths toMenuItem:(NSMenuItem*)item
{
NSMenu* submenu = [[NSMenu new] autorelease];
[submenu setAutoenablesItems:NO];
[submenu setDelegate:[OakOpenWithMenu sharedInstance]];
[item setRepresentedObject:paths];
[item setSubmenu:submenu];
}
- (void)menuNeedsUpdate:(NSMenu*)menu
{
NSMenuItem* superItem = [menu parentMenuItem];
if(menu.numberOfItems > 0 || !superItem)
return;
std::vector< std::pair<NSString*, NSURL*> > appURLs = ApplicationURLsForPaths(superItem.representedObject);
if(appURLs.empty())
{
[[menu addItemWithTitle:@"No Suitable Applications Found" action:@selector(dummy:) keyEquivalent:@""] setEnabled:NO];
return;
}
iterate(app, appURLs)
{
NSMenuItem* menuItem = [menu addItemWithTitle:app->first action:@selector(openWith:) keyEquivalent:@""];
[menuItem setIconForFile:[app->second path]];
[menuItem setTarget:self];
[menuItem setRepresentedObject:app->second];
}
if(appURLs.size() > 1)
[menu insertItem:[NSMenuItem separatorItem] atIndex:1];
}
- (void)openWith:(id)sender
{
NSURL* applicationURL = [sender representedObject];
NSSet* filePaths = [[[sender menu] parentMenuItem] representedObject];
NSMutableArray* fileURLs = [NSMutableArray arrayWithCapacity:filePaths.count];
for(NSString* filePath in filePaths)
[fileURLs addObject:[NSURL fileURLWithPath:filePath]];
[[NSWorkspace sharedWorkspace] openURLs:fileURLs
withAppBundleIdentifier:[[NSBundle bundleWithPath:applicationURL.path] bundleIdentifier]
options:0
additionalEventParamDescriptor:NULL
launchIdentifiers:NULL];
}
@end