1998-12-01 20:54:23 +00:00
|
|
|
|
/*
|
1998-12-10 21:14:52 +00:00
|
|
|
|
GSServicesManager.m
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
|
|
|
|
Copyright (C) 1998 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
|
|
Author: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
|
|
|
|
Date: Novemeber 1998
|
|
|
|
|
|
|
|
|
|
This file is part of the GNUstep GUI Library.
|
|
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
2007-10-29 21:16:17 +00:00
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
1998-12-01 20:54:23 +00:00
|
|
|
|
License as published by the Free Software Foundation; either
|
2008-06-10 04:01:49 +00:00
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
2007-10-29 21:16:17 +00:00
|
|
|
|
|
1998-12-01 20:54:23 +00:00
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2007-10-29 21:16:17 +00:00
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
Lesser General Public License for more details.
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
2007-10-29 21:16:17 +00:00
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
1998-12-01 20:54:23 +00:00
|
|
|
|
License along with this library; see the file COPYING.LIB.
|
2007-10-29 21:16:17 +00:00
|
|
|
|
If not, see <http://www.gnu.org/licenses/> or write to the
|
|
|
|
|
Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
|
|
|
|
Boston, MA 02110-1301, USA.
|
1998-12-01 20:54:23 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2010-05-24 10:48:14 +00:00
|
|
|
|
#import "config.h"
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
2010-05-24 10:48:14 +00:00
|
|
|
|
#import <Foundation/NSArray.h>
|
|
|
|
|
#import <Foundation/NSSet.h>
|
|
|
|
|
#import <Foundation/NSException.h>
|
|
|
|
|
#import <Foundation/NSData.h>
|
|
|
|
|
#import <Foundation/NSDictionary.h>
|
|
|
|
|
#import <Foundation/NSNotification.h>
|
|
|
|
|
#import <Foundation/NSRunLoop.h>
|
|
|
|
|
#import <Foundation/NSAutoreleasePool.h>
|
|
|
|
|
#import <Foundation/NSTimer.h>
|
|
|
|
|
#import <Foundation/NSProcessInfo.h>
|
|
|
|
|
#import <Foundation/NSFileManager.h>
|
|
|
|
|
#import <Foundation/NSConnection.h>
|
|
|
|
|
#import <Foundation/NSDistantObject.h>
|
|
|
|
|
#import <Foundation/NSMethodSignature.h>
|
|
|
|
|
#import <Foundation/NSPathUtilities.h>
|
|
|
|
|
#import <Foundation/NSUserDefaults.h>
|
|
|
|
|
#import <Foundation/NSSerialization.h>
|
|
|
|
|
#import <Foundation/NSPort.h>
|
|
|
|
|
#import <Foundation/NSPortNameServer.h>
|
|
|
|
|
#import <Foundation/NSTask.h>
|
|
|
|
|
#import <Foundation/NSObjCRuntime.h>
|
|
|
|
|
#import <Foundation/NSInvocation.h>
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
2010-05-24 10:48:14 +00:00
|
|
|
|
#import "AppKit/NSApplication.h"
|
|
|
|
|
#import "AppKit/NSPasteboard.h"
|
|
|
|
|
#import "AppKit/NSMenu.h"
|
|
|
|
|
#import "AppKit/NSPanel.h"
|
|
|
|
|
#import "AppKit/NSWindow.h"
|
|
|
|
|
#import "AppKit/NSWorkspace.h"
|
|
|
|
|
#import "AppKit/NSDocumentController.h"
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
2010-05-24 10:48:14 +00:00
|
|
|
|
#import "GNUstepGUI/GSServicesManager.h"
|
|
|
|
|
#import "GSGuiPrivate.h"
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
2003-04-09 16:34:49 +00:00
|
|
|
|
static GSServicesManager *manager = nil;
|
2003-04-09 16:12:22 +00:00
|
|
|
|
|
2005-06-06 04:05:05 +00:00
|
|
|
|
/**
|
1999-04-08 20:42:46 +00:00
|
|
|
|
* The GSListener class is for talking to other applications.
|
1998-12-01 20:54:23 +00:00
|
|
|
|
* It is a proxy with some dangerous methods implemented in a
|
|
|
|
|
* harmless manner to reduce the chances of a malicious app
|
1999-01-07 14:28:58 +00:00
|
|
|
|
* messing with us. This is responsible for forwarding service
|
|
|
|
|
* requests and other communications with the outside world.
|
1998-12-01 20:54:23 +00:00
|
|
|
|
*/
|
2003-07-14 12:27:42 +00:00
|
|
|
|
@interface GSListener : NSProxy
|
1998-12-01 20:54:23 +00:00
|
|
|
|
+ (id) connectionBecameInvalid: (NSNotification*)notification;
|
1999-04-08 20:42:46 +00:00
|
|
|
|
+ (GSListener*) listener;
|
1998-12-01 20:54:23 +00:00
|
|
|
|
+ (id) servicesProvider;
|
|
|
|
|
+ (void) setServicesProvider: (id)anObject;
|
|
|
|
|
- (Class) class;
|
|
|
|
|
- (void) dealloc;
|
|
|
|
|
- (void) release;
|
|
|
|
|
- (id) retain;
|
2004-09-05 13:46:13 +00:00
|
|
|
|
- (void) activateIgnoringOtherApps: (BOOL)flag;
|
1998-12-01 20:54:23 +00:00
|
|
|
|
- (id) self;
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
static NSConnection *listenerConnection = nil;
|
2003-07-14 12:27:42 +00:00
|
|
|
|
static NSMutableArray *listeners = nil;
|
1999-04-08 20:42:46 +00:00
|
|
|
|
static GSListener *listener = nil;
|
1998-12-01 20:54:23 +00:00
|
|
|
|
static id servicesProvider = nil;
|
2001-04-18 09:25:39 +00:00
|
|
|
|
static NSString *providerName = nil;
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
2003-06-22 14:36:17 +00:00
|
|
|
|
/**
|
|
|
|
|
* Unregisters the service provider registered on the named port.<br />
|
|
|
|
|
* Applications should use [NSApplication-setServicesProvider:] with a nil
|
|
|
|
|
* argument instead.
|
|
|
|
|
*/
|
1998-12-01 20:54:23 +00:00
|
|
|
|
void
|
|
|
|
|
NSUnregisterServicesProvider(NSString *name)
|
|
|
|
|
{
|
2001-04-18 09:25:39 +00:00
|
|
|
|
if (listenerConnection != nil)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Ensure there is no previous listener and nothing else using
|
|
|
|
|
* the given port name.
|
|
|
|
|
*/
|
2000-07-04 09:52:17 +00:00
|
|
|
|
[[NSPortNameServer systemDefaultPortNameServer] removePortForName: name];
|
1999-06-15 20:02:58 +00:00
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
|
|
|
removeObserver: [GSListener class]
|
|
|
|
|
name: NSConnectionDidDieNotification
|
|
|
|
|
object: listenerConnection];
|
2001-04-18 09:25:39 +00:00
|
|
|
|
DESTROY(listenerConnection);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
2023-09-12 22:40:58 +00:00
|
|
|
|
DESTROY(servicesProvider);
|
|
|
|
|
DESTROY(providerName);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-06-22 14:36:17 +00:00
|
|
|
|
/**
|
|
|
|
|
* Registers a services providing object using the specified port name.<br />
|
|
|
|
|
* Applications should not need to use this, as they can use the
|
|
|
|
|
* [NSApplication-setServicesProvider:] method instead. The NSApplication
|
|
|
|
|
* method will use the name of the application rather than an other port name.
|
|
|
|
|
*/
|
1998-12-01 20:54:23 +00:00
|
|
|
|
void
|
|
|
|
|
NSRegisterServicesProvider(id provider, NSString *name)
|
|
|
|
|
{
|
2005-10-09 06:39:08 +00:00
|
|
|
|
NSPortNameServer *ns;
|
|
|
|
|
id namedPort;
|
|
|
|
|
|
|
|
|
|
if ([name length] == 0)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"NSRegisterServicesProvider() no name provided"];
|
|
|
|
|
}
|
|
|
|
|
if (provider == nil)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"NSRegisterServicesProvider() no provider"];
|
|
|
|
|
}
|
|
|
|
|
if (servicesProvider == provider && [providerName isEqual: name])
|
|
|
|
|
{
|
|
|
|
|
return; // Already registered.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ns = [NSPortNameServer systemDefaultPortNameServer];
|
|
|
|
|
namedPort = [ns portForName: name];
|
2008-10-18 23:07:03 +00:00
|
|
|
|
if (namedPort && [listenerConnection receivePort] == namedPort)
|
2005-10-09 06:39:08 +00:00
|
|
|
|
{
|
|
|
|
|
[ns removePortForName: name];
|
|
|
|
|
namedPort = nil;
|
|
|
|
|
}
|
|
|
|
|
if (namedPort != nil)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"NSRegisterServicesProvider() %@ already in use",
|
|
|
|
|
name];
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-18 09:25:39 +00:00
|
|
|
|
if (listenerConnection != nil)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
1999-06-15 20:02:58 +00:00
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
|
|
|
removeObserver: [GSListener class]
|
|
|
|
|
name: NSConnectionDidDieNotification
|
|
|
|
|
object: listenerConnection];
|
2001-04-18 09:25:39 +00:00
|
|
|
|
DESTROY(listenerConnection);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
2005-10-09 06:39:08 +00:00
|
|
|
|
|
2006-03-08 11:11:20 +00:00
|
|
|
|
listenerConnection = [[NSConnection alloc]
|
|
|
|
|
initWithReceivePort: [NSPort port] sendPort: nil];
|
|
|
|
|
[listenerConnection setRootObject: [GSListener listener]];
|
|
|
|
|
if ([listenerConnection registerName: name] == NO)
|
|
|
|
|
{
|
|
|
|
|
DESTROY(listenerConnection);
|
|
|
|
|
}
|
|
|
|
|
|
2005-10-09 06:39:08 +00:00
|
|
|
|
if (listenerConnection != nil)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
2005-10-09 06:39:08 +00:00
|
|
|
|
RETAIN(listenerConnection);
|
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
|
|
|
addObserver: [GSListener class]
|
|
|
|
|
selector: @selector(connectionBecameInvalid:)
|
|
|
|
|
name: NSConnectionDidDieNotification
|
|
|
|
|
object: listenerConnection];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
2005-10-09 06:39:08 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSGenericException
|
|
|
|
|
format: @"unable to register %@", name];
|
|
|
|
|
}
|
|
|
|
|
|
1998-12-01 20:54:23 +00:00
|
|
|
|
ASSIGN(servicesProvider, provider);
|
2001-04-18 09:25:39 +00:00
|
|
|
|
ASSIGN(providerName, name);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-10-08 09:42:22 +00:00
|
|
|
|
|
|
|
|
|
@interface NSNotificationCenter (NSWorkspacePrivate)
|
|
|
|
|
- (void) _postLocal: (NSString*)name userInfo: (NSDictionary*)info;
|
|
|
|
|
@end
|
|
|
|
|
|
2005-06-06 04:05:05 +00:00
|
|
|
|
/**
|
2003-07-14 12:27:42 +00:00
|
|
|
|
* The GSListener class exists as a proxy to forward messages to
|
|
|
|
|
* service provider objects. It implements very few methods and
|
|
|
|
|
* those that it does implement are generally designed to defeat
|
|
|
|
|
* any attack by a malicious program.
|
1998-12-01 20:54:23 +00:00
|
|
|
|
*/
|
1999-04-08 20:42:46 +00:00
|
|
|
|
@implementation GSListener
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
|
|
|
|
+ (id) connectionBecameInvalid: (NSNotification*)notification
|
|
|
|
|
{
|
|
|
|
|
NSAssert(listenerConnection==[notification object],
|
|
|
|
|
NSInternalInconsistencyException);
|
|
|
|
|
|
1999-06-15 20:02:58 +00:00
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
|
|
|
removeObserver: self
|
|
|
|
|
name: NSConnectionDidDieNotification
|
|
|
|
|
object: listenerConnection];
|
2001-04-18 09:25:39 +00:00
|
|
|
|
DESTROY(listenerConnection);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
2003-07-14 12:27:42 +00:00
|
|
|
|
+ (void) initialize
|
|
|
|
|
{
|
|
|
|
|
static BOOL beenHere = NO;
|
|
|
|
|
|
|
|
|
|
if (beenHere == NO)
|
|
|
|
|
{
|
|
|
|
|
beenHere = YES;
|
|
|
|
|
listeners = [NSMutableArray new];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-04-08 20:42:46 +00:00
|
|
|
|
+ (GSListener*) listener
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
if (listener == nil)
|
|
|
|
|
{
|
|
|
|
|
listener = (id)NSAllocateObject(self, 0, NSDefaultMallocZone());
|
2003-07-14 12:27:42 +00:00
|
|
|
|
[listeners addObject: listener];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
return listener;
|
|
|
|
|
}
|
|
|
|
|
|
2003-07-14 12:27:42 +00:00
|
|
|
|
/**
|
|
|
|
|
* Needed to permit use of this class as a notification observer,
|
|
|
|
|
* since the notification system caches method implementations for speed.
|
|
|
|
|
*/
|
|
|
|
|
+ (IMP) methodForSelector: (SEL)aSelector
|
|
|
|
|
{
|
|
|
|
|
if (aSelector == 0)
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"%@ null selector given", NSStringFromSelector(_cmd)];
|
2011-02-24 13:27:24 +00:00
|
|
|
|
return class_getMethodImplementation(GSObjCClass(self), aSelector);
|
2003-07-14 12:27:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-12-01 20:54:23 +00:00
|
|
|
|
+ (id) servicesProvider
|
|
|
|
|
{
|
|
|
|
|
return servicesProvider;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (void) setServicesProvider: (id)anObject
|
|
|
|
|
{
|
|
|
|
|
if (servicesProvider != anObject)
|
|
|
|
|
{
|
2005-10-09 06:39:08 +00:00
|
|
|
|
NSString *port = [[GSServicesManager manager] port];
|
|
|
|
|
|
|
|
|
|
if (port == nil)
|
|
|
|
|
{
|
|
|
|
|
port = [[NSProcessInfo processInfo] processName];
|
|
|
|
|
}
|
|
|
|
|
NSRegisterServicesProvider(anObject, port);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-07-14 12:27:42 +00:00
|
|
|
|
- (id) autorelease
|
|
|
|
|
{
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
1998-12-01 20:54:23 +00:00
|
|
|
|
- (Class) class
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
2006-07-04 21:31:49 +00:00
|
|
|
|
GSNOSUPERDEALLOC;
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-07-14 12:27:42 +00:00
|
|
|
|
/**
|
2006-02-03 10:57:49 +00:00
|
|
|
|
* Selectively forwards those messages which are thought to be safe,
|
|
|
|
|
* and perform any special operations we need for workspace management
|
2006-02-03 14:34:45 +00:00
|
|
|
|
* etc.<br />
|
|
|
|
|
* The logic in this method <strong>must</strong> match that in
|
|
|
|
|
* methodSignatureForSelector:
|
2001-12-29 08:22:33 +00:00
|
|
|
|
*/
|
|
|
|
|
- (void) forwardInvocation: (NSInvocation*)anInvocation
|
|
|
|
|
{
|
|
|
|
|
SEL aSel = [anInvocation selector];
|
|
|
|
|
NSString *selName = NSStringFromSelector(aSel);
|
2006-02-03 10:57:49 +00:00
|
|
|
|
id target = nil;
|
|
|
|
|
id delegate;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We never permit remote processes to call private methods in this
|
|
|
|
|
* application.
|
|
|
|
|
*/
|
|
|
|
|
if ([selName hasPrefix: @"_"] == YES)
|
2001-12-29 08:22:33 +00:00
|
|
|
|
{
|
2006-02-03 10:57:49 +00:00
|
|
|
|
[NSException raise: NSGenericException
|
2006-02-03 14:34:45 +00:00
|
|
|
|
format: @"method name '%@' private in '%@'",
|
2006-02-03 10:57:49 +00:00
|
|
|
|
selName, [manager port]];
|
2005-10-08 09:42:22 +00:00
|
|
|
|
}
|
2006-02-03 10:57:49 +00:00
|
|
|
|
|
|
|
|
|
if ([selName hasSuffix: @":userData:error:"] == YES)
|
2005-10-08 09:42:22 +00:00
|
|
|
|
{
|
|
|
|
|
/*
|
2006-02-03 10:57:49 +00:00
|
|
|
|
* The selector matches the correct form for a services request,
|
|
|
|
|
* so we send the message to the services provider.
|
2005-10-08 09:42:22 +00:00
|
|
|
|
*/
|
2003-07-14 12:27:42 +00:00
|
|
|
|
if ([servicesProvider respondsToSelector: aSel] == YES)
|
|
|
|
|
{
|
|
|
|
|
NSPasteboard *pb;
|
2001-12-29 08:22:33 +00:00
|
|
|
|
|
2003-07-14 12:27:42 +00:00
|
|
|
|
/*
|
|
|
|
|
* Create a local NSPasteboard object for this pasteboard.
|
|
|
|
|
* If we try to use the remote NSPasteboard object, we get
|
|
|
|
|
* trouble when setting property lists since the remote
|
|
|
|
|
* NSPasteboard fails to serialize the local property
|
|
|
|
|
* list objects for sending to gpbs.
|
|
|
|
|
*/
|
|
|
|
|
[anInvocation getArgument: (void*)&pb atIndex: 2];
|
|
|
|
|
pb = [NSPasteboard pasteboardWithName: [pb name]];
|
|
|
|
|
[anInvocation setArgument: (void*)&pb atIndex: 2];
|
|
|
|
|
[anInvocation invokeWithTarget: servicesProvider];
|
|
|
|
|
return;
|
|
|
|
|
}
|
2006-02-03 10:57:49 +00:00
|
|
|
|
[NSException raise: NSGenericException
|
|
|
|
|
format: @"service request '%@' not implemented in '%@'",
|
|
|
|
|
selName, [manager port]];
|
2003-04-09 16:34:49 +00:00
|
|
|
|
}
|
2003-07-14 12:27:42 +00:00
|
|
|
|
|
2006-02-03 10:57:49 +00:00
|
|
|
|
delegate = [[NSApplication sharedApplication] delegate];
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We assume that messages of the form 'application:...' are all
|
2009-03-20 10:17:29 +00:00
|
|
|
|
* safe and do not need to be listed in GSPermittedMessages.
|
2006-02-03 10:57:49 +00:00
|
|
|
|
* They can be handled either by the application delegate or by
|
|
|
|
|
* the shared GSServicesManager instance.
|
|
|
|
|
*/
|
|
|
|
|
if ([selName hasPrefix: @"application:"] == YES)
|
|
|
|
|
{
|
|
|
|
|
if ([delegate respondsToSelector: aSel] == YES)
|
2005-12-04 08:44:54 +00:00
|
|
|
|
{
|
2006-02-03 10:57:49 +00:00
|
|
|
|
target = delegate;
|
|
|
|
|
}
|
|
|
|
|
else if ([manager respondsToSelector: aSel] == YES)
|
|
|
|
|
{
|
|
|
|
|
target = manager;
|
2005-12-04 08:44:54 +00:00
|
|
|
|
}
|
2006-02-03 10:57:49 +00:00
|
|
|
|
}
|
2005-12-04 08:44:54 +00:00
|
|
|
|
|
2006-02-03 10:57:49 +00:00
|
|
|
|
if (target == nil)
|
|
|
|
|
{
|
|
|
|
|
NSArray *messages;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Unless the message was of a format assumed to be safe,
|
|
|
|
|
* we must check it against the GSPermittedMessages array
|
|
|
|
|
* to see if the app allows it to be sent from a remote
|
|
|
|
|
* process.
|
|
|
|
|
*/
|
|
|
|
|
messages = [[NSUserDefaults standardUserDefaults] arrayForKey:
|
|
|
|
|
@"GSPermittedMessages"];
|
|
|
|
|
if (messages != nil && [messages containsObject: selName] == NO)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSGenericException
|
|
|
|
|
format: @"method '%@' not in GSPermittedMessages in '%@'",
|
|
|
|
|
selName, [manager port]];
|
|
|
|
|
}
|
|
|
|
|
if ([delegate respondsToSelector: aSel] == YES)
|
2003-07-14 12:27:42 +00:00
|
|
|
|
{
|
2006-02-03 10:57:49 +00:00
|
|
|
|
target = delegate;
|
2003-07-14 12:27:42 +00:00
|
|
|
|
}
|
2006-02-03 10:57:49 +00:00
|
|
|
|
else if ([NSApp respondsToSelector: aSel] == YES)
|
2003-07-14 12:27:42 +00:00
|
|
|
|
{
|
2006-02-03 10:57:49 +00:00
|
|
|
|
target = NSApp;
|
2003-07-14 12:27:42 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2006-02-03 10:57:49 +00:00
|
|
|
|
|
|
|
|
|
if (target == nil)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSGenericException
|
|
|
|
|
format: @"method '%@' not implemented in '%@'",
|
|
|
|
|
selName, [manager port]];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if ([selName isEqualToString: @"terminate:"])
|
|
|
|
|
{
|
|
|
|
|
NSNotificationCenter *c;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We handle the terminate: message as a special case, sending
|
|
|
|
|
* a power off notification before allowing it to be processed
|
|
|
|
|
* as normal.
|
|
|
|
|
*/
|
|
|
|
|
c = [[NSWorkspace sharedWorkspace] notificationCenter];
|
|
|
|
|
[c _postLocal: NSWorkspaceWillPowerOffNotification userInfo: nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[anInvocation invokeWithTarget: target];
|
|
|
|
|
}
|
2001-12-29 08:22:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-07-14 12:27:42 +00:00
|
|
|
|
/**
|
|
|
|
|
* Return the appropriate method signature for aSelector, checking
|
|
|
|
|
* to see if it's a standard service message or standard application
|
|
|
|
|
* message.<br />
|
|
|
|
|
* If the message is non-standard, it can be checked against a list
|
2006-02-03 14:34:45 +00:00
|
|
|
|
* of messages specified by the GSPermittedMessages user default.<br />
|
|
|
|
|
* The logic in this method <strong>must</strong> match that in
|
|
|
|
|
* forwardInvocation:
|
2003-07-14 12:27:42 +00:00
|
|
|
|
*/
|
|
|
|
|
- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector
|
1999-04-08 20:42:46 +00:00
|
|
|
|
{
|
2003-07-14 12:27:42 +00:00
|
|
|
|
NSMethodSignature *sig = nil;
|
|
|
|
|
NSString *selName = NSStringFromSelector(aSelector);
|
2006-02-03 14:34:45 +00:00
|
|
|
|
id delegate;
|
1999-04-08 20:42:46 +00:00
|
|
|
|
|
2006-02-03 14:34:45 +00:00
|
|
|
|
if ([selName hasSuffix: @":userData:error:"])
|
2005-10-08 09:42:22 +00:00
|
|
|
|
{
|
2006-02-03 14:34:45 +00:00
|
|
|
|
return [servicesProvider methodSignatureForSelector: aSelector];
|
2005-10-08 09:42:22 +00:00
|
|
|
|
}
|
2006-02-03 14:34:45 +00:00
|
|
|
|
|
|
|
|
|
delegate = [[NSApplication sharedApplication] delegate];
|
|
|
|
|
if ([selName hasPrefix: @"application:"] == YES)
|
1999-04-08 20:42:46 +00:00
|
|
|
|
{
|
2006-02-03 14:34:45 +00:00
|
|
|
|
if ([delegate respondsToSelector: aSelector] == YES)
|
|
|
|
|
{
|
|
|
|
|
sig = [delegate methodSignatureForSelector: aSelector];
|
|
|
|
|
}
|
|
|
|
|
else if ([manager respondsToSelector: aSelector] == YES)
|
|
|
|
|
{
|
|
|
|
|
sig = [manager methodSignatureForSelector: aSelector];
|
|
|
|
|
}
|
1999-04-08 20:42:46 +00:00
|
|
|
|
}
|
2006-02-03 14:34:45 +00:00
|
|
|
|
|
|
|
|
|
if (sig == nil)
|
1999-04-08 20:42:46 +00:00
|
|
|
|
{
|
2006-02-03 14:34:45 +00:00
|
|
|
|
NSArray *messages;
|
2003-07-14 12:27:42 +00:00
|
|
|
|
|
2006-02-03 14:34:45 +00:00
|
|
|
|
messages = [[NSUserDefaults standardUserDefaults] arrayForKey:
|
|
|
|
|
@"GSPermittedMessages"];
|
|
|
|
|
if (messages != nil && [messages containsObject: selName] == NO)
|
|
|
|
|
{
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
if ([delegate respondsToSelector: aSelector] == YES)
|
1999-04-08 20:42:46 +00:00
|
|
|
|
{
|
2006-02-03 14:34:45 +00:00
|
|
|
|
sig = [delegate methodSignatureForSelector: aSelector];
|
2003-07-14 12:27:42 +00:00
|
|
|
|
}
|
2006-02-03 14:34:45 +00:00
|
|
|
|
else if ([NSApp respondsToSelector: aSelector] == YES)
|
2003-07-14 12:27:42 +00:00
|
|
|
|
{
|
2006-02-03 14:34:45 +00:00
|
|
|
|
sig = [NSApp methodSignatureForSelector: aSelector];
|
1999-04-08 20:42:46 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2003-07-14 12:27:42 +00:00
|
|
|
|
return sig;
|
|
|
|
|
}
|
1999-04-08 20:42:46 +00:00
|
|
|
|
|
2003-07-14 12:27:42 +00:00
|
|
|
|
- (BOOL) respondsToSelector: (SEL)aSelector
|
|
|
|
|
{
|
|
|
|
|
if ([self methodSignatureForSelector: aSelector] != nil)
|
|
|
|
|
{
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
1999-04-08 20:42:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-12-01 20:54:23 +00:00
|
|
|
|
- (void) release
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) retain
|
|
|
|
|
{
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-05 13:46:13 +00:00
|
|
|
|
- (void) activateIgnoringOtherApps: (BOOL)flag
|
|
|
|
|
{
|
2004-09-18 13:16:55 +00:00
|
|
|
|
[NSApp activateIgnoringOtherApps: flag];
|
2004-09-05 13:46:13 +00:00
|
|
|
|
}
|
2004-09-18 13:16:55 +00:00
|
|
|
|
|
1998-12-01 20:54:23 +00:00
|
|
|
|
- (id) self
|
|
|
|
|
{
|
|
|
|
|
return self;
|
|
|
|
|
}
|
1999-04-08 20:42:46 +00:00
|
|
|
|
@end /* GSListener */
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1998-12-10 21:14:52 +00:00
|
|
|
|
@implementation GSServicesManager
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
|
|
|
|
static NSString *servicesName = @".GNUstepServices";
|
|
|
|
|
static NSString *disabledName = @".GNUstepDisabled";
|
|
|
|
|
|
2005-06-06 04:05:05 +00:00
|
|
|
|
/**
|
1998-12-01 20:54:23 +00:00
|
|
|
|
* Create a new listener for this application.
|
|
|
|
|
* Uses NSRegisterServicesProvider() to register itsself as a service
|
|
|
|
|
* provider with the applications name so we can handle incoming
|
|
|
|
|
* service requests.
|
|
|
|
|
*/
|
1998-12-10 21:14:52 +00:00
|
|
|
|
+ (GSServicesManager*) newWithApplication: (NSApplication*)app
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
2010-03-15 21:26:06 +00:00
|
|
|
|
NSString *str = nil;
|
2007-03-06 18:22:14 +00:00
|
|
|
|
NSArray *paths;
|
|
|
|
|
NSString *path = nil;
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
2001-04-18 09:25:39 +00:00
|
|
|
|
if (manager != nil)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
2002-01-06 13:26:27 +00:00
|
|
|
|
if (manager->_application == nil)
|
2001-04-18 09:25:39 +00:00
|
|
|
|
{
|
2002-01-06 13:26:27 +00:00
|
|
|
|
manager->_application = app;
|
2001-04-18 09:25:39 +00:00
|
|
|
|
}
|
2005-10-09 06:39:08 +00:00
|
|
|
|
return RETAIN(manager);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-12-10 21:14:52 +00:00
|
|
|
|
manager = [GSServicesManager alloc];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
2007-03-06 18:22:14 +00:00
|
|
|
|
paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
|
|
|
|
|
NSUserDomainMask, YES);
|
|
|
|
|
if ((paths != nil) && ([paths count] > 0))
|
|
|
|
|
{
|
|
|
|
|
str = [paths objectAtIndex: 0];
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* If standard search paths are not set up, try a default location.
|
|
|
|
|
*/
|
|
|
|
|
if (str == nil)
|
|
|
|
|
{
|
|
|
|
|
str = [[NSHomeDirectory() stringByAppendingPathComponent:
|
2010-02-26 05:20:59 +00:00
|
|
|
|
@"GNUstep"] stringByAppendingPathComponent: @"Library"];
|
2007-03-06 18:22:14 +00:00
|
|
|
|
}
|
2007-03-06 06:17:14 +00:00
|
|
|
|
str = [str stringByAppendingPathComponent: @"Services"];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
path = [str stringByAppendingPathComponent: servicesName];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
manager->_servicesPath = [path copy];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
path = [str stringByAppendingPathComponent: disabledName];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
manager->_disabledPath = [path copy];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
/*
|
2001-08-14 22:39:06 +00:00
|
|
|
|
* Don't retain application object - that would create a cycle.
|
1998-12-01 20:54:23 +00:00
|
|
|
|
*/
|
2002-01-06 13:26:27 +00:00
|
|
|
|
manager->_application = app;
|
|
|
|
|
manager->_returnInfo = [[NSMutableSet alloc] initWithCapacity: 16];
|
|
|
|
|
manager->_combinations = [[NSMutableDictionary alloc] initWithCapacity: 16];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
/*
|
|
|
|
|
* Check for changes to the services cache every thirty seconds.
|
|
|
|
|
*/
|
2002-01-06 13:26:27 +00:00
|
|
|
|
manager->_timer =
|
2003-07-21 10:41:10 +00:00
|
|
|
|
RETAIN([NSTimer scheduledTimerWithTimeInterval: 30.0
|
1998-12-01 20:54:23 +00:00
|
|
|
|
target: manager
|
|
|
|
|
selector: @selector(loadServices)
|
|
|
|
|
userInfo: nil
|
2003-07-21 10:41:10 +00:00
|
|
|
|
repeats: YES]);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
|
|
|
|
[manager loadServices];
|
|
|
|
|
return manager;
|
|
|
|
|
}
|
|
|
|
|
|
1998-12-10 21:14:52 +00:00
|
|
|
|
+ (GSServicesManager*) manager
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
if (manager == nil)
|
|
|
|
|
{
|
|
|
|
|
[self newWithApplication: nil];
|
|
|
|
|
}
|
|
|
|
|
return manager;
|
|
|
|
|
}
|
|
|
|
|
|
2003-04-09 16:12:22 +00:00
|
|
|
|
- (BOOL) application: (NSApplication*)theApp
|
|
|
|
|
openFile: (NSString*)file
|
|
|
|
|
{
|
|
|
|
|
id del = [NSApp delegate];
|
|
|
|
|
BOOL result = NO;
|
2013-11-01 20:34:29 +00:00
|
|
|
|
NSError *err = nil;
|
2003-04-09 16:12:22 +00:00
|
|
|
|
|
|
|
|
|
if ([del respondsToSelector: _cmd])
|
|
|
|
|
{
|
|
|
|
|
result = [del application: theApp openFile: file];
|
|
|
|
|
}
|
2003-04-09 16:34:49 +00:00
|
|
|
|
else if ([[NSDocumentController sharedDocumentController]
|
2013-11-01 20:34:29 +00:00
|
|
|
|
openDocumentWithContentsOfURL: [NSURL fileURLWithPath: file]
|
|
|
|
|
display: YES
|
|
|
|
|
error: &err] != nil)
|
2003-04-09 16:12:22 +00:00
|
|
|
|
{
|
2003-04-10 05:36:19 +00:00
|
|
|
|
[NSApp activateIgnoringOtherApps: YES];
|
2003-04-09 16:34:49 +00:00
|
|
|
|
result = YES;
|
2003-04-09 16:12:22 +00:00
|
|
|
|
}
|
2013-11-01 20:34:29 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[NSApp presentError: err];
|
|
|
|
|
}
|
2003-04-09 16:12:22 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-07 14:05:51 +00:00
|
|
|
|
- (void) application: (NSApplication*)theApp
|
|
|
|
|
openFiles: (NSArray*)files
|
|
|
|
|
{
|
|
|
|
|
id del = [NSApp delegate];
|
|
|
|
|
|
2012-08-07 16:10:27 +00:00
|
|
|
|
if ([del respondsToSelector: _cmd])
|
2012-08-07 14:05:51 +00:00
|
|
|
|
{
|
|
|
|
|
[del application: theApp openFiles: files];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NSString *filePath;
|
|
|
|
|
NSEnumerator *en = [files objectEnumerator];
|
|
|
|
|
|
|
|
|
|
while ((filePath = (NSString *)[en nextObject]) != nil)
|
|
|
|
|
{
|
|
|
|
|
[self application: theApp openFile: filePath];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-04-09 16:12:22 +00:00
|
|
|
|
- (BOOL) application: (NSApplication*)theApp
|
|
|
|
|
openFileWithoutUI: (NSString*)file
|
|
|
|
|
{
|
|
|
|
|
id del = [NSApp delegate];
|
|
|
|
|
BOOL result = NO;
|
2013-11-01 20:34:29 +00:00
|
|
|
|
NSError *err = nil;
|
2003-04-09 16:12:22 +00:00
|
|
|
|
|
|
|
|
|
if ([del respondsToSelector: _cmd])
|
|
|
|
|
{
|
|
|
|
|
result = [del application: theApp openFileWithoutUI: file];
|
|
|
|
|
}
|
2003-04-09 16:34:49 +00:00
|
|
|
|
else if ([[NSDocumentController sharedDocumentController]
|
2013-11-01 20:34:29 +00:00
|
|
|
|
openDocumentWithContentsOfURL: [NSURL fileURLWithPath: file]
|
|
|
|
|
display: NO
|
|
|
|
|
error: &err] != nil)
|
2003-04-09 16:12:22 +00:00
|
|
|
|
{
|
2003-04-09 16:34:49 +00:00
|
|
|
|
result = YES;
|
2003-04-09 16:12:22 +00:00
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) application: (NSApplication*)theApp
|
|
|
|
|
openTempFile: (NSString*)file
|
|
|
|
|
{
|
|
|
|
|
BOOL result = [self application: theApp openFile: file];
|
|
|
|
|
|
|
|
|
|
[[NSFileManager defaultManager] removeFileAtPath: file handler: nil];
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-20 10:17:29 +00:00
|
|
|
|
- (BOOL) application: (NSApplication*)theApp
|
|
|
|
|
openURL: (NSURL*)aURL
|
|
|
|
|
{
|
|
|
|
|
id del = [NSApp delegate];
|
|
|
|
|
BOOL result = NO;
|
2013-11-01 20:34:29 +00:00
|
|
|
|
NSError *err = nil;
|
2009-03-20 10:17:29 +00:00
|
|
|
|
|
|
|
|
|
if ([del respondsToSelector: _cmd])
|
|
|
|
|
{
|
|
|
|
|
result = [del application: theApp openURL: aURL];
|
|
|
|
|
}
|
|
|
|
|
else if ([[NSDocumentController sharedDocumentController]
|
2013-11-01 20:34:29 +00:00
|
|
|
|
openDocumentWithContentsOfURL: aURL
|
|
|
|
|
display: YES
|
|
|
|
|
error: &err] != nil)
|
2009-03-20 10:17:29 +00:00
|
|
|
|
{
|
|
|
|
|
[NSApp activateIgnoringOtherApps: YES];
|
|
|
|
|
result = YES;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NSString *s = [aURL absoluteString];
|
|
|
|
|
|
|
|
|
|
result = [self application: theApp openFile: s];
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2003-04-09 16:12:22 +00:00
|
|
|
|
- (BOOL) application: (NSApplication*)theApp
|
|
|
|
|
printFile: (NSString*)file
|
|
|
|
|
{
|
|
|
|
|
id del = [NSApp delegate];
|
|
|
|
|
|
|
|
|
|
if ([del respondsToSelector: _cmd])
|
|
|
|
|
return [del application: theApp printFile: file];
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
1998-12-01 20:54:23 +00:00
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
|
|
|
|
NSString *appName;
|
|
|
|
|
|
1999-09-03 08:59:07 +00:00
|
|
|
|
appName = [[NSProcessInfo processInfo] processName];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
[_timer invalidate];
|
2003-07-21 10:41:10 +00:00
|
|
|
|
RELEASE(_timer);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
NSUnregisterServicesProvider(appName);
|
2002-01-06 13:26:27 +00:00
|
|
|
|
RELEASE(_languages);
|
|
|
|
|
RELEASE(_returnInfo);
|
|
|
|
|
RELEASE(_combinations);
|
|
|
|
|
RELEASE(_title2info);
|
|
|
|
|
RELEASE(_menuTitles);
|
|
|
|
|
RELEASE(_servicesMenu);
|
|
|
|
|
RELEASE(_disabledPath);
|
|
|
|
|
RELEASE(_servicesPath);
|
|
|
|
|
RELEASE(_disabledStamp);
|
|
|
|
|
RELEASE(_servicesStamp);
|
|
|
|
|
RELEASE(_allDisabled);
|
|
|
|
|
RELEASE(_allServices);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
2002-06-10 23:48:09 +00:00
|
|
|
|
- (void) doService: (NSMenuItem*)item
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
NSString *title = [self item2title: item];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
NSDictionary *info = [_title2info objectForKey: title];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
NSArray *sendTypes = [info objectForKey: @"NSSendTypes"];
|
|
|
|
|
NSArray *returnTypes = [info objectForKey: @"NSReturnTypes"];
|
|
|
|
|
unsigned i, j;
|
|
|
|
|
unsigned es = [sendTypes count];
|
|
|
|
|
unsigned er = [returnTypes count];
|
2006-07-04 21:31:49 +00:00
|
|
|
|
NSResponder *resp = [[_application keyWindow] firstResponder];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
id obj = nil;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i <= es; i++)
|
|
|
|
|
{
|
|
|
|
|
NSString *sendType;
|
|
|
|
|
|
|
|
|
|
sendType = (i < es) ? [sendTypes objectAtIndex: i] : nil;
|
|
|
|
|
|
|
|
|
|
for (j = 0; j <= er; j++)
|
|
|
|
|
{
|
|
|
|
|
NSString *returnType;
|
|
|
|
|
|
|
|
|
|
returnType = (j < er) ? [returnTypes objectAtIndex: j] : nil;
|
|
|
|
|
|
|
|
|
|
obj = [resp validRequestorForSendType: sendType
|
|
|
|
|
returnType: returnType];
|
|
|
|
|
if (obj != nil)
|
|
|
|
|
{
|
|
|
|
|
NSPasteboard *pb;
|
|
|
|
|
|
|
|
|
|
pb = [NSPasteboard pasteboardWithUniqueName];
|
2002-06-08 18:11:17 +00:00
|
|
|
|
if (sendType
|
|
|
|
|
&& [obj writeSelectionToPasteboard: pb
|
|
|
|
|
types: sendTypes] == NO)
|
1998-12-14 15:59:51 +00:00
|
|
|
|
{
|
|
|
|
|
NSRunAlertPanel(nil,
|
|
|
|
|
@"object failed to write to pasteboard",
|
|
|
|
|
@"Continue", nil, nil);
|
|
|
|
|
}
|
|
|
|
|
else if (NSPerformService(title, pb) == YES)
|
|
|
|
|
{
|
2002-06-08 18:11:17 +00:00
|
|
|
|
if (returnType
|
|
|
|
|
&& [obj readSelectionFromPasteboard: pb] == NO)
|
1998-12-14 15:59:51 +00:00
|
|
|
|
{
|
|
|
|
|
NSRunAlertPanel(nil,
|
|
|
|
|
@"object failed to read from pasteboard",
|
|
|
|
|
@"Continue", nil, nil);
|
|
|
|
|
}
|
|
|
|
|
}
|
1998-12-01 20:54:23 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-06-27 11:13:30 +00:00
|
|
|
|
/**
|
|
|
|
|
* Return a dictionary of information about registered filter services.
|
|
|
|
|
*/
|
2003-06-30 16:17:45 +00:00
|
|
|
|
- (NSArray*) filters
|
2003-06-27 11:13:30 +00:00
|
|
|
|
{
|
|
|
|
|
return [_allServices objectForKey: @"ByFilter"];
|
|
|
|
|
}
|
|
|
|
|
|
1998-12-01 20:54:23 +00:00
|
|
|
|
- (BOOL) hasRegisteredTypes: (NSDictionary*)service
|
|
|
|
|
{
|
|
|
|
|
NSArray *sendTypes = [service objectForKey: @"NSSendTypes"];
|
|
|
|
|
NSArray *returnTypes = [service objectForKey: @"NSReturnTypes"];
|
|
|
|
|
NSString *type;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We know that both sendTypes and returnTypes can't be nil since
|
|
|
|
|
* make_services has validated the service entry for us.
|
|
|
|
|
*/
|
|
|
|
|
if (sendTypes == nil || [sendTypes count] == 0)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < [returnTypes count]; i++)
|
|
|
|
|
{
|
|
|
|
|
type = [returnTypes objectAtIndex: i];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
if ([_returnInfo member: type] != nil)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (returnTypes == nil || [returnTypes count] == 0)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < [sendTypes count]; i++)
|
|
|
|
|
{
|
|
|
|
|
type = [sendTypes objectAtIndex: i];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
if ([_combinations objectForKey: type] != nil)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < [sendTypes count]; i++)
|
|
|
|
|
{
|
|
|
|
|
NSSet *rset;
|
|
|
|
|
|
|
|
|
|
type = [sendTypes objectAtIndex: i];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
rset = [_combinations objectForKey: type];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
if (rset != nil)
|
|
|
|
|
{
|
|
|
|
|
unsigned j;
|
|
|
|
|
|
|
|
|
|
for (j = 0; j < [returnTypes count]; j++)
|
|
|
|
|
{
|
|
|
|
|
type = [returnTypes objectAtIndex: j];
|
|
|
|
|
if ([rset member: type] != nil)
|
|
|
|
|
{
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
2005-06-06 04:05:05 +00:00
|
|
|
|
/**
|
2002-06-10 23:48:09 +00:00
|
|
|
|
* Use tag in menu item to identify slot in menu titles array that
|
1998-12-01 20:54:23 +00:00
|
|
|
|
* contains the full title of the service.
|
2002-06-10 23:48:09 +00:00
|
|
|
|
* Return nil if this is not one of our service menu items.
|
1998-12-01 20:54:23 +00:00
|
|
|
|
*/
|
2005-01-21 21:43 Alexander Malmberg <alexander@malmberg.org>
Various whitespace cleanups, comment type fixes, and changes
to avoid warnings from recent versions of gcc.
* Headers/Additions/GNUstepGUI/GSToolbar.h (-_toolbars): Declare.
* Source/NSWindow+Toolbar.m: Remove conflicting declaration of
[NSToolbar -_toolbars].
* Headers/Additions/GNUstepGUI/GSServicesManager.h,
Source/GSServicesMananger.m (-item2title:, -validateMenuItem:):
Adjust argument types.
* Headers/AppKit/NSMenu.h (-validateMenuItem:): Adjust argument
type.
* Source/NSTextView.m (-sizeToFit): Don't use size uninitialized
if neither resizable flags is set.
(-insertText:): Adjust argument type.
* Headers/AppKit/NSResponder.h, Source/NSResponder.m (-insertText:):
Adjust argument type. Document.
* Headers/AppKit/NSView.h: Change type of ivar _window to NSWindow *.
* Source/GSTitleView.m (-mouseDown:): Always initialize
startWindowOrigin.
* Source/NSApplication.m (-setApplicationIconImage:): Add casts
to avoid warnings.
* Source/NSCell.m (-cellSize): Add default: case.
* Source/NSPasteboard.m
([GSFiltered -pasteboard:provideDataForType:]): Detect and warn if we
can't find a filter that will get us the desired type.
* Source/NSProgressIndicator.m: Comment out unused variable 'images'.
* Source/NSBezierPath.m: Declare GSBezierPath fully before using it.
(-bezierPathByFlatteningPath, -bezierPathByReversingPath): Make sure
variables are always initialized.
* Source/NSMenuView.m,
* Source/NSPrintOperation.m,
* Source/NSSplitView.m,
* Source/NSTableHeaderView.m: Make sure variables are always
initialized.
* Source/NSBox.m,
* Source/NSImageview.m,
* Source/NSText.m,
* Source/NSTextStorage.m: Add missing includes.
* Source/GSKeyBindingTable.m,
* Source/GSLayoutManager.m,
* Source/NSBitmapImageRep+PNM.m,
* Source/NSBundleAdditions.m,
* Source/NSLayoutManager.m,
* Source/nsimage-tiff.h,
* Source/tiff.m,
* Headers/Additions/GNUstepGUI/GSDisplayServer.h,
* Source/GSDisplayServer.m: Change signedness of various variables.
* Source/NSPanel.m (-sendEvent:): Remove.
* Source/NSWindow.m (-becomesKeyOnlyIfNeeded): New method.
(-_sendEvent:becomesKeyOnlyIfNeeded:): Remove. Move code ...
(-sendEvent:): ... here. Use -becomesKeyOnlyIfNeeded instead
of the argument.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@20590 72102866-910b-0410-8b05-ffd578937521
2005-01-21 20:39:18 +00:00
|
|
|
|
- (NSString*) item2title: (id<NSMenuItem>)item
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned pos;
|
|
|
|
|
|
|
|
|
|
if ([item target] != self)
|
|
|
|
|
return nil;
|
|
|
|
|
pos = [item tag];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
if (pos > [_menuTitles count])
|
1998-12-01 20:54:23 +00:00
|
|
|
|
return nil;
|
2002-01-06 13:26:27 +00:00
|
|
|
|
return [_menuTitles objectAtIndex: pos];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) loadServices
|
|
|
|
|
{
|
|
|
|
|
NSFileManager *mgr = [NSFileManager defaultManager];
|
|
|
|
|
BOOL changed = NO;
|
|
|
|
|
|
2002-01-06 13:26:27 +00:00
|
|
|
|
if ([mgr fileExistsAtPath: _disabledPath])
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
NSDictionary *attr;
|
|
|
|
|
NSDate *mod;
|
|
|
|
|
|
2002-01-06 13:26:27 +00:00
|
|
|
|
attr = [mgr fileAttributesAtPath: _disabledPath
|
1998-12-01 20:54:23 +00:00
|
|
|
|
traverseLink: YES];
|
|
|
|
|
mod = [attr objectForKey: NSFileModificationDate];
|
2024-10-31 19:04:45 +00:00
|
|
|
|
if (_disabledStamp == nil
|
|
|
|
|
|| [_disabledStamp laterDate: mod] != _disabledStamp)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
NSData *data;
|
|
|
|
|
id plist = nil;
|
|
|
|
|
|
2002-01-06 13:26:27 +00:00
|
|
|
|
data = [NSData dataWithContentsOfFile: _disabledPath];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
if (data)
|
|
|
|
|
{
|
|
|
|
|
plist = [NSDeserializer deserializePropertyListFromData: data
|
|
|
|
|
mutableContainers: NO];
|
|
|
|
|
if (plist)
|
|
|
|
|
{
|
|
|
|
|
NSMutableSet *s;
|
|
|
|
|
changed = YES;
|
|
|
|
|
s = (NSMutableSet*)[NSMutableSet setWithArray: plist];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
ASSIGN(_allDisabled, s);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2002-06-12 21:04:51 +00:00
|
|
|
|
/* Track most recent version of file loaded */
|
|
|
|
|
ASSIGN(_disabledStamp, mod);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-06 13:26:27 +00:00
|
|
|
|
if ([mgr fileExistsAtPath: _servicesPath])
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
NSDictionary *attr;
|
|
|
|
|
NSDate *mod;
|
|
|
|
|
|
2002-01-06 13:26:27 +00:00
|
|
|
|
attr = [mgr fileAttributesAtPath: _servicesPath
|
1998-12-01 20:54:23 +00:00
|
|
|
|
traverseLink: YES];
|
|
|
|
|
mod = [attr objectForKey: NSFileModificationDate];
|
2024-10-31 19:04:45 +00:00
|
|
|
|
if (_servicesStamp == nil
|
|
|
|
|
|| [_servicesStamp laterDate: mod] != _servicesStamp)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
NSData *data;
|
|
|
|
|
id plist = nil;
|
|
|
|
|
|
2002-01-06 13:26:27 +00:00
|
|
|
|
data = [NSData dataWithContentsOfFile: _servicesPath];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
if (data)
|
|
|
|
|
{
|
|
|
|
|
plist = [NSDeserializer deserializePropertyListFromData: data
|
|
|
|
|
mutableContainers: YES];
|
|
|
|
|
if (plist)
|
|
|
|
|
{
|
2002-01-06 13:26:27 +00:00
|
|
|
|
ASSIGN(_allServices, plist);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
changed = YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-06-12 21:04:51 +00:00
|
|
|
|
/* Track most recent version of file loaded */
|
|
|
|
|
ASSIGN(_servicesStamp, mod);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (changed)
|
|
|
|
|
{
|
2007-12-07 12:31:51 +00:00
|
|
|
|
/* If we have changed the enabled/disabled services,
|
|
|
|
|
* or there have been services added/removed
|
|
|
|
|
* then we must rebuild the services menu to add/remove
|
|
|
|
|
* items as appropriate.
|
|
|
|
|
*/
|
|
|
|
|
[self rebuildServicesMenu];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSDictionary*) menuServices
|
|
|
|
|
{
|
2002-01-06 13:26:27 +00:00
|
|
|
|
if (_allServices == nil)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
[self loadServices];
|
|
|
|
|
}
|
2002-01-06 13:26:27 +00:00
|
|
|
|
return _title2info;
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-10-09 06:39:08 +00:00
|
|
|
|
/**
|
|
|
|
|
* Returns the 'port' of this application ... this is the name the
|
|
|
|
|
* application is registered under so that other apps can connect to
|
|
|
|
|
* it to use any services it provides.
|
|
|
|
|
*/
|
|
|
|
|
- (NSString*) port
|
|
|
|
|
{
|
|
|
|
|
return _port;
|
|
|
|
|
}
|
|
|
|
|
|
2007-12-07 12:31:51 +00:00
|
|
|
|
/**
|
|
|
|
|
* Makes the current set of usable services consistent with the
|
|
|
|
|
* data types currently available.
|
|
|
|
|
*/
|
1998-12-01 20:54:23 +00:00
|
|
|
|
- (void) rebuildServices
|
|
|
|
|
{
|
|
|
|
|
NSDictionary *services;
|
|
|
|
|
NSMutableArray *newLang;
|
|
|
|
|
NSMutableSet *alreadyFound;
|
|
|
|
|
NSMutableDictionary *newServices;
|
|
|
|
|
unsigned pos;
|
|
|
|
|
|
2002-01-06 13:26:27 +00:00
|
|
|
|
if (_allServices == nil)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2011-03-05 16:18:04 +00:00
|
|
|
|
newLang = [[[[NSUserDefaults standardUserDefaults]
|
|
|
|
|
stringArrayForKey: @"NSLanguages"] mutableCopy] autorelease];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
if (newLang == nil)
|
|
|
|
|
{
|
|
|
|
|
newLang = [NSMutableArray arrayWithCapacity: 1];
|
|
|
|
|
}
|
|
|
|
|
if ([newLang containsObject: @"default"] == NO)
|
|
|
|
|
{
|
|
|
|
|
[newLang addObject: @"default"];
|
|
|
|
|
}
|
2002-01-06 13:26:27 +00:00
|
|
|
|
ASSIGN(_languages, newLang);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
2002-01-06 13:26:27 +00:00
|
|
|
|
services = [_allServices objectForKey: @"ByService"];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
|
|
|
|
newServices = [NSMutableDictionary dictionaryWithCapacity: 16];
|
|
|
|
|
alreadyFound = [NSMutableSet setWithCapacity: 16];
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Build dictionary of services we can use.
|
|
|
|
|
* 1. make sure we make dictionary keyed on preferred menu item language
|
|
|
|
|
* 2. don't include entries for services already examined.
|
|
|
|
|
* 3. don't include entries for menu items specifically disabled.
|
|
|
|
|
* 4. don't include entries for which we have no registered types.
|
|
|
|
|
*/
|
2002-01-06 13:26:27 +00:00
|
|
|
|
for (pos = 0; pos < [_languages count]; pos++)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
NSDictionary *byLanguage;
|
|
|
|
|
|
2002-01-06 13:26:27 +00:00
|
|
|
|
byLanguage = [services objectForKey: [_languages objectAtIndex: pos]];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
if (byLanguage != nil)
|
|
|
|
|
{
|
|
|
|
|
NSEnumerator *enumerator = [byLanguage keyEnumerator];
|
|
|
|
|
NSString *menuItem;
|
|
|
|
|
|
|
|
|
|
while ((menuItem = [enumerator nextObject]) != nil)
|
|
|
|
|
{
|
|
|
|
|
NSDictionary *service = [byLanguage objectForKey: menuItem];
|
|
|
|
|
|
|
|
|
|
if ([alreadyFound member: service] != nil)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
[alreadyFound addObject: service];
|
|
|
|
|
|
|
|
|
|
/* See if this service item is disabled. */
|
2002-01-06 13:26:27 +00:00
|
|
|
|
if ([_allDisabled member: menuItem] != nil)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if ([self hasRegisteredTypes: service])
|
|
|
|
|
[newServices setObject: service forKey: menuItem];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-01-06 13:26:27 +00:00
|
|
|
|
if ([newServices isEqual: _title2info] == NO)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
NSArray *titles;
|
|
|
|
|
|
2002-01-06 13:26:27 +00:00
|
|
|
|
ASSIGN(_title2info, newServices);
|
|
|
|
|
titles = [_title2info allKeys];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
titles = [titles sortedArrayUsingSelector: @selector(compare:)];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
ASSIGN(_menuTitles, titles);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
[self rebuildServicesMenu];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-12-07 12:31:51 +00:00
|
|
|
|
/** Adds or removes items in the services menu in response to a change
|
|
|
|
|
* in the services which are available to the app.
|
|
|
|
|
*/
|
1998-12-01 20:54:23 +00:00
|
|
|
|
- (void) rebuildServicesMenu
|
|
|
|
|
{
|
2002-01-06 13:26:27 +00:00
|
|
|
|
if (_servicesMenu != nil)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
NSMutableSet *keyEquivalents;
|
|
|
|
|
unsigned pos;
|
|
|
|
|
unsigned loc0;
|
1999-02-01 14:14:58 +00:00
|
|
|
|
unsigned loc1 = 0;
|
1998-12-01 20:54:23 +00:00
|
|
|
|
SEL sel = @selector(doService:);
|
|
|
|
|
NSMenu *submenu = nil;
|
|
|
|
|
|
2002-06-12 22:34:32 +00:00
|
|
|
|
[_servicesMenu setAutoenablesItems: NO];
|
2007-12-07 12:31:51 +00:00
|
|
|
|
for (pos = [_servicesMenu numberOfItems]; pos > 0; pos--)
|
|
|
|
|
{
|
|
|
|
|
[_servicesMenu removeItemAtIndex: 0];
|
|
|
|
|
}
|
2002-06-12 22:34:32 +00:00
|
|
|
|
[_servicesMenu setAutoenablesItems: YES];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
|
|
|
|
keyEquivalents = [NSMutableSet setWithCapacity: 4];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
for (loc0 = pos = 0; pos < [_menuTitles count]; pos++)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
2002-01-06 13:26:27 +00:00
|
|
|
|
NSString *title = [_menuTitles objectAtIndex: pos];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
NSString *equiv = @"";
|
2007-12-07 12:31:51 +00:00
|
|
|
|
NSDictionary *info;
|
1998-12-01 20:54:23 +00:00
|
|
|
|
NSDictionary *titles;
|
|
|
|
|
NSDictionary *equivs;
|
|
|
|
|
NSRange r;
|
|
|
|
|
unsigned lang;
|
|
|
|
|
id<NSMenuItem> item;
|
|
|
|
|
|
2007-12-07 12:31:51 +00:00
|
|
|
|
if (NSShowsServicesMenuItem(title) == NO)
|
|
|
|
|
{
|
|
|
|
|
continue; // We don't want to show this one.
|
|
|
|
|
}
|
|
|
|
|
|
1998-12-01 20:54:23 +00:00
|
|
|
|
/*
|
|
|
|
|
* Find the key equivalent corresponding to this menu title
|
|
|
|
|
* in the service definition.
|
|
|
|
|
*/
|
2007-12-07 12:31:51 +00:00
|
|
|
|
info = [_title2info objectForKey: title];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
titles = [info objectForKey: @"NSMenuItem"];
|
|
|
|
|
equivs = [info objectForKey: @"NSKeyEquivalent"];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
for (lang = 0; lang < [_languages count]; lang++)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
2002-01-06 13:26:27 +00:00
|
|
|
|
NSString *language = [_languages objectAtIndex: lang];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
NSString *t = [titles objectForKey: language];
|
|
|
|
|
|
|
|
|
|
if ([t isEqual: title])
|
|
|
|
|
{
|
|
|
|
|
equiv = [equivs objectForKey: language];
|
2002-01-06 13:06:56 +00:00
|
|
|
|
if (equiv == nil)
|
|
|
|
|
{
|
|
|
|
|
equiv = [equivs objectForKey: @"default"];
|
|
|
|
|
}
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Make a note that we are using the key equivalent, or
|
|
|
|
|
* set to nil if we have already used it in this menu.
|
|
|
|
|
*/
|
|
|
|
|
if (equiv)
|
|
|
|
|
{
|
|
|
|
|
if ([keyEquivalents member: equiv] == nil)
|
|
|
|
|
{
|
|
|
|
|
[keyEquivalents addObject: equiv];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
equiv = @"";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r = [title rangeOfString: @"/"];
|
|
|
|
|
if (r.length > 0)
|
|
|
|
|
{
|
|
|
|
|
NSString *subtitle = [title substringFromIndex: r.location+1];
|
|
|
|
|
NSString *parentTitle = [title substringToIndex: r.location];
|
|
|
|
|
NSMenu *menu;
|
|
|
|
|
|
2002-01-06 13:26:27 +00:00
|
|
|
|
item = [_servicesMenu itemWithTitle: parentTitle];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
if (item == nil)
|
|
|
|
|
{
|
|
|
|
|
loc1 = 0;
|
2002-01-06 13:26:27 +00:00
|
|
|
|
item = [_servicesMenu insertItemWithTitle: parentTitle
|
1998-12-01 20:54:23 +00:00
|
|
|
|
action: 0
|
|
|
|
|
keyEquivalent: @""
|
|
|
|
|
atIndex: loc0++];
|
|
|
|
|
menu = [[NSMenu alloc] initWithTitle: parentTitle];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
[_servicesMenu setSubmenu: menu
|
1998-12-01 20:54:23 +00:00
|
|
|
|
forItem: item];
|
2001-04-18 09:25:39 +00:00
|
|
|
|
RELEASE(menu);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-06-10 23:48:09 +00:00
|
|
|
|
menu = (NSMenu*)[item submenu];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
if (menu != submenu)
|
|
|
|
|
{
|
|
|
|
|
[submenu sizeToFit];
|
|
|
|
|
submenu = menu;
|
|
|
|
|
}
|
|
|
|
|
item = [submenu insertItemWithTitle: subtitle
|
|
|
|
|
action: sel
|
|
|
|
|
keyEquivalent: equiv
|
|
|
|
|
atIndex: loc1++];
|
|
|
|
|
[item setTarget: self];
|
|
|
|
|
[item setTag: pos];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-01-06 13:26:27 +00:00
|
|
|
|
item = [_servicesMenu insertItemWithTitle: title
|
1998-12-01 20:54:23 +00:00
|
|
|
|
action: sel
|
|
|
|
|
keyEquivalent: equiv
|
|
|
|
|
atIndex: loc0++];
|
|
|
|
|
[item setTarget: self];
|
|
|
|
|
[item setTag: pos];
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-20 20:55:23 +00:00
|
|
|
|
[submenu update];
|
|
|
|
|
// [submenu sizeToFit];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
// [_servicesMenu sizeToFit];
|
|
|
|
|
[_servicesMenu update];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-06-06 04:05:05 +00:00
|
|
|
|
/**
|
1998-12-01 20:54:23 +00:00
|
|
|
|
* Set up connection to listen for incoming service requests.
|
|
|
|
|
*/
|
|
|
|
|
- (void) registerAsServiceProvider
|
|
|
|
|
{
|
1999-01-09 06:49:48 +00:00
|
|
|
|
NSString *appName;
|
|
|
|
|
BOOL registered;
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
1999-09-03 08:59:07 +00:00
|
|
|
|
appName = [[NSProcessInfo processInfo] processName];
|
1998-12-10 19:53:41 +00:00
|
|
|
|
NS_DURING
|
1999-01-09 06:49:48 +00:00
|
|
|
|
{
|
|
|
|
|
NSRegisterServicesProvider(self, appName);
|
|
|
|
|
registered = YES;
|
|
|
|
|
}
|
1998-12-10 19:53:41 +00:00
|
|
|
|
NS_HANDLER
|
2001-04-18 09:25:39 +00:00
|
|
|
|
{
|
|
|
|
|
registered = NO;
|
|
|
|
|
}
|
1998-12-10 19:53:41 +00:00
|
|
|
|
NS_ENDHANDLER
|
1999-01-09 06:49:48 +00:00
|
|
|
|
|
|
|
|
|
if (registered == NO)
|
|
|
|
|
{
|
2005-12-04 08:44:54 +00:00
|
|
|
|
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
|
2005-12-04 08:55:21 +00:00
|
|
|
|
if ([defs boolForKey: @"NSUseRunningCopy"] == YES)
|
|
|
|
|
{
|
|
|
|
|
id app;
|
2005-12-04 08:44:54 +00:00
|
|
|
|
|
2005-12-04 08:55:21 +00:00
|
|
|
|
/*
|
|
|
|
|
* Try to activate the other app and terminate self.
|
|
|
|
|
*/
|
|
|
|
|
app = [NSConnection rootProxyForConnectionWithRegisteredName: appName
|
|
|
|
|
host: @""];
|
|
|
|
|
NS_DURING
|
2005-12-04 08:44:54 +00:00
|
|
|
|
{
|
2005-12-04 08:55:21 +00:00
|
|
|
|
[app activateIgnoringOtherApps: YES];
|
2005-12-04 08:44:54 +00:00
|
|
|
|
}
|
2005-12-04 08:55:21 +00:00
|
|
|
|
NS_HANDLER
|
2005-12-04 08:44:54 +00:00
|
|
|
|
{
|
2005-12-04 08:55:21 +00:00
|
|
|
|
/* maybe it terminated. */
|
2005-12-04 08:44:54 +00:00
|
|
|
|
}
|
2005-12-04 08:55:21 +00:00
|
|
|
|
NS_ENDHANDLER
|
|
|
|
|
registered = NO;
|
2005-12-04 08:44:54 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2005-10-09 06:39:08 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned count = 0;
|
1999-02-01 14:14:58 +00:00
|
|
|
|
|
2005-10-09 06:39:08 +00:00
|
|
|
|
/*
|
|
|
|
|
* Try to rename self as a copy.
|
|
|
|
|
*/
|
|
|
|
|
while (registered == NO && ++count < 100)
|
|
|
|
|
{
|
|
|
|
|
NSString *tmp;
|
1999-01-09 06:49:48 +00:00
|
|
|
|
|
2005-10-09 06:39:08 +00:00
|
|
|
|
tmp = [appName stringByAppendingFormat: @"Copy%d", count];
|
|
|
|
|
NS_DURING
|
|
|
|
|
{
|
|
|
|
|
NSRegisterServicesProvider(self, tmp);
|
|
|
|
|
registered = YES;
|
|
|
|
|
appName = tmp;
|
|
|
|
|
}
|
|
|
|
|
NS_HANDLER
|
|
|
|
|
{
|
|
|
|
|
registered = NO;
|
|
|
|
|
}
|
|
|
|
|
NS_ENDHANDLER
|
|
|
|
|
}
|
1999-01-09 06:49:48 +00:00
|
|
|
|
if (registered == NO)
|
|
|
|
|
{
|
2005-12-04 08:55:21 +00:00
|
|
|
|
int result;
|
|
|
|
|
|
2005-10-09 06:39:08 +00:00
|
|
|
|
/*
|
|
|
|
|
* Something is seriously wrong - we can't talk to the
|
|
|
|
|
* nameserver, so all interaction with the workspace manager
|
|
|
|
|
* and/or other applications will fail.
|
|
|
|
|
* Give the user a chance to keep on going anyway.
|
|
|
|
|
*/
|
1999-01-09 06:49:48 +00:00
|
|
|
|
result = NSRunAlertPanel(appName,
|
|
|
|
|
@"Unable to register application with ANY name",
|
|
|
|
|
@"Abort", @"Continue", nil);
|
|
|
|
|
if (result == NSAlertDefaultReturn)
|
2005-10-09 06:39:08 +00:00
|
|
|
|
{
|
|
|
|
|
registered = YES;
|
|
|
|
|
}
|
1999-01-09 06:49:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (registered == NO)
|
2005-10-09 06:39:08 +00:00
|
|
|
|
{
|
|
|
|
|
[[NSApplication sharedApplication] terminate: self];
|
|
|
|
|
}
|
1999-01-09 06:49:48 +00:00
|
|
|
|
}
|
2005-10-09 06:39:08 +00:00
|
|
|
|
ASSIGN(_port, appName);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-06-06 04:05:05 +00:00
|
|
|
|
/**
|
1998-12-01 20:54:23 +00:00
|
|
|
|
* Register send and return types that an object can handle - we keep
|
|
|
|
|
* a note of all the possible combinations -
|
|
|
|
|
* 'returnInfo' is a set of all the return types that can be handled
|
|
|
|
|
* without a send.
|
2005-06-06 04:05:05 +00:00
|
|
|
|
* 'combinations' is a dictionary of all send types, with the associated
|
1998-12-01 20:54:23 +00:00
|
|
|
|
* values being sets of possible return types.
|
|
|
|
|
*/
|
|
|
|
|
- (void) registerSendTypes: (NSArray *)sendTypes
|
|
|
|
|
returnTypes: (NSArray *)returnTypes
|
|
|
|
|
{
|
|
|
|
|
BOOL didChange = NO;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < [sendTypes count]; i++)
|
|
|
|
|
{
|
|
|
|
|
NSString *sendType = [sendTypes objectAtIndex: i];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
NSMutableSet *returnSet = [_combinations objectForKey: sendType];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
|
|
|
|
if (returnSet == nil)
|
|
|
|
|
{
|
|
|
|
|
returnSet = [NSMutableSet setWithCapacity: [returnTypes count]];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
[_combinations setObject: returnSet forKey: sendType];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
[returnSet addObjectsFromArray: returnTypes];
|
|
|
|
|
didChange = YES;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
unsigned count = [returnSet count];
|
|
|
|
|
|
|
|
|
|
[returnSet addObjectsFromArray: returnTypes];
|
|
|
|
|
if ([returnSet count] != count)
|
|
|
|
|
{
|
|
|
|
|
didChange = YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-06 13:26:27 +00:00
|
|
|
|
i = [_returnInfo count];
|
|
|
|
|
[_returnInfo addObjectsFromArray: returnTypes];
|
|
|
|
|
if ([_returnInfo count] != i)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
didChange = YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (didChange)
|
|
|
|
|
{
|
2007-12-07 12:31:51 +00:00
|
|
|
|
/* Types have changed, so we need to enable/disable items in the
|
|
|
|
|
* services menu depending on what types they support.
|
|
|
|
|
*/
|
1998-12-01 20:54:23 +00:00
|
|
|
|
[self rebuildServices];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSMenu*) servicesMenu
|
|
|
|
|
{
|
2002-01-06 13:26:27 +00:00
|
|
|
|
return _servicesMenu;
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) servicesProvider
|
|
|
|
|
{
|
1999-04-08 20:42:46 +00:00
|
|
|
|
return [GSListener servicesProvider];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) setServicesMenu: (NSMenu*)aMenu
|
|
|
|
|
{
|
2002-01-06 13:26:27 +00:00
|
|
|
|
ASSIGN(_servicesMenu, aMenu);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
[self rebuildServicesMenu];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) setServicesProvider: (id)anObject
|
|
|
|
|
{
|
1999-04-08 20:42:46 +00:00
|
|
|
|
[GSListener setServicesProvider: anObject];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (int) setShowsServicesMenuItem: (NSString*)item to: (BOOL)enable
|
|
|
|
|
{
|
|
|
|
|
NSData *d;
|
|
|
|
|
|
|
|
|
|
[self loadServices];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
if (_allDisabled == nil)
|
2003-06-22 12:04:47 +00:00
|
|
|
|
{
|
|
|
|
|
_allDisabled = [[NSMutableSet alloc] initWithCapacity: 1];
|
|
|
|
|
}
|
1998-12-01 20:54:23 +00:00
|
|
|
|
if (enable)
|
2003-06-22 12:04:47 +00:00
|
|
|
|
{
|
|
|
|
|
[_allDisabled removeObject: item];
|
|
|
|
|
}
|
1998-12-01 20:54:23 +00:00
|
|
|
|
else
|
2003-06-22 12:04:47 +00:00
|
|
|
|
{
|
|
|
|
|
[_allDisabled addObject: item];
|
|
|
|
|
}
|
2002-01-06 13:26:27 +00:00
|
|
|
|
d = [NSSerializer serializePropertyList: [_allDisabled allObjects]];
|
|
|
|
|
if ([d writeToFile: _disabledPath atomically: YES] == YES)
|
2003-06-22 12:04:47 +00:00
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
1998-12-01 20:54:23 +00:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
1998-12-04 10:44:02 +00:00
|
|
|
|
- (BOOL) showsServicesMenuItem: (NSString*)item
|
1998-12-03 13:27:08 +00:00
|
|
|
|
{
|
|
|
|
|
[self loadServices];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
if ([_allDisabled member: item] == nil)
|
1998-12-03 13:27:08 +00:00
|
|
|
|
return YES;
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-21 21:43 Alexander Malmberg <alexander@malmberg.org>
Various whitespace cleanups, comment type fixes, and changes
to avoid warnings from recent versions of gcc.
* Headers/Additions/GNUstepGUI/GSToolbar.h (-_toolbars): Declare.
* Source/NSWindow+Toolbar.m: Remove conflicting declaration of
[NSToolbar -_toolbars].
* Headers/Additions/GNUstepGUI/GSServicesManager.h,
Source/GSServicesMananger.m (-item2title:, -validateMenuItem:):
Adjust argument types.
* Headers/AppKit/NSMenu.h (-validateMenuItem:): Adjust argument
type.
* Source/NSTextView.m (-sizeToFit): Don't use size uninitialized
if neither resizable flags is set.
(-insertText:): Adjust argument type.
* Headers/AppKit/NSResponder.h, Source/NSResponder.m (-insertText:):
Adjust argument type. Document.
* Headers/AppKit/NSView.h: Change type of ivar _window to NSWindow *.
* Source/GSTitleView.m (-mouseDown:): Always initialize
startWindowOrigin.
* Source/NSApplication.m (-setApplicationIconImage:): Add casts
to avoid warnings.
* Source/NSCell.m (-cellSize): Add default: case.
* Source/NSPasteboard.m
([GSFiltered -pasteboard:provideDataForType:]): Detect and warn if we
can't find a filter that will get us the desired type.
* Source/NSProgressIndicator.m: Comment out unused variable 'images'.
* Source/NSBezierPath.m: Declare GSBezierPath fully before using it.
(-bezierPathByFlatteningPath, -bezierPathByReversingPath): Make sure
variables are always initialized.
* Source/NSMenuView.m,
* Source/NSPrintOperation.m,
* Source/NSSplitView.m,
* Source/NSTableHeaderView.m: Make sure variables are always
initialized.
* Source/NSBox.m,
* Source/NSImageview.m,
* Source/NSText.m,
* Source/NSTextStorage.m: Add missing includes.
* Source/GSKeyBindingTable.m,
* Source/GSLayoutManager.m,
* Source/NSBitmapImageRep+PNM.m,
* Source/NSBundleAdditions.m,
* Source/NSLayoutManager.m,
* Source/nsimage-tiff.h,
* Source/tiff.m,
* Headers/Additions/GNUstepGUI/GSDisplayServer.h,
* Source/GSDisplayServer.m: Change signedness of various variables.
* Source/NSPanel.m (-sendEvent:): Remove.
* Source/NSWindow.m (-becomesKeyOnlyIfNeeded): New method.
(-_sendEvent:becomesKeyOnlyIfNeeded:): Remove. Move code ...
(-sendEvent:): ... here. Use -becomesKeyOnlyIfNeeded instead
of the argument.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@20590 72102866-910b-0410-8b05-ffd578937521
2005-01-21 20:39:18 +00:00
|
|
|
|
- (BOOL) validateMenuItem: (id<NSMenuItem>)item
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
NSString *title = [self item2title: item];
|
2002-01-06 13:26:27 +00:00
|
|
|
|
NSDictionary *info = [_title2info objectForKey: title];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
NSArray *sendTypes = [info objectForKey: @"NSSendTypes"];
|
|
|
|
|
NSArray *returnTypes = [info objectForKey: @"NSReturnTypes"];
|
|
|
|
|
unsigned i, j;
|
|
|
|
|
unsigned es = [sendTypes count];
|
|
|
|
|
unsigned er = [returnTypes count];
|
2006-07-04 21:31:49 +00:00
|
|
|
|
NSResponder *resp = [[_application keyWindow] firstResponder];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
|
|
|
|
/*
|
2002-06-10 23:48:09 +00:00
|
|
|
|
* If the menu item is not in our map, it must be the item containing
|
|
|
|
|
* a sub-menu - so we see if any item in the submenu is valid.
|
1998-12-01 20:54:23 +00:00
|
|
|
|
*/
|
|
|
|
|
if (title == nil)
|
|
|
|
|
{
|
2002-06-10 23:48:09 +00:00
|
|
|
|
NSMenu *sub;
|
|
|
|
|
|
|
|
|
|
if (![item isKindOfClass: [NSMenuItem class]])
|
|
|
|
|
return NO;
|
|
|
|
|
|
|
|
|
|
sub = [item submenu];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
|
|
|
|
if (sub && [sub isKindOfClass: [NSMenu class]])
|
|
|
|
|
{
|
|
|
|
|
NSArray *a = [sub itemArray];
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < [a count]; i++)
|
|
|
|
|
{
|
|
|
|
|
if ([self validateMenuItem: [a objectAtIndex: i]] == YES)
|
|
|
|
|
{
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2002-06-10 23:48:09 +00:00
|
|
|
|
* The item corresponds to one of our services - so we check to see if
|
1998-12-01 20:54:23 +00:00
|
|
|
|
* there is anything that can deal with it.
|
|
|
|
|
*/
|
|
|
|
|
if (es == 0)
|
|
|
|
|
{
|
|
|
|
|
if (er == 0)
|
|
|
|
|
{
|
|
|
|
|
if ([resp validRequestorForSendType: nil
|
|
|
|
|
returnType: nil] != nil)
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (j = 0; j < er; j++)
|
|
|
|
|
{
|
|
|
|
|
NSString *returnType;
|
|
|
|
|
|
|
|
|
|
returnType = [returnTypes objectAtIndex: j];
|
|
|
|
|
if ([resp validRequestorForSendType: nil
|
|
|
|
|
returnType: returnType] != nil)
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < es; i++)
|
|
|
|
|
{
|
|
|
|
|
NSString *sendType;
|
|
|
|
|
|
|
|
|
|
sendType = [sendTypes objectAtIndex: i];
|
|
|
|
|
|
|
|
|
|
if (er == 0)
|
|
|
|
|
{
|
|
|
|
|
if ([resp validRequestorForSendType: sendType
|
|
|
|
|
returnType: nil] != nil)
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (j = 0; j < er; j++)
|
|
|
|
|
{
|
|
|
|
|
NSString *returnType;
|
|
|
|
|
|
|
|
|
|
returnType = [returnTypes objectAtIndex: j];
|
|
|
|
|
if ([resp validRequestorForSendType: sendType
|
|
|
|
|
returnType: returnType] != nil)
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) updateServicesMenu
|
|
|
|
|
{
|
2002-01-06 13:26:27 +00:00
|
|
|
|
if (_servicesMenu && [[_application mainMenu] autoenablesItems])
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
NSArray *a;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
2002-01-06 13:26:27 +00:00
|
|
|
|
a = [_servicesMenu itemArray];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < [a count]; i++)
|
|
|
|
|
{
|
2002-06-10 23:48:09 +00:00
|
|
|
|
NSMenuItem *item = [a objectAtIndex: i];
|
|
|
|
|
BOOL wasEnabled = [item isEnabled];
|
1998-12-14 15:59:51 +00:00
|
|
|
|
BOOL shouldBeEnabled;
|
2002-06-10 23:48:09 +00:00
|
|
|
|
NSString *title = [self item2title: item];
|
1998-12-14 15:59:51 +00:00
|
|
|
|
|
|
|
|
|
/*
|
2002-06-10 23:48:09 +00:00
|
|
|
|
* If there is no title mapping, this item must be a
|
|
|
|
|
* submenu - so we check the submenu items.
|
2003-10-29 01:22:50 +00:00
|
|
|
|
*
|
|
|
|
|
* We always enable the submenu item itself. We do this
|
|
|
|
|
* to prevent confusion (if the user is trying to use
|
|
|
|
|
* a disabled item, it's clearer to show that item disabled
|
|
|
|
|
* than to hide it in a disabled submenu), and to encourage
|
|
|
|
|
* the user to explore the interface (it makes it possible
|
|
|
|
|
* to browse the service list at any time).
|
1998-12-14 15:59:51 +00:00
|
|
|
|
*/
|
2002-06-10 23:48:09 +00:00
|
|
|
|
if (title == nil && [[item submenu] isKindOfClass: [NSMenu class]])
|
1998-12-14 15:59:51 +00:00
|
|
|
|
{
|
2002-06-10 23:48:09 +00:00
|
|
|
|
NSArray *sub = [[item submenu] itemArray];
|
2003-04-09 16:12:22 +00:00
|
|
|
|
unsigned j;
|
1998-12-14 15:59:51 +00:00
|
|
|
|
|
2003-10-29 01:22:50 +00:00
|
|
|
|
shouldBeEnabled = YES;
|
1998-12-14 15:59:51 +00:00
|
|
|
|
for (j = 0; j < [sub count]; j++)
|
|
|
|
|
{
|
2002-06-10 23:48:09 +00:00
|
|
|
|
NSMenuItem *subitem = [sub objectAtIndex: j];
|
|
|
|
|
BOOL subWasEnabled = [subitem isEnabled];
|
1998-12-14 15:59:51 +00:00
|
|
|
|
BOOL subShouldBeEnabled = NO;
|
|
|
|
|
|
2002-06-10 23:48:09 +00:00
|
|
|
|
if ([self validateMenuItem: subitem] == YES)
|
1998-12-14 15:59:51 +00:00
|
|
|
|
{
|
|
|
|
|
subShouldBeEnabled = YES;
|
|
|
|
|
}
|
|
|
|
|
if (subWasEnabled != subShouldBeEnabled)
|
|
|
|
|
{
|
2002-06-10 23:48:09 +00:00
|
|
|
|
[subitem setEnabled: subShouldBeEnabled];
|
1998-12-14 15:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2009-02-18 09:40:55 +00:00
|
|
|
|
{
|
|
|
|
|
shouldBeEnabled = [self validateMenuItem: item];
|
|
|
|
|
}
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
|
|
|
|
if (wasEnabled != shouldBeEnabled)
|
|
|
|
|
{
|
2002-06-10 23:48:09 +00:00
|
|
|
|
[item setEnabled: shouldBeEnabled];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1998-12-10 21:14:52 +00:00
|
|
|
|
@end /* GSServicesManager */
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
|
|
|
|
|
2003-06-22 09:29:34 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p>Establishes an NSConnection to the application listening at port
|
|
|
|
|
* (by convention usually the application name), launching appName
|
|
|
|
|
* if necessary. Returns the proxy to the remote application, or nil
|
|
|
|
|
* on failure.
|
|
|
|
|
* </p>
|
2003-07-14 12:27:42 +00:00
|
|
|
|
* <p>The value of port specifies the name of the distributed objects
|
|
|
|
|
* service to which the connection is to be made. If this is nil
|
|
|
|
|
* it will be inferred from appName ... by convention, applications
|
|
|
|
|
* use their own name (minus any path or extension) for this.
|
|
|
|
|
* </p>
|
2005-11-20 20:29:06 +00:00
|
|
|
|
* <p>If appName is nil or cannot be launched, this attempts to locate any
|
|
|
|
|
* application in a standard location whose name matches port and launch
|
|
|
|
|
* that application.
|
|
|
|
|
* </p>
|
2003-07-14 12:27:42 +00:00
|
|
|
|
* <p>The value of expire provides a timeout in case the application cannot
|
|
|
|
|
* be contacted promptly. If it is omitted, a thirty second timeout is
|
|
|
|
|
* used.
|
|
|
|
|
* </p>
|
2003-06-22 09:29:34 +00:00
|
|
|
|
*/
|
1999-01-07 15:52:42 +00:00
|
|
|
|
id
|
|
|
|
|
GSContactApplication(NSString *appName, NSString *port, NSDate *expire)
|
|
|
|
|
{
|
2001-05-04 04:11:53 +00:00
|
|
|
|
id app;
|
1999-01-07 15:52:42 +00:00
|
|
|
|
|
2003-07-14 12:27:42 +00:00
|
|
|
|
if (port == nil)
|
|
|
|
|
{
|
|
|
|
|
port = [[appName lastPathComponent] stringByDeletingPathExtension];
|
|
|
|
|
}
|
|
|
|
|
if (expire == nil)
|
|
|
|
|
{
|
2003-07-14 13:10:00 +00:00
|
|
|
|
expire = [NSDate dateWithTimeIntervalSinceNow: 30.0];
|
2003-07-14 12:27:42 +00:00
|
|
|
|
}
|
2001-04-18 09:25:39 +00:00
|
|
|
|
if (providerName != nil && [port isEqual: providerName] == YES)
|
1999-01-07 15:52:42 +00:00
|
|
|
|
{
|
2001-05-04 04:11:53 +00:00
|
|
|
|
app = [GSListener listener]; // Contect our own listener.
|
1999-01-07 15:52:42 +00:00
|
|
|
|
}
|
2001-04-18 09:25:39 +00:00
|
|
|
|
else
|
1999-01-07 15:52:42 +00:00
|
|
|
|
{
|
2001-04-18 09:25:39 +00:00
|
|
|
|
NS_DURING
|
|
|
|
|
{
|
|
|
|
|
app = [NSConnection rootProxyForConnectionWithRegisteredName: port
|
|
|
|
|
host: @""];
|
|
|
|
|
}
|
|
|
|
|
NS_HANDLER
|
|
|
|
|
{
|
|
|
|
|
return nil; /* Fatal error in DO */
|
|
|
|
|
}
|
|
|
|
|
NS_ENDHANDLER
|
1999-01-07 15:52:42 +00:00
|
|
|
|
}
|
|
|
|
|
if (app == nil)
|
|
|
|
|
{
|
2005-11-20 20:29:06 +00:00
|
|
|
|
if (appName == nil
|
|
|
|
|
|| [[NSWorkspace sharedWorkspace] launchApplication: appName] == NO)
|
1999-01-07 15:52:42 +00:00
|
|
|
|
{
|
2005-11-20 20:29:06 +00:00
|
|
|
|
if (port == nil
|
|
|
|
|
|| [[NSWorkspace sharedWorkspace] launchApplication: port] == NO)
|
|
|
|
|
{
|
|
|
|
|
return nil; /* Unable to launch. */
|
|
|
|
|
}
|
1999-01-07 15:52:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NS_DURING
|
|
|
|
|
{
|
|
|
|
|
app = [NSConnection
|
|
|
|
|
rootProxyForConnectionWithRegisteredName: port
|
|
|
|
|
host: @""];
|
|
|
|
|
while (app == nil && [expire timeIntervalSinceNow] > 0.1)
|
|
|
|
|
{
|
|
|
|
|
NSRunLoop *loop = [NSRunLoop currentRunLoop];
|
|
|
|
|
NSDate *next;
|
|
|
|
|
|
|
|
|
|
[NSTimer scheduledTimerWithTimeInterval: 0.1
|
|
|
|
|
invocation: nil
|
|
|
|
|
repeats: NO];
|
|
|
|
|
next = [NSDate dateWithTimeIntervalSinceNow: 0.2];
|
|
|
|
|
[loop runUntilDate: next];
|
|
|
|
|
app = [NSConnection
|
|
|
|
|
rootProxyForConnectionWithRegisteredName: port
|
|
|
|
|
host: @""];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NS_HANDLER
|
|
|
|
|
{
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
NS_ENDHANDLER
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return app;
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-18 13:16:55 +00:00
|
|
|
|
static NSDictionary *
|
|
|
|
|
serviceFromAnyLocalizedTitle(NSString *title)
|
2003-10-21 09:35:08 +00:00
|
|
|
|
{
|
2003-10-28 10:19:51 +00:00
|
|
|
|
NSDictionary *allServices;
|
|
|
|
|
NSEnumerator *e1;
|
|
|
|
|
NSDictionary *service;
|
2003-10-21 09:35:08 +00:00
|
|
|
|
|
|
|
|
|
allServices = [manager menuServices];
|
2003-10-28 10:19:51 +00:00
|
|
|
|
if (allServices == nil)
|
|
|
|
|
{
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
2003-10-21 09:35:08 +00:00
|
|
|
|
|
2003-10-28 10:19:51 +00:00
|
|
|
|
if ([allServices objectForKey: title] != nil)
|
|
|
|
|
{
|
|
|
|
|
return [allServices objectForKey: title];
|
|
|
|
|
}
|
2003-10-21 09:35:08 +00:00
|
|
|
|
e1 = [allServices objectEnumerator];
|
2003-10-28 10:19:51 +00:00
|
|
|
|
while ((service = [e1 nextObject]) != nil)
|
2003-10-21 09:35:08 +00:00
|
|
|
|
{
|
2003-10-28 10:19:51 +00:00
|
|
|
|
NSDictionary *menuItems;
|
|
|
|
|
NSString *itemName;
|
|
|
|
|
NSEnumerator *e2;
|
2003-10-21 09:35:08 +00:00
|
|
|
|
|
|
|
|
|
menuItems = [service objectForKey: @"NSMenuItem"];
|
2003-10-28 10:19:51 +00:00
|
|
|
|
if (menuItems == nil)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2003-10-21 09:35:08 +00:00
|
|
|
|
e2 = [menuItems objectEnumerator];
|
2003-10-28 10:19:51 +00:00
|
|
|
|
while ((itemName = [e2 nextObject]) != nil)
|
|
|
|
|
{
|
|
|
|
|
if ([itemName isEqualToString: title] == YES)
|
|
|
|
|
{
|
|
|
|
|
return service;
|
|
|
|
|
}
|
|
|
|
|
}
|
2003-10-21 09:35:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
2003-06-30 16:17:45 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p>Given the name of a serviceItem, and some data in a pasteboard
|
|
|
|
|
* this function sends the data to the service provider (launching
|
|
|
|
|
* another application if necessary) and retrieves the result of
|
|
|
|
|
* the service in the pastebaord.
|
|
|
|
|
* </p>
|
|
|
|
|
* Returns YES on success, NO otherwise.
|
|
|
|
|
*/
|
1998-12-01 20:54:23 +00:00
|
|
|
|
BOOL
|
2003-06-30 16:17:45 +00:00
|
|
|
|
NSPerformService(NSString *serviceItem, NSPasteboard *pboard)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
{
|
|
|
|
|
NSDictionary *service;
|
|
|
|
|
NSString *port;
|
|
|
|
|
NSString *timeout;
|
|
|
|
|
double seconds;
|
|
|
|
|
NSDate *finishBy;
|
|
|
|
|
NSString *appPath;
|
|
|
|
|
id provider;
|
|
|
|
|
NSString *message;
|
|
|
|
|
NSString *selName;
|
|
|
|
|
NSString *userData;
|
|
|
|
|
NSString *error = nil;
|
|
|
|
|
|
2004-09-18 13:16:55 +00:00
|
|
|
|
service = serviceFromAnyLocalizedTitle(serviceItem);
|
2003-06-30 16:17:45 +00:00
|
|
|
|
if (service == nil)
|
2003-06-27 14:47:11 +00:00
|
|
|
|
{
|
2003-06-30 16:17:45 +00:00
|
|
|
|
NSRunAlertPanel(nil,
|
|
|
|
|
@"No service matching '%@'",
|
|
|
|
|
@"Continue", nil, nil,
|
|
|
|
|
serviceItem);
|
|
|
|
|
return NO; /* No matching service. */
|
1998-12-14 15:59:51 +00:00
|
|
|
|
}
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
|
|
|
|
port = [service objectForKey: @"NSPortName"];
|
|
|
|
|
timeout = [service objectForKey: @"NSTimeout"];
|
|
|
|
|
if (timeout && [timeout floatValue] > 100)
|
|
|
|
|
{
|
|
|
|
|
seconds = [timeout floatValue] / 1000.0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
seconds = 30.0;
|
|
|
|
|
}
|
|
|
|
|
finishBy = [NSDate dateWithTimeIntervalSinceNow: seconds];
|
|
|
|
|
appPath = [service objectForKey: @"ServicePath"];
|
|
|
|
|
userData = [service objectForKey: @"NSUserData"];
|
2003-06-30 16:17:45 +00:00
|
|
|
|
message = [service objectForKey: @"NSMessage"];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
selName = [message stringByAppendingString: @":userData:error:"];
|
|
|
|
|
|
|
|
|
|
/*
|
2001-05-04 04:11:53 +00:00
|
|
|
|
* Locate the service provider ... this will be a proxy to the remote
|
|
|
|
|
* object, or a local object (if we provide the service ourself)
|
1998-12-01 20:54:23 +00:00
|
|
|
|
*/
|
1999-01-07 15:52:42 +00:00
|
|
|
|
provider = GSContactApplication(appPath, port, finishBy);
|
|
|
|
|
if (provider == nil)
|
1998-12-14 15:59:51 +00:00
|
|
|
|
{
|
2003-06-30 16:17:45 +00:00
|
|
|
|
NSRunAlertPanel(nil,
|
|
|
|
|
@"Failed to contact service provider for '%@'",
|
|
|
|
|
@"Continue", nil, nil,
|
|
|
|
|
serviceItem);
|
1998-12-14 15:59:51 +00:00
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-04 04:11:53 +00:00
|
|
|
|
/*
|
|
|
|
|
* If the service provider is a remote object, we can set timeouts on
|
|
|
|
|
* the NSConnection so we don't hang waiting for it to reply.
|
|
|
|
|
*/
|
2004-09-27 15:53:04 +00:00
|
|
|
|
/*
|
|
|
|
|
This check for a remote object is ugly. When GSListener is reworked,
|
|
|
|
|
this should be improved.
|
|
|
|
|
|
|
|
|
|
For now, we can't use -isProxy since GSListener is a proxy, and we can't
|
|
|
|
|
use -isKindOfClass: since it gets forwarded. Fortunately, -class isn't
|
|
|
|
|
forwarded, so that's what we use.
|
|
|
|
|
|
|
|
|
|
(Note, though, that we can't even use
|
|
|
|
|
[provider class] == [GSListener class] since [GSListener -class] returns
|
|
|
|
|
NULL instead of the real class.)
|
|
|
|
|
*/
|
|
|
|
|
if ([provider class] == [NSDistantObject class])
|
2001-05-04 04:11:53 +00:00
|
|
|
|
{
|
|
|
|
|
NSConnection *connection;
|
1998-12-01 20:54:23 +00:00
|
|
|
|
|
2001-05-04 04:11:53 +00:00
|
|
|
|
connection = [(NSDistantObject*)provider connectionForProxy];
|
|
|
|
|
seconds = [finishBy timeIntervalSinceNow];
|
|
|
|
|
[connection setRequestTimeout: seconds];
|
|
|
|
|
[connection setReplyTimeout: seconds];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* At last, we ask for the service to be performed.
|
2003-07-14 12:27:42 +00:00
|
|
|
|
* We create an untyped selector matching the message name we have,
|
|
|
|
|
* Using that, we get a method signature from the provider, and
|
|
|
|
|
* take the type information from that to make a fully typed
|
|
|
|
|
* selector, with which we can create and use an invocation.
|
2001-05-04 04:11:53 +00:00
|
|
|
|
*/
|
1998-12-01 20:54:23 +00:00
|
|
|
|
NS_DURING
|
|
|
|
|
{
|
2010-02-25 09:08:35 +00:00
|
|
|
|
SEL sel = NSSelectorFromString(selName);
|
2003-07-14 12:27:42 +00:00
|
|
|
|
NSMethodSignature *sig = [provider methodSignatureForSelector: sel];
|
|
|
|
|
|
|
|
|
|
if (sig != nil)
|
|
|
|
|
{
|
|
|
|
|
NSInvocation *inv;
|
|
|
|
|
NSString **errPtr = &error;
|
|
|
|
|
|
|
|
|
|
inv = [NSInvocation invocationWithMethodSignature: sig];
|
|
|
|
|
[inv setTarget: provider];
|
|
|
|
|
[inv setSelector: sel];
|
|
|
|
|
[inv setArgument: (void*)&pboard atIndex: 2];
|
|
|
|
|
[inv setArgument: (void*)&userData atIndex: 3];
|
|
|
|
|
[inv setArgument: (void*)&errPtr atIndex: 4];
|
|
|
|
|
[inv invoke];
|
|
|
|
|
}
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
NS_HANDLER
|
|
|
|
|
{
|
1998-12-14 15:59:51 +00:00
|
|
|
|
error = [NSString stringWithFormat: @"%@", [localException reason]];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
NS_ENDHANDLER
|
|
|
|
|
|
|
|
|
|
if (error != nil)
|
|
|
|
|
{
|
2003-06-30 16:17:45 +00:00
|
|
|
|
NSRunAlertPanel(nil,
|
|
|
|
|
@"Failed to contact service provider for '%@': %@",
|
|
|
|
|
@"Continue", nil, nil,
|
|
|
|
|
serviceItem, error);
|
1998-12-01 20:54:23 +00:00
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
2003-06-22 12:04:47 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p>Controls whether the item name should be included in the services menu.
|
|
|
|
|
* </p>
|
|
|
|
|
* <p>If enabled is YES then the services menu for each application will
|
|
|
|
|
* include the named item, if enabled is NO then the service will not be
|
|
|
|
|
* shown in application services menus.
|
|
|
|
|
* </p>
|
|
|
|
|
* <p>Returns 0 if the setting is successfuly changed. Non-zero otherwise.
|
|
|
|
|
* </p>
|
|
|
|
|
*/
|
1998-12-01 20:54:23 +00:00
|
|
|
|
int
|
|
|
|
|
NSSetShowsServicesMenuItem(NSString *name, BOOL enabled)
|
|
|
|
|
{
|
1998-12-10 21:14:52 +00:00
|
|
|
|
return [[GSServicesManager manager] setShowsServicesMenuItem: name
|
2003-06-22 12:04:47 +00:00
|
|
|
|
to: enabled];
|
1998-12-01 20:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-06-22 12:04:47 +00:00
|
|
|
|
/**
|
|
|
|
|
* Returns a flag indicating whether the named service is supposed to be
|
|
|
|
|
* displayed in application services menus.
|
|
|
|
|
*/
|
1998-12-03 13:27:08 +00:00
|
|
|
|
BOOL
|
2003-06-22 12:04:47 +00:00
|
|
|
|
NSShowsServicesMenuItem(NSString *name)
|
1998-12-03 13:27:08 +00:00
|
|
|
|
{
|
1998-12-10 21:14:52 +00:00
|
|
|
|
return [[GSServicesManager manager] showsServicesMenuItem: name];
|
1998-12-03 13:27:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-06-22 12:04:47 +00:00
|
|
|
|
/**
|
|
|
|
|
* A services providing application may use this to update the list of
|
2003-06-27 11:13:30 +00:00
|
|
|
|
* services it provides.<br />
|
|
|
|
|
* In order to update the services advertised, the application must
|
|
|
|
|
* create a <em>.service</em> bundle and place it in
|
|
|
|
|
* <code>~/Library/Services</code> before invoking this function.
|
2003-06-22 12:04:47 +00:00
|
|
|
|
*/
|
2001-08-14 22:39:06 +00:00
|
|
|
|
void
|
|
|
|
|
NSUpdateDynamicServices(void)
|
|
|
|
|
{
|
2010-02-26 05:20:59 +00:00
|
|
|
|
/* Get the workspace manager to make sure that cached service info is
|
|
|
|
|
* up to date.
|
|
|
|
|
*/
|
|
|
|
|
[[NSWorkspace sharedWorkspace] findApplications];
|
|
|
|
|
|
|
|
|
|
/* Reload service information from disk cache.
|
|
|
|
|
*/
|
2001-08-14 22:39:06 +00:00
|
|
|
|
[[GSServicesManager manager] loadServices];
|
|
|
|
|
}
|