new open with menu for os x

This commit is contained in:
John Preston 2015-01-21 13:20:23 +03:00
parent 13887d91d9
commit 471d83fb97
11 changed files with 254 additions and 7 deletions

View File

@ -491,7 +491,10 @@ void VideoSaveLink::doSave(bool forceSavingAs) const {
QString already = data->already(true);
if (!already.isEmpty() && !forceSavingAs) {
psOpenFile(already, true);
QPoint pos(QCursor::pos());
if (!psShowOpenWithMenu(pos.x(), pos.y(), already)) {
psOpenFile(already, true);
}
} else {
QDir alreadyDir(already.isEmpty() ? QDir() : QFileInfo(already).dir());
QString name = already.isEmpty() ? QString(".mov") : already;
@ -577,7 +580,10 @@ void AudioSaveLink::doSave(bool forceSavingAs) const {
QString already = data->already(true);
if (!already.isEmpty() && !forceSavingAs) {
psOpenFile(already, true);
QPoint pos(QCursor::pos());
if (!psShowOpenWithMenu(pos.x(), pos.y(), already)) {
psOpenFile(already, true);
}
} else {
QDir alreadyDir(already.isEmpty() ? QDir() : QFileInfo(already).dir());
QString name = already.isEmpty() ? QString(".ogg") : already;
@ -678,7 +684,10 @@ void DocumentSaveLink::doSave(bool forceSavingAs) const {
QString already = data->already(true);
if (!already.isEmpty() && !forceSavingAs) {
psOpenFile(already, true);
QPoint pos(QCursor::pos());
if (!psShowOpenWithMenu(pos.x(), pos.y(), already)) {
psOpenFile(already, true);
}
} else {
QDir alreadyDir(already.isEmpty() ? QDir() : QFileInfo(already).dir());
QString name = already.isEmpty() ? data->name : already, filter;

View File

@ -1050,7 +1050,12 @@ void MainWidget::videoLoadProgress(mtpFileLoader *loader) {
video->finish();
QString already = video->already();
if (!already.isEmpty() && video->openOnSave) {
psOpenFile(already, video->openOnSave < 0);
QPoint pos(QCursor::pos());
if (video->openOnSave < 0 && !psShowOpenWithMenu(pos.x(), pos.y(), already)) {
psOpenFile(already, true);
} else {
psOpenFile(already, video->openOnSave < 0);
}
}
}
}
@ -1105,7 +1110,12 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) {
audioVoice()->play(audio);
}
} else {
psOpenFile(already, audio->openOnSave < 0);
QPoint pos(QCursor::pos());
if (audio->openOnSave < 0 && !psShowOpenWithMenu(pos.x(), pos.y(), already)) {
psOpenFile(already, true);
} else {
psOpenFile(already, audio->openOnSave < 0);
}
}
}
}
@ -1164,7 +1174,12 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) {
psOpenFile(already);
}
} else {
psOpenFile(already, document->openOnSave < 0);
QPoint pos(QCursor::pos());
if (document->openOnSave < 0 && !psShowOpenWithMenu(pos.x(), pos.y(), already)) {
psOpenFile(already, true);
} else {
psOpenFile(already, document->openOnSave < 0);
}
}
}
}

View File

@ -1654,6 +1654,10 @@ void psExecTelegram() {
_execUpdater(false);
}
bool psShowOpenWithMenu(int x, int y, const QString &file) {
return false;
}
void psAutoStart(bool start, bool silent) {
}

View File

@ -197,6 +197,8 @@ bool psCheckReadyUpdate();
void psExecUpdater();
void psExecTelegram();
bool psShowOpenWithMenu(int x, int y, const QString &file);
void psPostprocessFile(const QString &name);
void psOpenFile(const QString &name, bool openWith = false);
void psShowInFolder(const QString &name);

View File

@ -1110,6 +1110,10 @@ bool psCheckReadyUpdate() {
return true;
}
bool psShowOpenWithMenu(int x, int y, const QString &file) {
return objc_showOpenWithMenu(x, y, file);
}
void psPostprocessFile(const QString &name) {
}

View File

@ -221,6 +221,8 @@ bool psCheckReadyUpdate();
void psExecUpdater();
void psExecTelegram();
bool psShowOpenWithMenu(int x, int y, const QString &file);
void psPostprocessFile(const QString &name);
void psOpenFile(const QString &name, bool openWith = false);
void psShowInFolder(const QString &name);

View File

