diff options
author | Markus Goetz <markus@woboq.com> | 2018-04-11 14:40:27 +0300 |
---|---|---|
committer | Markus Goetz <markus@woboq.com> | 2018-04-12 14:12:42 +0300 |
commit | aceff1f8632d159922d49dbcaa1aaf930a4a2fed (patch) | |
tree | acf7cc789a48a34e450237367a31622578112f90 /shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt | |
parent | 113f56fdc5a895cb0893da89904d77374e1a7762 (diff) |
macOS: Implement new dynamic Finder menu items #6328
Diffstat (limited to 'shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt')
4 files changed, 85 insertions, 59 deletions
diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.h b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.h index 67a436094..803c5270f 100644 --- a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.h +++ b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.h @@ -23,6 +23,7 @@ NSMutableSet *_registeredDirectories; NSString *_shareMenuTitle; NSMutableDictionary *_strings; + NSMutableArray *_menuItems; } @end diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m index 2e917d132..a0f791882 100644 --- a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m +++ b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m @@ -21,7 +21,7 @@ - (instancetype)init { self = [super init]; - + FIFinderSyncController *syncController = [FIFinderSyncController defaultController]; NSBundle *extBundle = [NSBundle bundleForClass:[self class]]; // This was added to the bundle's Info.plist to get it from the build system @@ -43,7 +43,7 @@ [syncController setBadgeImage:sync label:@"Synchronizing" forBadgeIdentifier:@"NEW+SWM"]; [syncController setBadgeImage:warning label:@"Ignored" forBadgeIdentifier:@"IGNORE+SWM"]; [syncController setBadgeImage:error label:@"Error" forBadgeIdentifier:@"ERROR+SWM"]; - + // The Mach port name needs to: // - Be prefixed with the code signing Team ID // - Then infixed with the sandbox App Group @@ -55,12 +55,12 @@ // the sandboxed App Extension needs. // https://developer.apple.com/library/mac/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24 NSString *serverName = [socketApiPrefix stringByAppendingString:@".socketApi"]; - // NSLog(@"FinderSync serverName %@", serverName); + //NSLog(@"FinderSync serverName %@", serverName); _syncClientProxy = [[SyncClientProxy alloc] initWithDelegate:self serverName:serverName]; _registeredDirectories = [[NSMutableSet alloc] init]; _strings = [[NSMutableDictionary alloc] init]; - + [_syncClientProxy start]; return self; } @@ -74,13 +74,27 @@ NSLog(@"ERROR: Could not determine file type of %@", [url path]); isDir = NO; } - + NSString* normalizedPath = [[url path] decomposedStringWithCanonicalMapping]; [_syncClientProxy askForIcon:normalizedPath isDirectory:isDir]; } #pragma mark - Menu and toolbar item support +- (NSString*) selectedPathsSeparatedByRecordSeparator +{ + FIFinderSyncController *syncController = [FIFinderSyncController defaultController]; + NSMutableString *string = [[NSMutableString alloc] init]; + [syncController.selectedItemURLs enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { + if (string.length > 0) { + [string appendString:@"\x1e"]; // record separator + } + NSString* normalizedPath = [[obj path] decomposedStringWithCanonicalMapping]; + [string appendString:normalizedPath]; + }]; + return string; +} + - (NSMenu *)menuForMenuKind:(FIMenuKind)whichMenu { FIFinderSyncController *syncController = [FIFinderSyncController defaultController]; @@ -101,54 +115,43 @@ } }]; + NSString *paths = [self selectedPathsSeparatedByRecordSeparator]; + // calling this IPC calls us back from client with several MENU_ITEM entries and then our askOnSocket returns again + [_syncClientProxy askOnSocket:paths query:@"GET_MENU_ITEMS"]; + id contextMenuTitle = [_strings objectForKey:@"CONTEXT_MENU_TITLE"]; - id shareTitle = [_strings objectForKey:@"SHARE_MENU_TITLE"]; - id copyLinkTitle = [_strings objectForKey:@"COPY_PRIVATE_LINK_MENU_TITLE"]; - id emailLinkTitle = [_strings objectForKey:@"EMAIL_PRIVATE_LINK_MENU_TITLE"]; if (contextMenuTitle && !onlyRootsSelected) { - NSMenu *menu = [[NSMenu alloc] initWithTitle:@""]; - NSMenu *subMenu = [[NSMenu alloc] initWithTitle:@""]; - NSMenuItem *subMenuItem = [menu addItemWithTitle:contextMenuTitle action:nil keyEquivalent:@""]; - subMenuItem.submenu = subMenu; - subMenuItem.image = [[NSBundle mainBundle] imageForResource:@"app.icns"]; - - [subMenu addItemWithTitle:shareTitle action:@selector(shareMenuAction:) keyEquivalent:@""]; - [subMenu addItemWithTitle:copyLinkTitle action:@selector(copyLinkMenuAction:) keyEquivalent:@""]; - [subMenu addItemWithTitle:emailLinkTitle action:@selector(emailLinkMenuAction:) keyEquivalent:@""]; - + NSMenu *menu = [[NSMenu alloc] initWithTitle:@""]; + NSMenu *subMenu = [[NSMenu alloc] initWithTitle:@""]; + NSMenuItem *subMenuItem = [menu addItemWithTitle:contextMenuTitle action:nil keyEquivalent:@""]; + subMenuItem.submenu = subMenu; + subMenuItem.image = [[NSBundle mainBundle] imageForResource:@"app.icns"]; + + // There is an annoying bug in macOS (at least 10.13.3), it does not use/copy over the representedObject of a menu item + // So we have to use tag instead. + int idx = 0; + for (NSArray* item in _menuItems) { + NSMenuItem *actionItem = [subMenu addItemWithTitle:[item valueForKey:@"text"] + action:@selector(subMenuActionClicked:) + keyEquivalent:@""]; + [actionItem setTag:idx]; + [actionItem setTarget:self]; + NSString *flags = [item valueForKey:@"flags"]; // e.g. "d" + if ([flags rangeOfString:@"d"].location != NSNotFound) { + [actionItem setEnabled:false]; + } + idx++; + } return menu; } return nil; } -- (IBAction)shareMenuAction:(id)sender -{ - NSArray* items = [[FIFinderSyncController defaultController] selectedItemURLs]; - - [items enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { - NSString* normalizedPath = [[obj path] decomposedStringWithCanonicalMapping]; - [_syncClientProxy askOnSocket:normalizedPath query:@"SHARE"]; - }]; -} - -- (IBAction)copyLinkMenuAction:(id)sender -{ - NSArray* items = [[FIFinderSyncController defaultController] selectedItemURLs]; - - [items enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { - NSString* normalizedPath = [[obj path] decomposedStringWithCanonicalMapping]; - [_syncClientProxy askOnSocket:normalizedPath query:@"COPY_PRIVATE_LINK"]; - }]; -} - -- (IBAction)emailLinkMenuAction:(id)sender -{ - NSArray* items = [[FIFinderSyncController defaultController] selectedItemURLs]; - - [items enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { - NSString* normalizedPath = [[obj path] decomposedStringWithCanonicalMapping]; - [_syncClientProxy askOnSocket:normalizedPath query:@"EMAIL_PRIVATE_LINK"]; - }]; +- (void)subMenuActionClicked:(id)sender { + long idx = [(NSMenuItem*)sender tag]; + NSString *command = [[_menuItems objectAtIndex:idx] valueForKey:@"command"]; + NSString *paths = [self selectedPathsSeparatedByRecordSeparator]; + [_syncClientProxy askOnSocket:paths query:command]; } #pragma mark - SyncClientProxyDelegate implementation @@ -181,6 +184,14 @@ [_strings setObject:value forKey:key]; } +- (void)resetMenuItems +{ + _menuItems = [[NSMutableArray alloc] init]; +} +- (void)addMenuItem:(NSDictionary *)item { + [_menuItems addObject:item]; +} + - (void)connectionDidDie { [_strings removeAllObjects]; diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.h b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.h index 8f9633416..1d0fd74b8 100644 --- a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.h +++ b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.h @@ -21,6 +21,8 @@ - (void)registerPath:(NSString*)path; - (void)unregisterPath:(NSString*)path; - (void)setString:(NSString*)key value:(NSString*)value; +- (void)resetMenuItems; +- (void)addMenuItem:(NSDictionary *)item; - (void)connectionDidDie; @end diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.m b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.m index b7b64571f..656f77003 100644 --- a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.m +++ b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.m @@ -27,7 +27,7 @@ - (instancetype)initWithDelegate:(id)arg1 serverName:(NSString*)serverName { self = [super init]; - + self.delegate = arg1; _serverName = serverName; _remoteEnd = nil; @@ -41,20 +41,20 @@ { if (_remoteEnd) return; - + // Lookup the server connection NSConnection *conn = [NSConnection connectionWithRegisteredName:_serverName host:nil]; - + if (!conn) { // Could not connect to the sync client [self scheduleRetry]; return; } - + [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(connectionDidDie:) - name:NSConnectionDidDieNotification - object:conn]; + selector:@selector(connectionDidDie:) + name:NSConnectionDidDieNotification + object:conn]; NSDistantObject <ServerProtocol> *server = (NSDistantObject <ServerProtocol> *)[conn rootProxy]; assert(server); @@ -71,7 +71,7 @@ // The server replied with the distant object that we will use for tx _remoteEnd = (NSDistantObject <ChannelProtocol> *)tx; [_remoteEnd setProtocolForProxy:@protocol(ChannelProtocol)]; - + // Everything is set up, start querying [self askOnSocket:@"" query:@"GET_STRINGS"]; } @@ -83,7 +83,7 @@ - (void)connectionDidDie:(NSNotification*)notification { -#pragma unused(notification) +#pragma unused(notification) _remoteEnd = nil; [_delegate connectionDidDie]; @@ -95,11 +95,11 @@ - (void)sendMessage:(NSData*)msg { NSString *answer = [[NSString alloc] initWithData:msg encoding:NSUTF8StringEncoding]; - - // Cut the trailing newline + + // Cut the trailing newline. We always only receive one line from the client. answer = [answer substringToIndex:[answer length] - 1]; NSArray *chunks = [answer componentsSeparatedByString: @":"]; - + if( [[chunks objectAtIndex:0] isEqualToString:@"STATUS"] ) { NSString *result = [chunks objectAtIndex:1]; NSString *path = [chunks objectAtIndex:2]; @@ -123,6 +123,18 @@ // BEGIN and END messages, do nothing. } else if( [[chunks objectAtIndex:0 ] isEqualToString:@"STRING"] ) { [_delegate setString:[chunks objectAtIndex:1] value:[chunks objectAtIndex:2]]; + } else if( [[chunks objectAtIndex:0 ] isEqualToString:@"GET_MENU_ITEMS"] ) { + if ([[chunks objectAtIndex:1] isEqualToString:@"BEGIN"]) { + [_delegate resetMenuItems]; + } else if ([[chunks objectAtIndex:1] isEqualToString:@"END"]) { + // Don't do anything special, the askOnSocket call in FinderSync menuForMenuKind will return after this line + } + } else if( [[chunks objectAtIndex:0 ] isEqualToString:@"MENU_ITEM"] ) { + NSMutableDictionary *item = [[NSMutableDictionary alloc] init]; + [item setValue:[chunks objectAtIndex:1] forKey:@"command"]; // e.g. "COPY_PRIVATE_LINK" + [item setValue:[chunks objectAtIndex:2] forKey:@"flags"]; // e.g. "d" + [item setValue:[chunks objectAtIndex:3] forKey:@"text"]; // e.g. "Copy private link to clipboard" + [_delegate addMenuItem:item]; } else { NSLog(@"SyncState: Unknown command %@", [chunks objectAtIndex:0]); } @@ -131,7 +143,7 @@ - (void)askOnSocket:(NSString*)path query:(NSString*)verb { NSString *query = [NSString stringWithFormat:@"%@:%@\n", verb,path]; - + @try { [_remoteEnd sendMessage:[query dataUsingEncoding:NSUTF8StringEncoding]]; } @catch(NSException* e) { |