mirror of
https://github.com/chylex/Nextcloud-Desktop.git
synced 2025-05-14 02:34:09 +02:00
macOS: Implement new dynamic Finder menu items #6328
This commit is contained in:
parent
5b0a1d4c79
commit
d3cbe63801
shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt
src/gui
@ -23,6 +23,7 @@
|
||||
NSMutableSet *_registeredDirectories;
|
||||
NSString *_shareMenuTitle;
|
||||
NSMutableDictionary *_strings;
|
||||
NSMutableArray *_menuItems;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -231,6 +231,8 @@ SocketApi::~SocketApi()
|
||||
|
||||
void SocketApi::slotNewConnection()
|
||||
{
|
||||
// Note that on macOS this is not actually a line-based QIODevice, it's a SocketApiSocket which is our
|
||||
// custom message based macOS IPC.
|
||||
QIODevice *socket = _localServer.nextPendingConnection();
|
||||
|
||||
if (!socket) {
|
||||
|
Loading…
Reference in New Issue
Block a user