@ -59,6 +59,8 @@ void objc_debugShowAlert(const QString &str);
void objc_outputDebugString(const QString &str);
int64 objc_idleTime();
bool objc_showOpenWithMenu(int x, int y, const QString &file);
void objc_showInFinder(const QString &file, const QString &path);
void objc_openFile(const QString &file, bool openwith);
void objc_start();

View File

@ -369,6 +369,209 @@ int64 objc_idleTime() { // taken from https://github.com/trueinteractions/tint/i
return (result == err) ? -1 : int64(result);
}
@interface OpenWithApp : NSObject {
NSString *fullname;
NSURL *app;
NSImage *icon;
}
@property (nonatomic, retain) NSString *fullname;
@property (nonatomic, retain) NSURL *app;
@property (nonatomic, retain) NSImage *icon;
@end
@implementation OpenWithApp
@synthesize fullname, app, icon;
- (void) dealloc {
[fullname release];
[app release];
[icon release];
[super dealloc];
}
@end
@interface OpenFileWithInterface : NSObject {
}
- (id) init:(NSString *)file;
- (BOOL) popupAtX:(int)x andY:(int)y;
- (void) itemChosen:(id)sender;
- (void) dealloc;
@end
@implementation OpenFileWithInterface {
NSString *toOpen;
NSURL *defUrl;
NSString *defBundle, *defName, *defVersion;
NSImage *defIcon;
NSMutableArray *apps;
NSMenu *menu;
}
- (void) fillAppByUrl:(NSURL*)url bundle:(NSString**)bundle name:(NSString**)name version:(NSString**)version icon:(NSImage**)icon {
NSBundle *b = [NSBundle bundleWithURL:url];
if (b) {
NSString *path = [url path];
*name = [[NSFileManager defaultManager] displayNameAtPath: path];
if (!*name) *name = (NSString*)[b objectForInfoDictionaryKey:@"CFBundleDisplayName"];
if (!*name) *name = (NSString*)[b objectForInfoDictionaryKey:@"CFBundleName"];
if (*name) {
*bundle = [b bundleIdentifier];
if (bundle) {
*version = (NSString*)[b objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
*icon = [[NSWorkspace sharedWorkspace] iconForFile: path];
if (*icon && [*icon isValid]) [*icon setSize: CGSizeMake(16., 16.)];
return;
}
}
}
*bundle = *name = *version = nil;
*icon = nil;
}
- (id) init:(NSString*)file {
toOpen = file;
if (self = [super init]) {
NSURL *url = [NSURL fileURLWithPath:file];
defUrl = [[NSWorkspace sharedWorkspace] URLForApplicationToOpenURL:url];
if (defUrl) {
[self fillAppByUrl:defUrl bundle:&defBundle name:&defName version:&defVersion icon:&defIcon];
if (!defBundle || !defName) {
defUrl = nil;
}
}
NSArray *appsList = (NSArray*)LSCopyApplicationURLsForURL(CFURLRef(url), kLSRolesAll);
NSMutableDictionary *data = [NSMutableDictionary dictionaryWithCapacity:16];
int fullcount = 0;
for (id app in appsList) {
if (fullcount > 15) break;
NSString *bundle = nil, *name = nil, *version = nil;
NSImage *icon = nil;
[self fillAppByUrl:(NSURL*)app bundle:&bundle name:&name version:&version icon:&icon];
if (bundle && name) {
if ([bundle isEqualToString:defBundle] && [version isEqualToString:defVersion]) continue;
NSString *key = [[NSArray arrayWithObjects:bundle, name, nil] componentsJoinedByString:@"|"];
if (!version) version = @"";
NSMutableDictionary *versions = (NSMutableDictionary*)[data objectForKey:key];
if (!versions) {
versions = [NSMutableDictionary dictionaryWithCapacity:2];
[data setValue:versions forKey:key];
}
if (![versions objectForKey:version]) {
[versions setValue:[NSArray arrayWithObjects:name, icon, app, nil] forKey:version];
++fullcount;
}
}
}
if (fullcount || defUrl) {
apps = [NSMutableArray arrayWithCapacity:fullcount];
for (id key in data) {
NSMutableDictionary *val = (NSMutableDictionary*)[data objectForKey:key];
for (id ver in val) {
NSArray *app = (NSArray*)[val objectForKey:ver];
OpenWithApp *a = [[OpenWithApp alloc] init];
NSString *fullname = (NSString*)[app objectAtIndex:0], *version = (NSString*)ver;
BOOL showVersion = ([val count] > 1);
if (!showVersion) {
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^\\d+\\.\\d+\\.\\d+(\\.\\d+)?$" options:NSRegularExpressionCaseInsensitive error:&error];
showVersion = ![regex numberOfMatchesInString:version options:NSMatchingWithoutAnchoringBounds range:{0,[version length]}];
}
if (showVersion) fullname = [[NSArray arrayWithObjects:fullname, @" (", version, @")", nil] componentsJoinedByString:@""];
[a setFullname:fullname];
[a setIcon:(NSImage*)[app objectAtIndex:1]];
[a setApp:(NSURL*)[app objectAtIndex:2]];
[apps addObject:a];
[a release];
}
}
}
[apps sortUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"fullname" ascending:YES]]];
[appsList release];
menu = nil;
}
return self;
}
- (BOOL) popupAtX:(int)x andY:(int)y {
if (![apps count] && !defName) return NO;
menu = [[NSMenu alloc] initWithTitle:@"Open With"];
int index = 0;
if (defName) {
NSMenuItem *item = [menu insertItemWithTitle:[[NSArray arrayWithObjects:defName, @" (default)", nil] componentsJoinedByString:@""] action:@selector(itemChosen:) keyEquivalent:@"" atIndex:index++];
if (defIcon) [item setImage:defIcon];
[item setTarget:self];
[menu insertItem:[NSMenuItem separatorItem] atIndex:index++];
}
if ([apps count]) {
for (id a in apps) {
OpenWithApp *app = (OpenWithApp*)a;
NSMenuItem *item = [menu insertItemWithTitle:[a fullname] action:@selector(itemChosen:) keyEquivalent:@"" atIndex:index++];
if ([app icon]) [item setImage:[app icon]];
[item setTarget:self];
}
[menu insertItem:[NSMenuItem separatorItem] atIndex:index++];
}
NSMenuItem *item = [menu insertItemWithTitle:@"Other..." action:@selector(itemChosen:) keyEquivalent:@"" atIndex:index++];
[item setTarget:self];
[menu popUpMenuPositioningItem:nil atLocation:CGPointMake(x, y) inView:nil];
return YES;
}
- (void) itemChosen:(id)sender {
NSArray *items = [menu itemArray];
NSURL *url = nil;
for (int i = 0, l = [items count]; i < l; ++i) {
if ([items objectAtIndex:i] == sender) {
if (defName) i -= 2;
if (i < 0) {
url = defUrl;
} else if (i < int([apps count])) {
url = [(OpenWithApp*)[apps objectAtIndex:i] app];
}
break;
}
}
if (url) {
[[NSWorkspace sharedWorkspace] openFile:toOpen withApplication:[url path]];
} else {
objc_openFile(objcString(toOpen), true);
}
}
- (void) dealloc {
if (apps) [apps release];
[super dealloc];
if (menu) [menu release];
}
@end
bool objc_showOpenWithMenu(int x, int y, const QString &f) {
NSString *file = QNSString(f).s();
@try {
OpenFileWithInterface *menu = [[OpenFileWithInterface alloc] init:file];
QRect r = QApplication::desktop()->screenGeometry(QPoint(x, y));
y = r.y() + r.height() - y;
return !![menu popupAtX:x andY:y];
}
@catch (NSException *exception) {
}
@finally {
}
return false;
}
void objc_showInFinder(const QString &file, const QString &path) {
[[NSWorkspace sharedWorkspace] selectFile:QNSString(file).s() inFileViewerRootedAtPath:QNSString(path).s()];
}

View File

@ -2295,6 +2295,10 @@ void psExecTelegram() {
}
}
bool psShowOpenWithMenu(int x, int y, const QString &file) {
return false;
}
void _manageAppLnk(bool create, bool silent, int path_csidl, const wchar_t *args, const wchar_t *description) {
WCHAR startupFolder[MAX_PATH];
HRESULT hres = SHGetFolderPath(0, path_csidl, 0, SHGFP_TYPE_CURRENT, startupFolder);

View File

@ -199,6 +199,8 @@ bool psCheckReadyUpdate();
void psExecUpdater();
void psExecTelegram();
bool psShowOpenWithMenu(int x, int y, const QString &file);
void psPostprocessFile(const QString &name);
void psOpenFile(const QString &name, bool openWith = false);
void psShowInFolder(const QString &name);

View File

@ -1813,7 +1813,7 @@
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.7.8;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEBUG_INFORMATION_FORMAT = dwarf;
DYLIB_COMPATIBILITY_VERSION = 0.7;
DYLIB_CURRENT_VERSION = 0.7.8;
ENABLE_STRICT_OBJC_MSGSEND = YES;