mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-06-04 16:00:41 +00:00
Various tidyups and services improvements.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@17210 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
b0e1cf1da8
commit
126ecf308c
6 changed files with 758 additions and 450 deletions
11
ChangeLog
11
ChangeLog
|
@ -1,3 +1,14 @@
|
||||||
|
2003-07-14 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
|
* Headers/gnustep/gui/GSServicesManager.h: removed a function
|
||||||
|
* Headers/gnustep/gui/NSApplication.h: and added it here so it's
|
||||||
|
public. Also fixed some argument name missmatches.
|
||||||
|
* Source/GSServicesManager.m: Some fixes for recent changes to
|
||||||
|
DO in base library ... also make listener even safer.
|
||||||
|
* Source/NSPasteboard.m: Improve documentation and change pasteboards
|
||||||
|
to always be sent over DO bycopy.
|
||||||
|
* Tools/example.m: Set error response to nil when there is no error.
|
||||||
|
|
||||||
2003-07-12 18:20 Alexander Malmberg <alexander@malmberg.org>
|
2003-07-12 18:20 Alexander Malmberg <alexander@malmberg.org>
|
||||||
|
|
||||||
* Headers/gnustep/gui/NSTextView.h, Source/NSTextView.m: Add
|
* Headers/gnustep/gui/NSTextView.h, Source/NSTextView.m: Add
|
||||||
|
|
|
@ -86,7 +86,5 @@
|
||||||
- (void) updateServicesMenu;
|
- (void) updateServicesMenu;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
id GSContactApplication(NSString *appName, NSString *port, NSDate *expire);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -411,16 +411,21 @@ APPKIT_EXPORT NSString *NSApplicationWillUpdateNotification;
|
||||||
* Determine Whether an Item Is Included in Services Menus
|
* Determine Whether an Item Is Included in Services Menus
|
||||||
*/
|
*/
|
||||||
APPKIT_EXPORT int
|
APPKIT_EXPORT int
|
||||||
NSSetShowsServicesMenuItem(NSString *item, BOOL showService);
|
NSSetShowsServicesMenuItem(NSString *name, BOOL enabled);
|
||||||
|
|
||||||
APPKIT_EXPORT BOOL
|
APPKIT_EXPORT BOOL
|
||||||
NSShowsServicesMenuItem(NSString *item);
|
NSShowsServicesMenuItem(NSString *name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Programmatically Invoke a Service
|
* Programmatically Invoke a Service
|
||||||
*/
|
*/
|
||||||
APPKIT_EXPORT BOOL
|
APPKIT_EXPORT BOOL
|
||||||
NSPerformService(NSString *item, NSPasteboard *pboard);
|
NSPerformService(NSString *serviceItem, NSPasteboard *pboard);
|
||||||
|
|
||||||
|
#ifndef NO_GNUSTEP
|
||||||
|
APPKIT_EXPORT id
|
||||||
|
GSContactApplication(NSString *appName, NSString *port, NSDate *expire);
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Force Services Menu to Update Based on New Services
|
* Force Services Menu to Update Based on New Services
|
||||||
|
|
|
@ -67,7 +67,7 @@ static GSServicesManager *manager = nil;
|
||||||
* messing with us. This is responsible for forwarding service
|
* messing with us. This is responsible for forwarding service
|
||||||
* requests and other communications with the outside world.
|
* requests and other communications with the outside world.
|
||||||
*/
|
*/
|
||||||
@interface GSListener : NSObject
|
@interface GSListener : NSProxy
|
||||||
+ (id) connectionBecameInvalid: (NSNotification*)notification;
|
+ (id) connectionBecameInvalid: (NSNotification*)notification;
|
||||||
+ (GSListener*) listener;
|
+ (GSListener*) listener;
|
||||||
+ (id) servicesProvider;
|
+ (id) servicesProvider;
|
||||||
|
@ -77,13 +77,10 @@ static GSServicesManager *manager = nil;
|
||||||
- (void) release;
|
- (void) release;
|
||||||
- (id) retain;
|
- (id) retain;
|
||||||
- (id) self;
|
- (id) self;
|
||||||
- (void) performService: (NSString*)name
|
|
||||||
withPasteboard: (NSPasteboard*)pb
|
|
||||||
userData: (NSString*)ud
|
|
||||||
error: (NSString**)e;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
static NSConnection *listenerConnection = nil;
|
static NSConnection *listenerConnection = nil;
|
||||||
|
static NSMutableArray *listeners = nil;
|
||||||
static GSListener *listener = nil;
|
static GSListener *listener = nil;
|
||||||
static id servicesProvider = nil;
|
static id servicesProvider = nil;
|
||||||
static NSString *providerName = nil;
|
static NSString *providerName = nil;
|
||||||
|
@ -159,10 +156,10 @@ NSRegisterServicesProvider(id provider, NSString *name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The GSListener class exists as a proxy to forward messages to
|
* The GSListener class exists as a proxy to forward messages to
|
||||||
* service provider objects. It implements very few methods and
|
* service provider objects. It implements very few methods and
|
||||||
* those that it does implement are generally designed to defeat
|
* those that it does implement are generally designed to defeat
|
||||||
* any attack by a malicious program.
|
* any attack by a malicious program.
|
||||||
*/
|
*/
|
||||||
@implementation GSListener
|
@implementation GSListener
|
||||||
|
|
||||||
|
@ -179,15 +176,39 @@ NSRegisterServicesProvider(id provider, NSString *name)
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (void) initialize
|
||||||
|
{
|
||||||
|
static BOOL beenHere = NO;
|
||||||
|
|
||||||
|
if (beenHere == NO)
|
||||||
|
{
|
||||||
|
beenHere = YES;
|
||||||
|
listeners = [NSMutableArray new];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+ (GSListener*) listener
|
+ (GSListener*) listener
|
||||||
{
|
{
|
||||||
if (listener == nil)
|
if (listener == nil)
|
||||||
{
|
{
|
||||||
listener = (id)NSAllocateObject(self, 0, NSDefaultMallocZone());
|
listener = (id)NSAllocateObject(self, 0, NSDefaultMallocZone());
|
||||||
|
[listeners addObject: listener];
|
||||||
}
|
}
|
||||||
return listener;
|
return listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)];
|
||||||
|
return get_imp(GSObjCClass(self), aSelector);
|
||||||
|
}
|
||||||
|
|
||||||
+ (id) servicesProvider
|
+ (id) servicesProvider
|
||||||
{
|
{
|
||||||
return servicesProvider;
|
return servicesProvider;
|
||||||
|
@ -204,6 +225,11 @@ NSRegisterServicesProvider(id provider, NSString *name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (id) autorelease
|
||||||
|
{
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
- (Class) class
|
- (Class) class
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -213,47 +239,13 @@ NSRegisterServicesProvider(id provider, NSString *name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Obsolete ... prefer forwardInvocation now.
|
* Selectively forwards those messages which are known to be safe.
|
||||||
*/
|
*/
|
||||||
- forward: (SEL)aSel :(arglist_t)frame
|
|
||||||
{
|
|
||||||
NSString *selName = NSStringFromSelector(aSel);
|
|
||||||
id delegate;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the selector matches the correct form for a services request,
|
|
||||||
* send the message to the services provider - otherwise raise an
|
|
||||||
* exception to say the method is not implemented.
|
|
||||||
*/
|
|
||||||
if ([selName hasSuffix: @":userData:error:"])
|
|
||||||
return [servicesProvider performv: aSel :frame];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the applications delegate can handle the message - forward to it.
|
|
||||||
*/
|
|
||||||
delegate = [[NSApplication sharedApplication] delegate];
|
|
||||||
if ([delegate respondsToSelector: aSel] == YES)
|
|
||||||
return [delegate performv: aSel :frame];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the selector matches the correct form for a file operaqtion
|
|
||||||
* send the message to the manager.
|
|
||||||
*/
|
|
||||||
if ([selName hasPrefix: @"application:"] == YES
|
|
||||||
&& [manager respondsToSelector: aSel] == YES)
|
|
||||||
return [(id)manager performv: aSel :frame];
|
|
||||||
|
|
||||||
[NSException raise: NSGenericException
|
|
||||||
format: @"method %@ not implemented", selName];
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) forwardInvocation: (NSInvocation*)anInvocation
|
- (void) forwardInvocation: (NSInvocation*)anInvocation
|
||||||
{
|
{
|
||||||
SEL aSel = [anInvocation selector];
|
SEL aSel = [anInvocation selector];
|
||||||
NSString *selName = NSStringFromSelector(aSel);
|
NSString *selName = NSStringFromSelector(aSel);
|
||||||
id delegate;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the selector matches the correct form for a services request,
|
* If the selector matches the correct form for a services request,
|
||||||
|
@ -261,74 +253,130 @@ NSRegisterServicesProvider(id provider, NSString *name)
|
||||||
*/
|
*/
|
||||||
if ([selName hasSuffix: @":userData:error:"])
|
if ([selName hasSuffix: @":userData:error:"])
|
||||||
{
|
{
|
||||||
[anInvocation invokeWithTarget: servicesProvider];
|
if ([servicesProvider respondsToSelector: aSel] == YES)
|
||||||
return;
|
{
|
||||||
}
|
NSPasteboard *pb;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the applications delegate can handle the message - forward to it.
|
* Create a local NSPasteboard object for this pasteboard.
|
||||||
*/
|
* If we try to use the remote NSPasteboard object, we get
|
||||||
delegate = [[NSApplication sharedApplication] delegate];
|
* trouble when setting property lists since the remote
|
||||||
if ([delegate respondsToSelector: aSel] == YES)
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
[anInvocation invokeWithTarget: delegate];
|
id delegate = [[NSApplication sharedApplication] delegate];
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
if ([selName hasPrefix: @"application:"] == YES)
|
||||||
* If the selector matches the correct form for a file operaqtion
|
{
|
||||||
* send the message to the manager.
|
if ([delegate respondsToSelector: aSel] == YES)
|
||||||
*/
|
{
|
||||||
if ([selName hasPrefix: @"application:"] == YES
|
[anInvocation invokeWithTarget: delegate];
|
||||||
&& [manager respondsToSelector: aSel] == YES)
|
return;
|
||||||
{
|
}
|
||||||
[anInvocation invokeWithTarget: manager];
|
else if ([manager respondsToSelector: aSel] == YES)
|
||||||
return;
|
{
|
||||||
}
|
[anInvocation invokeWithTarget: manager];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ([delegate respondsToSelector: aSel] == YES)
|
||||||
|
{
|
||||||
|
NSArray *messages;
|
||||||
|
|
||||||
|
messages = [[NSUserDefaults standardUserDefaults] arrayForKey:
|
||||||
|
@"GSPermittedMessages"];
|
||||||
|
if (messages != nil)
|
||||||
|
{
|
||||||
|
if ([messages containsObject: selName] == YES)
|
||||||
|
{
|
||||||
|
[anInvocation invokeWithTarget: delegate];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[anInvocation invokeWithTarget: delegate];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
[NSException raise: NSGenericException
|
[NSException raise: NSGenericException
|
||||||
format: @"method %@ not implemented", selName];
|
format: @"method %@ not implemented", selName];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) performService: (NSString*)name
|
/**
|
||||||
withPasteboard: (NSPasteboard*)pb
|
* Return the appropriate method signature for aSelector, checking
|
||||||
userData: (NSString*)ud
|
* to see if it's a standard service message or standard application
|
||||||
error: (NSString**)e
|
* message.<br />
|
||||||
|
* If the message is non-standard, it can be checked against a list
|
||||||
|
* of messages specified by the GSPermittedMessages user default.
|
||||||
|
*/
|
||||||
|
- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector
|
||||||
{
|
{
|
||||||
id obj = servicesProvider;
|
NSMethodSignature *sig = nil;
|
||||||
SEL msgSel = NSSelectorFromString(name);
|
NSString *selName = NSStringFromSelector(aSelector);
|
||||||
IMP msgImp;
|
|
||||||
|
|
||||||
/*
|
if ([selName hasSuffix: @":userData:error:"])
|
||||||
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.
|
|
||||||
*/
|
|
||||||
pb = [NSPasteboard pasteboardWithName: [pb name]];
|
|
||||||
|
|
||||||
if (obj != nil && [obj respondsToSelector: msgSel])
|
|
||||||
{
|
{
|
||||||
msgImp = [obj methodForSelector: msgSel];
|
sig = [servicesProvider methodSignatureForSelector: aSelector];
|
||||||
if (msgImp != 0)
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
id delegate = [[NSApplication sharedApplication] delegate];
|
||||||
|
|
||||||
|
if ([selName hasPrefix: @"application:"] == YES)
|
||||||
{
|
{
|
||||||
(*msgImp)(obj, msgSel, pb, ud, e);
|
if ([delegate respondsToSelector: aSelector] == YES)
|
||||||
return;
|
{
|
||||||
|
sig = [delegate methodSignatureForSelector: aSelector];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sig = [manager methodSignatureForSelector: aSelector];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NSArray *messages;
|
||||||
|
|
||||||
|
messages = [[NSUserDefaults standardUserDefaults] arrayForKey:
|
||||||
|
@"GSPermittedMessages"];
|
||||||
|
|
||||||
|
if (messages != nil)
|
||||||
|
{
|
||||||
|
if ([messages containsObject: selName] == YES)
|
||||||
|
{
|
||||||
|
sig = [delegate methodSignatureForSelector: aSelector];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sig = [delegate methodSignatureForSelector: aSelector];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
|
||||||
obj = [[NSApplication sharedApplication] delegate];
|
- (BOOL) respondsToSelector: (SEL)aSelector
|
||||||
if (obj != nil && [obj respondsToSelector: msgSel])
|
{
|
||||||
|
if ([self methodSignatureForSelector: aSelector] != nil)
|
||||||
{
|
{
|
||||||
msgImp = [obj methodForSelector: msgSel];
|
return YES;
|
||||||
if (msgImp != 0)
|
|
||||||
{
|
|
||||||
(*msgImp)(obj, msgSel, pb, ud, e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return NO;
|
||||||
*e = @"No object available to provide service";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) release
|
- (void) release
|
||||||
|
@ -1250,14 +1298,29 @@ static NSString *disabledName = @".GNUstepDisabled";
|
||||||
* if necessary. Returns the proxy to the remote application, or nil
|
* if necessary. Returns the proxy to the remote application, or nil
|
||||||
* on failure.
|
* on failure.
|
||||||
* </p>
|
* </p>
|
||||||
* The value of expire provides a timeout in case the application cannot
|
* <p>The value of port specifies the name of the distributed objects
|
||||||
* be contacted promptly.
|
* 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>
|
||||||
|
* <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>
|
||||||
*/
|
*/
|
||||||
id
|
id
|
||||||
GSContactApplication(NSString *appName, NSString *port, NSDate *expire)
|
GSContactApplication(NSString *appName, NSString *port, NSDate *expire)
|
||||||
{
|
{
|
||||||
id app;
|
id app;
|
||||||
|
|
||||||
|
if (port == nil)
|
||||||
|
{
|
||||||
|
port = [[appName lastPathComponent] stringByDeletingPathExtension];
|
||||||
|
}
|
||||||
|
if (expire == nil)
|
||||||
|
{
|
||||||
|
expire = [NSDate datyeWithTimeIntervalSinceNow: 30.0];
|
||||||
|
}
|
||||||
if (providerName != nil && [port isEqual: providerName] == YES)
|
if (providerName != nil && [port isEqual: providerName] == YES)
|
||||||
{
|
{
|
||||||
app = [GSListener listener]; // Contect our own listener.
|
app = [GSListener listener]; // Contect our own listener.
|
||||||
|
@ -1391,13 +1454,31 @@ NSPerformService(NSString *serviceItem, NSPasteboard *pboard)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* At last, we ask for the service to be performed.
|
* At last, we ask for the service to be performed.
|
||||||
|
* 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.
|
||||||
*/
|
*/
|
||||||
NS_DURING
|
NS_DURING
|
||||||
{
|
{
|
||||||
[provider performService: selName
|
const char *name = [selName UTF8String];
|
||||||
withPasteboard: pboard
|
SEL sel = GSSelectorFromNameAndTypes(name, 0);
|
||||||
userData: userData
|
NSMethodSignature *sig = [provider methodSignatureForSelector: sel];
|
||||||
error: &error];
|
|
||||||
|
if (sig != nil)
|
||||||
|
{
|
||||||
|
NSInvocation *inv;
|
||||||
|
NSString **errPtr = &error;
|
||||||
|
|
||||||
|
sel = GSSelectorFromNameAndTypes(name, [sig methodType]);
|
||||||
|
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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
NS_HANDLER
|
NS_HANDLER
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,7 +31,12 @@
|
||||||
<p>
|
<p>
|
||||||
The pasteboard system is the core of OpenStep inter-application
|
The pasteboard system is the core of OpenStep inter-application
|
||||||
communications. This chapter is concerned with the use of the system,
|
communications. This chapter is concerned with the use of the system,
|
||||||
for detailed reference see the [NSPasteboard] class.
|
for detailed reference see the [NSPasteboard] class.<br />
|
||||||
|
For non-standard services provided by applications (ie those which
|
||||||
|
do not fit the general <em>services</em> mechanism described below),
|
||||||
|
you generally use the Distributed Objects system (see [NSConnection])
|
||||||
|
directly, and some hints about that are provided at the end of this
|
||||||
|
chapter.
|
||||||
</p>
|
</p>
|
||||||
<section>
|
<section>
|
||||||
<heading>Cut and Paste</heading>
|
<heading>Cut and Paste</heading>
|
||||||
|
@ -401,6 +406,109 @@
|
||||||
</deflist>
|
</deflist>
|
||||||
</desc>
|
</desc>
|
||||||
</deflist>
|
</deflist>
|
||||||
|
<p>
|
||||||
|
Filter services are used implicitly whenever you get a pasteboard
|
||||||
|
by using one of the methods +pasteboardByFilteringData:ofType:,
|
||||||
|
+pasteboardByFilteringFile: or +pasteboardByFilteringTypesInPasteboard:
|
||||||
|
as the pasteboard system will automatically invoke any available
|
||||||
|
filter to convert the data in the pastebaord to any required
|
||||||
|
type as long as a conversion can be done using a single filter.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<heading>Distributed Objects services</heading>
|
||||||
|
<p>
|
||||||
|
While the general <em>services</em> mechanism described above
|
||||||
|
covers most eventualities, there are some circumstances where
|
||||||
|
you might want your application to offer more complex services
|
||||||
|
which require the client application to have been written to
|
||||||
|
make use of those services and where the interaction between
|
||||||
|
the two is much trickier.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In most cases, such situations are handled by server processes
|
||||||
|
rather than gui applications, thus avoiding all the overheads
|
||||||
|
of a gui application ... linking with the gui library and
|
||||||
|
using the windowing system etc. On occasion you may actually
|
||||||
|
want the services to use facilities from the gui library
|
||||||
|
(such as the [NSPasteboard] or [NSWorkspace] class).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Traditionally, NeXTstep and GNUstep applications permit you to
|
||||||
|
connect to an application using the standard [NSConnection]
|
||||||
|
mechanisms, with the name of the port you connect to being
|
||||||
|
(by convention) the name of the application. The root proxy
|
||||||
|
of the NSConnection obtained this way would be the
|
||||||
|
[NSApplication-delegate] object, and any messages sent to
|
||||||
|
this object would be handled by the application delegate.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In the interests of security, GNUstep provides a mechanism to
|
||||||
|
ensure that <em>only</em> those methods you explicitly want to
|
||||||
|
be available to remote processes are actually available.<br />
|
||||||
|
Those methods are assumed to be any of the standard application
|
||||||
|
methods, and any methods implementing the standard <em>services</em>
|
||||||
|
mechanism (ie. methods whose names begin <code>application:</code>
|
||||||
|
or end with <code>:userData:error:</code>), plus any methods
|
||||||
|
listed in the array returned by the
|
||||||
|
<code>GSPermittedMessages</code> user default.<br />
|
||||||
|
If your application wishes to make non-standard methods available,
|
||||||
|
it should use [NSUserDefaults-registerDefaults:] to set a standard
|
||||||
|
value for GSPermittedMessages. Users of the application can then
|
||||||
|
use the defaults system to override that standard setting for the
|
||||||
|
application in order to reduce or increase the list of messages
|
||||||
|
available to remote processes.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
To make use of a service, you need to check to ensure that the
|
||||||
|
application providing the service is running, connect to it,
|
||||||
|
and then send messages to it. You should take care to catch
|
||||||
|
exceptions and deal with a loss of connection to the server
|
||||||
|
application.<br />
|
||||||
|
As an aid to using the services, GNUstep provides a helper function
|
||||||
|
(GSContactApplication()) which encapsulates the process of
|
||||||
|
establishing a connection and
|
||||||
|
launching the server application if necessary.
|
||||||
|
</p>
|
||||||
|
<example>
|
||||||
|
id proxy = GSContactApplication(@"pathToApp", nil, nil);
|
||||||
|
if (proxy != nil)
|
||||||
|
{
|
||||||
|
NS_EXCEPTION
|
||||||
|
{
|
||||||
|
id result = [proxy performTask: taskName withArgument: anArgument];
|
||||||
|
|
||||||
|
if (result == nil)
|
||||||
|
{
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NS_HANDLER
|
||||||
|
// Handle exception
|
||||||
|
NS_ENDHANDLER
|
||||||
|
}
|
||||||
|
</example>
|
||||||
|
<p>
|
||||||
|
If we want to send repeated messages, we may store the proxy to
|
||||||
|
server application, and might want to keep track of the state of
|
||||||
|
the connection to be sure that the proxy is still valid.
|
||||||
|
</p>
|
||||||
|
<example>
|
||||||
|
ASSIGN(remote, proxy);
|
||||||
|
// We want to keep hold of the proxy for use later, so we need to know
|
||||||
|
// if the connection dies ... we ask for a notification to call our
|
||||||
|
// connectionBecameInvalid: method when the connection dies ... in that
|
||||||
|
// method we can release the proxy.
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
addObserver: self
|
||||||
|
selector: @selector(connectionBecameInvalid:)
|
||||||
|
name: NSConnectionDidDieNotification
|
||||||
|
object: [remote connectionForProxy]];
|
||||||
|
</example>
|
||||||
</section>
|
</section>
|
||||||
</chapter>
|
</chapter>
|
||||||
*/
|
*/
|
||||||
|
@ -419,6 +527,7 @@
|
||||||
#include <Foundation/NSMapTable.h>
|
#include <Foundation/NSMapTable.h>
|
||||||
#include <Foundation/NSNotification.h>
|
#include <Foundation/NSNotification.h>
|
||||||
#include <Foundation/NSException.h>
|
#include <Foundation/NSException.h>
|
||||||
|
#include <Foundation/NSInvocation.h>
|
||||||
#include <Foundation/NSLock.h>
|
#include <Foundation/NSLock.h>
|
||||||
#include <Foundation/NSPathUtilities.h>
|
#include <Foundation/NSPathUtilities.h>
|
||||||
#include <Foundation/NSPortNameServer.h>
|
#include <Foundation/NSPortNameServer.h>
|
||||||
|
@ -440,7 +549,7 @@ static NSString *namePrefix = @"NSTypedFilenamesPboardType:";
|
||||||
/*
|
/*
|
||||||
* A pasteboard class for lazily filtering data
|
* A pasteboard class for lazily filtering data
|
||||||
*/
|
*/
|
||||||
@interface FilteredPasteboard : NSPasteboard
|
@interface GSFiltered : NSPasteboard
|
||||||
{
|
{
|
||||||
@public
|
@public
|
||||||
NSArray *originalTypes;
|
NSArray *originalTypes;
|
||||||
|
@ -450,7 +559,7 @@ static NSString *namePrefix = @"NSTypedFilenamesPboardType:";
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation FilteredPasteboard
|
@implementation GSFiltered
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an array of types, produce an array of all the types we can
|
* Given an array of types, produce an array of all the types we can
|
||||||
|
@ -489,11 +598,58 @@ static NSString *namePrefix = @"NSTypedFilenamesPboardType:";
|
||||||
- (void) dealloc
|
- (void) dealloc
|
||||||
{
|
{
|
||||||
DESTROY(originalTypes);
|
DESTROY(originalTypes);
|
||||||
|
DESTROY(file);
|
||||||
DESTROY(data);
|
DESTROY(data);
|
||||||
DESTROY(pboard);
|
DESTROY(pboard);
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GSFiltered instances are encoded differently from standard pasteboards,
|
||||||
|
* they have no names and are instead represented by whatever it is they
|
||||||
|
* are filtering.
|
||||||
|
*/
|
||||||
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
||||||
|
{
|
||||||
|
if (data != nil)
|
||||||
|
{
|
||||||
|
[aCoder encodeObject: data];
|
||||||
|
[aCoder encodeObject: [originalTypes lastObject]];
|
||||||
|
}
|
||||||
|
else if (file != nil)
|
||||||
|
{
|
||||||
|
[aCoder encodeObject: file];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[aCoder encodeObject: pboard];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id) initWithCoder: (NSCoder*)aCoder
|
||||||
|
{
|
||||||
|
NSPasteboard *p;
|
||||||
|
id val = [aCoder decodeObject];
|
||||||
|
|
||||||
|
if ([val isKindOfClass: [NSData class]] == YES)
|
||||||
|
{
|
||||||
|
NSString *s = [aCoder decodeObject];
|
||||||
|
|
||||||
|
p = [NSPasteboard pasteboardByFilteringData: val ofType: s];
|
||||||
|
}
|
||||||
|
else if ([val isKindOfClass: [NSString class]] == YES)
|
||||||
|
{
|
||||||
|
p = [NSPasteboard pasteboardByFilteringFile: val];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p = [NSPasteboard pasteboardByFilteringTypesInPasteboard: val];
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSIGN(self, p);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method actually performs any filtering required.
|
* This method actually performs any filtering required.
|
||||||
*/
|
*/
|
||||||
|
@ -791,10 +947,31 @@ static NSString *namePrefix = @"NSTypedFilenamesPboardType:";
|
||||||
*/
|
*/
|
||||||
NS_DURING
|
NS_DURING
|
||||||
{
|
{
|
||||||
[provider performService: selName
|
const char *cName;
|
||||||
withPasteboard: tmp
|
SEL sel;
|
||||||
userData: userData
|
NSMethodSignature *sig;
|
||||||
error: &error];
|
|
||||||
|
cName = [selName UTF8String];
|
||||||
|
sel = GSSelectorFromNameAndTypes(cName, 0);
|
||||||
|
sig = [provider methodSignatureForSelector: sel];
|
||||||
|
if (sig != nil)
|
||||||
|
{
|
||||||
|
NSInvocation *inv;
|
||||||
|
NSString **errPtr = &error;
|
||||||
|
|
||||||
|
sel = GSSelectorFromNameAndTypes(cName, [sig methodType]);
|
||||||
|
inv = [NSInvocation invocationWithMethodSignature: sig];
|
||||||
|
[inv setTarget: provider];
|
||||||
|
[inv setSelector: sel];
|
||||||
|
[inv setArgument: (void*)&tmp atIndex: 2];
|
||||||
|
[inv setArgument: (void*)&userData atIndex: 3];
|
||||||
|
[inv setArgument: (void*)&errPtr atIndex: 4];
|
||||||
|
[inv invoke];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error = @"No remote object to handle filter";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
NS_HANDLER
|
NS_HANDLER
|
||||||
{
|
{
|
||||||
|
@ -821,6 +998,8 @@ static NSString *namePrefix = @"NSTypedFilenamesPboardType:";
|
||||||
|
|
||||||
|
|
||||||
@interface NSPasteboard (Private)
|
@interface NSPasteboard (Private)
|
||||||
|
+ (void) _localServer: (id<GSPasteboardSvr>)s;
|
||||||
|
+ (id) _lostServer: (NSNotification*)notification;
|
||||||
+ (id<GSPasteboardSvr>) _pbs;
|
+ (id<GSPasteboardSvr>) _pbs;
|
||||||
+ (NSPasteboard*) _pasteboardWithTarget: (id<GSPasteboardObj>)aTarget
|
+ (NSPasteboard*) _pasteboardWithTarget: (id<GSPasteboardObj>)aTarget
|
||||||
name: (NSString*)aName;
|
name: (NSString*)aName;
|
||||||
|
@ -864,9 +1043,15 @@ static NSMutableDictionary *pasteboards = nil;
|
||||||
static id<GSPasteboardSvr> the_server = nil;
|
static id<GSPasteboardSvr> the_server = nil;
|
||||||
static NSMapTable *mimeMap = NULL;
|
static NSMapTable *mimeMap = NULL;
|
||||||
|
|
||||||
//
|
/**
|
||||||
// Class methods
|
* Returns the general pasteboard found by calling +pasteboardWithName:
|
||||||
//
|
* with NSGeneralPboard as the name.
|
||||||
|
*/
|
||||||
|
+ (NSPasteboard*) generalPasteboard
|
||||||
|
{
|
||||||
|
return [self pasteboardWithName: NSGeneralPboard];
|
||||||
|
}
|
||||||
|
|
||||||
+ (void) initialize
|
+ (void) initialize
|
||||||
{
|
{
|
||||||
if (self == [NSPasteboard class])
|
if (self == [NSPasteboard class])
|
||||||
|
@ -878,210 +1063,93 @@ static NSMapTable *mimeMap = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Special method to use a local server rather than connecting over DO
|
* <p>Creates and returns a pasteboard from which the data in the named
|
||||||
|
* file can be read in all the types to which it can be converted by
|
||||||
|
* filter services.<br />
|
||||||
|
* The type of data in the file is inferred from the file extension.
|
||||||
|
* </p>
|
||||||
|
* <p>No filtering is actually performed until some object asks the
|
||||||
|
* pasteboard for the data, so calling this method is quite inexpensive.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
+ (void) _localServer: (id<GSPasteboardSvr>)s
|
+ (NSPasteboard*) pasteboardByFilteringData: (NSData*)data
|
||||||
|
ofType: (NSString*)type
|
||||||
{
|
{
|
||||||
the_server = s;
|
GSFiltered *p;
|
||||||
}
|
NSArray *types;
|
||||||
|
NSArray *originalTypes;
|
||||||
|
|
||||||
+ (id) _lostServer: (NSNotification*)notification
|
originalTypes = [NSArray arrayWithObject: type];
|
||||||
{
|
types = [GSFiltered _typesFilterableFrom: originalTypes];
|
||||||
id obj = the_server;
|
p = (GSFiltered*)[GSFiltered pasteboardWithUniqueName];
|
||||||
|
p->originalTypes = [originalTypes copy];
|
||||||
the_server = nil;
|
p->data = [data copy];
|
||||||
[[NSNotificationCenter defaultCenter]
|
[p declareTypes: types owner: p];
|
||||||
removeObserver: self
|
|
||||||
name: NSConnectionDidDieNotification
|
|
||||||
object: [notification object]];
|
|
||||||
RELEASE(obj);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (id<GSPasteboardSvr>) _pbs
|
|
||||||
{
|
|
||||||
if (the_server == nil)
|
|
||||||
{
|
|
||||||
NSString *host;
|
|
||||||
NSString *description;
|
|
||||||
|
|
||||||
host = [[NSUserDefaults standardUserDefaults] stringForKey: @"NSHost"];
|
|
||||||
if (host == nil)
|
|
||||||
{
|
|
||||||
host = @"";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NSHost *h;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we have a host specified, but it is the current host,
|
|
||||||
* we do not need to ask for a host by name (nameserver lookup
|
|
||||||
* can be faster) and the empty host name can be used to
|
|
||||||
* indicate that we may start a pasteboard server locally.
|
|
||||||
*/
|
|
||||||
h = [NSHost hostWithName: host];
|
|
||||||
if (h == nil)
|
|
||||||
{
|
|
||||||
NSLog(@"Unknown NSHost (%@) ignored", host);
|
|
||||||
host = @"";
|
|
||||||
}
|
|
||||||
else if ([h isEqual: [NSHost currentHost]] == YES)
|
|
||||||
{
|
|
||||||
host = @"";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
host = [h name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([host length] == 0)
|
|
||||||
{
|
|
||||||
description = @"local host";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
description = host;
|
|
||||||
}
|
|
||||||
|
|
||||||
the_server = (id<GSPasteboardSvr>)[NSConnection
|
|
||||||
rootProxyForConnectionWithRegisteredName: PBSNAME host: host];
|
|
||||||
if (the_server == nil && [host length] > 0)
|
|
||||||
{
|
|
||||||
NSString *service;
|
|
||||||
|
|
||||||
service = [PBSNAME stringByAppendingFormat: @"-%@", host];
|
|
||||||
the_server = (id<GSPasteboardSvr>)[NSConnection
|
|
||||||
rootProxyForConnectionWithRegisteredName: service host: @"*"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RETAIN((id)the_server) != nil)
|
|
||||||
{
|
|
||||||
NSConnection *conn = [(id)the_server connectionForProxy];
|
|
||||||
Protocol *p = @protocol(GSPasteboardSvr);
|
|
||||||
|
|
||||||
[(id)the_server setProtocolForProxy: p];
|
|
||||||
[[NSNotificationCenter defaultCenter]
|
|
||||||
addObserver: self
|
|
||||||
selector: @selector(_lostServer:)
|
|
||||||
name: NSConnectionDidDieNotification
|
|
||||||
object: conn];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
static BOOL recursion = NO;
|
|
||||||
static NSString *cmd = nil;
|
|
||||||
static NSArray *args = nil;
|
|
||||||
|
|
||||||
if (cmd == nil && recursion ==NO)
|
|
||||||
{
|
|
||||||
#ifdef GNUSTEP_BASE_LIBRARY
|
|
||||||
cmd = RETAIN([[NSSearchPathForDirectoriesInDomains(
|
|
||||||
GSToolsDirectory, NSSystemDomainMask, YES) objectAtIndex: 0]
|
|
||||||
stringByAppendingPathComponent: @"gpbs"]);
|
|
||||||
#else
|
|
||||||
cmd = RETAIN([[@GNUSTEP_INSTALL_PREFIX
|
|
||||||
stringByAppendingPathComponent: @"Tools"]
|
|
||||||
stringByAppendingPathComponent: @"gpbs"]);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if (recursion == YES || cmd == nil)
|
|
||||||
{
|
|
||||||
NSLog(@"Unable to contact pasteboard server - "
|
|
||||||
@"please ensure that gpbs is running for %@.", description);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NSLog(@"\nI couldn't contact the pasteboard server for %@ -\n"
|
|
||||||
@"so I'm attempting to to start one - which will take a few seconds.\n"
|
|
||||||
@"Trying to launch gpbs from %@ or a machine/operating-system subdirectory.\n"
|
|
||||||
@"It is recommended that you start the pasteboard server (gpbs) when\n"
|
|
||||||
@"your windowing system is started up.\n", description,
|
|
||||||
[cmd stringByDeletingLastPathComponent]);
|
|
||||||
if ([host length] > 0)
|
|
||||||
{
|
|
||||||
args = [[NSArray alloc] initWithObjects:
|
|
||||||
@"-NSHost", host, nil];
|
|
||||||
}
|
|
||||||
[NSTask launchedTaskWithLaunchPath: cmd arguments: args];
|
|
||||||
[NSTimer scheduledTimerWithTimeInterval: 5.0
|
|
||||||
invocation: nil
|
|
||||||
repeats: NO];
|
|
||||||
[[NSRunLoop currentRunLoop] runUntilDate:
|
|
||||||
[NSDate dateWithTimeIntervalSinceNow: 5.0]];
|
|
||||||
recursion = YES;
|
|
||||||
[self _pbs];
|
|
||||||
recursion = NO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return the_server;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Creating and Releasing an NSPasteboard Object
|
|
||||||
*/
|
|
||||||
+ (NSPasteboard*) _pasteboardWithTarget: (id<GSPasteboardObj>)aTarget
|
|
||||||
name: (NSString*)aName
|
|
||||||
{
|
|
||||||
NSPasteboard *p = nil;
|
|
||||||
|
|
||||||
[dictionary_lock lock];
|
|
||||||
p = [pasteboards objectForKey: aName];
|
|
||||||
if (p != nil)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* It is conceivable that the following may have occurred -
|
|
||||||
* 1. The pasteboard was created on the server
|
|
||||||
* 2. We set up an NSPasteboard to point to it
|
|
||||||
* 3. The pasteboard on the server was destroyed by a [-releaseGlobally]
|
|
||||||
* 4. The named pasteboard was asked for again - resulting in a new
|
|
||||||
* object being created on the server.
|
|
||||||
* If this is the case, our proxy for the object on the server will be
|
|
||||||
* out of date, so we swap it for the newly created one.
|
|
||||||
*/
|
|
||||||
if (p->target != (id)aTarget)
|
|
||||||
{
|
|
||||||
AUTORELEASE(p->target);
|
|
||||||
p->target = RETAIN((id)aTarget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* For a newly created NSPasteboard object, we must make an entry
|
|
||||||
* in the dictionary so we can look it up safely.
|
|
||||||
*/
|
|
||||||
p = [self alloc];
|
|
||||||
if (p != nil)
|
|
||||||
{
|
|
||||||
p->target = RETAIN((id)aTarget);
|
|
||||||
p->name = RETAIN(aName);
|
|
||||||
[pasteboards setObject: p forKey: aName];
|
|
||||||
AUTORELEASE(p);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* The AUTORELEASE ensures that the NSPasteboard object we are
|
|
||||||
* returning will be released once our caller has finished with it.
|
|
||||||
* This is necessary so that our RELEASE method will be called to
|
|
||||||
* remove the NSPasteboard from the 'pasteboards' array when it is not
|
|
||||||
* needed any more.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
[dictionary_lock unlock];
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the general pasteboard found by calling +pasteboardWithName:
|
* <p>Creates and returns a pasteboard from which the data in the named
|
||||||
* with NSGeneralPboard as the name.
|
* file can be read in all the types to which it can be converted by
|
||||||
|
* filter services.<br />
|
||||||
|
* The type of data in the file is inferred from the file extension.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
+ (NSPasteboard*) generalPasteboard
|
+ (NSPasteboard*) pasteboardByFilteringFile: (NSString*)filename
|
||||||
{
|
{
|
||||||
return [self pasteboardWithName: NSGeneralPboard];
|
GSFiltered *p;
|
||||||
|
NSString *ext = [filename pathExtension];
|
||||||
|
NSArray *types;
|
||||||
|
NSArray *originalTypes;
|
||||||
|
|
||||||
|
if ([ext length] > 0)
|
||||||
|
{
|
||||||
|
originalTypes = [NSArray arrayWithObjects:
|
||||||
|
NSCreateFileContentsPboardType(ext),
|
||||||
|
NSFileContentsPboardType,
|
||||||
|
nil];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
originalTypes = [NSArray arrayWithObject: NSFileContentsPboardType];
|
||||||
|
}
|
||||||
|
types = [GSFiltered _typesFilterableFrom: originalTypes];
|
||||||
|
p = (GSFiltered*)[GSFiltered pasteboardWithUniqueName];
|
||||||
|
p->originalTypes = [originalTypes copy];
|
||||||
|
p->file = [filename copy];
|
||||||
|
[p declareTypes: types owner: p];
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates and returns a pasteboard where the data contained in pboard
|
||||||
|
* is available for reading in as many types as it can be converted to by
|
||||||
|
* available filter services. This normally expands on the range of types
|
||||||
|
* available in pboard.
|
||||||
|
* </p>
|
||||||
|
* <p>NB. This only permits a single level of filtering ... if pboard was
|
||||||
|
* previously returned by another filtering method, it is returned instead
|
||||||
|
* of a new pasteboard.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
+ (NSPasteboard*) pasteboardByFilteringTypesInPasteboard: (NSPasteboard*)pboard
|
||||||
|
{
|
||||||
|
GSFiltered *p;
|
||||||
|
NSArray *types;
|
||||||
|
NSArray *originalTypes;
|
||||||
|
|
||||||
|
if ([pboard isKindOfClass: [GSFiltered class]] == YES)
|
||||||
|
{
|
||||||
|
return pboard;
|
||||||
|
}
|
||||||
|
originalTypes = [pboard types];
|
||||||
|
types = [GSFiltered _typesFilterableFrom: originalTypes];
|
||||||
|
p = (GSFiltered*)[GSFiltered pasteboardWithUniqueName];
|
||||||
|
p->originalTypes = [originalTypes copy];
|
||||||
|
p->pboard = RETAIN(pboard);
|
||||||
|
[p declareTypes: types owner: p];
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1168,95 +1236,6 @@ static NSMapTable *mimeMap = NULL;
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Creates and returns a pasteboard from which the data in the named
|
|
||||||
* file can be read in all the types to which it can be converted by
|
|
||||||
* filter services.<br />
|
|
||||||
* The type of data in the file is inferred from the file extension.
|
|
||||||
* </p>
|
|
||||||
* <p>No filtering is actually performed until some object asks the
|
|
||||||
* pasteboard for the data, so calling this method is quite inexpensive.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
+ (NSPasteboard*) pasteboardByFilteringData: (NSData*)data
|
|
||||||
ofType: (NSString*)type
|
|
||||||
{
|
|
||||||
FilteredPasteboard *p;
|
|
||||||
NSArray *types;
|
|
||||||
NSArray *originalTypes;
|
|
||||||
|
|
||||||
originalTypes = [NSArray arrayWithObject: type];
|
|
||||||
types = [FilteredPasteboard _typesFilterableFrom: originalTypes];
|
|
||||||
p = (FilteredPasteboard*)[FilteredPasteboard pasteboardWithUniqueName];
|
|
||||||
p->originalTypes = [originalTypes copy];
|
|
||||||
p->data = [data copy];
|
|
||||||
[p declareTypes: types owner: p];
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Creates and returns a pasteboard from which the data in the named
|
|
||||||
* file can be read in all the types to which it can be converted by
|
|
||||||
* filter services.<br />
|
|
||||||
* The type of data in the file is inferred from the file extension.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
+ (NSPasteboard*) pasteboardByFilteringFile: (NSString*)filename
|
|
||||||
{
|
|
||||||
FilteredPasteboard *p;
|
|
||||||
NSString *ext = [filename pathExtension];
|
|
||||||
NSArray *types;
|
|
||||||
NSArray *originalTypes;
|
|
||||||
|
|
||||||
if ([ext length] > 0)
|
|
||||||
{
|
|
||||||
originalTypes = [NSArray arrayWithObjects:
|
|
||||||
NSCreateFileContentsPboardType(ext),
|
|
||||||
NSFileContentsPboardType,
|
|
||||||
nil];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
originalTypes = [NSArray arrayWithObject: NSFileContentsPboardType];
|
|
||||||
}
|
|
||||||
types = [FilteredPasteboard _typesFilterableFrom: originalTypes];
|
|
||||||
p = (FilteredPasteboard*)[FilteredPasteboard pasteboardWithUniqueName];
|
|
||||||
p->originalTypes = [originalTypes copy];
|
|
||||||
p->file = [filename copy];
|
|
||||||
[p declareTypes: types owner: p];
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Creates and returns a pasteboard where the data contained in pboard
|
|
||||||
* is available for reading in as many types as it can be converted to by
|
|
||||||
* available filter services. This normally expands on the range of types
|
|
||||||
* available in pboard.
|
|
||||||
* </p>
|
|
||||||
* <p>NB. This only permits a single level of filtering ... if pboard was
|
|
||||||
* previously returned by another filtering method, it is returned instead
|
|
||||||
* of a new pasteboard.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
+ (NSPasteboard*) pasteboardByFilteringTypesInPasteboard: (NSPasteboard*)pboard
|
|
||||||
{
|
|
||||||
FilteredPasteboard *p;
|
|
||||||
NSArray *types;
|
|
||||||
NSArray *originalTypes;
|
|
||||||
|
|
||||||
if ([pboard isKindOfClass: [FilteredPasteboard class]] == YES)
|
|
||||||
{
|
|
||||||
return pboard;
|
|
||||||
}
|
|
||||||
originalTypes = [pboard types];
|
|
||||||
types = [FilteredPasteboard _typesFilterableFrom: originalTypes];
|
|
||||||
p = (FilteredPasteboard*)[FilteredPasteboard pasteboardWithUniqueName];
|
|
||||||
p->originalTypes = [originalTypes copy];
|
|
||||||
p->pboard = RETAIN(pboard);
|
|
||||||
[p declareTypes: types owner: p];
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of the types from which data of the specified type
|
* Returns an array of the types from which data of the specified type
|
||||||
* can be produced by registered filter services.<br />
|
* can be produced by registered filter services.<br />
|
||||||
|
@ -1290,55 +1269,6 @@ static NSMapTable *mimeMap = NULL;
|
||||||
return [types allObjects];
|
return [types allObjects];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Instance methods
|
|
||||||
*/
|
|
||||||
|
|
||||||
- (id) _target
|
|
||||||
{
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Creating and Releasing an NSPasteboard Object
|
|
||||||
*/
|
|
||||||
|
|
||||||
- (void) dealloc
|
|
||||||
{
|
|
||||||
RELEASE(target);
|
|
||||||
RELEASE(name);
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases the receiver in the pasteboard server so that no other application
|
|
||||||
* can use the pasteboard. This should not be called for any of the standard
|
|
||||||
* pasteboards, only for temporary ones.
|
|
||||||
*/
|
|
||||||
- (void) releaseGlobally
|
|
||||||
{
|
|
||||||
if ([name isEqualToString: NSGeneralPboard] == YES
|
|
||||||
|| [name isEqualToString: NSFontPboard] == YES
|
|
||||||
|| [name isEqualToString: NSRulerPboard] == YES
|
|
||||||
|| [name isEqualToString: NSFindPboard] == YES
|
|
||||||
|| [name isEqualToString: NSDragPboard] == YES)
|
|
||||||
{
|
|
||||||
[NSException raise: NSGenericException
|
|
||||||
format: @"Illegal attempt to globally release %@", name];
|
|
||||||
}
|
|
||||||
[target releaseGlobally];
|
|
||||||
[pasteboards removeObjectForKey: name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the pasteboard name (as given to +pasteboardWithName:)
|
|
||||||
* for the receiver.
|
|
||||||
*/
|
|
||||||
- (NSString*) name
|
|
||||||
{
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Adds newTypes to the pasteboard and declares newOwner to be the owner
|
* <p>Adds newTypes to the pasteboard and declares newOwner to be the owner
|
||||||
* of the pasteboard. Use only after -declareTypes:owner: has been called
|
* of the pasteboard. Use only after -declareTypes:owner: has been called
|
||||||
|
@ -1456,6 +1386,79 @@ static NSMapTable *mimeMap = NULL;
|
||||||
return changeCount;
|
return changeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) dealloc
|
||||||
|
{
|
||||||
|
RELEASE(target);
|
||||||
|
RELEASE(name);
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode for DO by using just our name.
|
||||||
|
*/
|
||||||
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
||||||
|
{
|
||||||
|
[aCoder encodeObject: name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode from DO by creating a new pasteboard with the decoded name.
|
||||||
|
*/
|
||||||
|
- (id) initWithCoder: (NSCoder*)aCoder
|
||||||
|
{
|
||||||
|
NSString *n = [aCoder decodeObject];
|
||||||
|
NSPasteboard *p = [[self class] pasteboardWithName: n];
|
||||||
|
|
||||||
|
ASSIGN(self, p);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the pasteboard name (as given to +pasteboardWithName:)
|
||||||
|
* for the receiver.
|
||||||
|
*/
|
||||||
|
- (NSString*) name
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases the receiver in the pasteboard server so that no other application
|
||||||
|
* can use the pasteboard. This should not be called for any of the standard
|
||||||
|
* pasteboards, only for temporary ones.
|
||||||
|
*/
|
||||||
|
- (void) releaseGlobally
|
||||||
|
{
|
||||||
|
if ([name isEqualToString: NSGeneralPboard] == YES
|
||||||
|
|| [name isEqualToString: NSFontPboard] == YES
|
||||||
|
|| [name isEqualToString: NSRulerPboard] == YES
|
||||||
|
|| [name isEqualToString: NSFindPboard] == YES
|
||||||
|
|| [name isEqualToString: NSDragPboard] == YES)
|
||||||
|
{
|
||||||
|
[NSException raise: NSGenericException
|
||||||
|
format: @"Illegal attempt to globally release %@", name];
|
||||||
|
}
|
||||||
|
[target releaseGlobally];
|
||||||
|
[pasteboards removeObjectForKey: name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pasteboards sent over DO should always be copied so that a local
|
||||||
|
* instance is created to communicate with the pastebaord server.
|
||||||
|
*/
|
||||||
|
- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder
|
||||||
|
{
|
||||||
|
if ([self class] == [NSPasteboard class])
|
||||||
|
{
|
||||||
|
return self; // Always encode bycopy.
|
||||||
|
}
|
||||||
|
if ([self class] == [GSFiltered class])
|
||||||
|
{
|
||||||
|
return self; // Always encode bycopy.
|
||||||
|
}
|
||||||
|
return [super replacementObjectForPortCoder: aCoder];
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hack to ensure correct release of NSPasteboard objects -
|
* Hack to ensure correct release of NSPasteboard objects -
|
||||||
* If we are released such that the only thing retaining us
|
* If we are released such that the only thing retaining us
|
||||||
|
@ -1853,6 +1856,212 @@ static NSMapTable *mimeMap = NULL;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@implementation NSPasteboard (Private)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Special method to use a local server rather than connecting over DO
|
||||||
|
*/
|
||||||
|
+ (void) _localServer: (id<GSPasteboardSvr>)s
|
||||||
|
{
|
||||||
|
the_server = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (id) _lostServer: (NSNotification*)notification
|
||||||
|
{
|
||||||
|
id obj = the_server;
|
||||||
|
|
||||||
|
the_server = nil;
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
removeObserver: self
|
||||||
|
name: NSConnectionDidDieNotification
|
||||||
|
object: [notification object]];
|
||||||
|
RELEASE(obj);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (id<GSPasteboardSvr>) _pbs
|
||||||
|
{
|
||||||
|
if (the_server == nil)
|
||||||
|
{
|
||||||
|
NSString *host;
|
||||||
|
NSString *description;
|
||||||
|
|
||||||
|
host = [[NSUserDefaults standardUserDefaults] stringForKey: @"NSHost"];
|
||||||
|
if (host == nil)
|
||||||
|
{
|
||||||
|
host = @"";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NSHost *h;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have a host specified, but it is the current host,
|
||||||
|
* we do not need to ask for a host by name (nameserver lookup
|
||||||
|
* can be faster) and the empty host name can be used to
|
||||||
|
* indicate that we may start a pasteboard server locally.
|
||||||
|
*/
|
||||||
|
h = [NSHost hostWithName: host];
|
||||||
|
if (h == nil)
|
||||||
|
{
|
||||||
|
NSLog(@"Unknown NSHost (%@) ignored", host);
|
||||||
|
host = @"";
|
||||||
|
}
|
||||||
|
else if ([h isEqual: [NSHost currentHost]] == YES)
|
||||||
|
{
|
||||||
|
host = @"";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
host = [h name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([host length] == 0)
|
||||||
|
{
|
||||||
|
description = @"local host";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
description = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
the_server = (id<GSPasteboardSvr>)[NSConnection
|
||||||
|
rootProxyForConnectionWithRegisteredName: PBSNAME host: host];
|
||||||
|
if (the_server == nil && [host length] > 0)
|
||||||
|
{
|
||||||
|
NSString *service;
|
||||||
|
|
||||||
|
service = [PBSNAME stringByAppendingFormat: @"-%@", host];
|
||||||
|
the_server = (id<GSPasteboardSvr>)[NSConnection
|
||||||
|
rootProxyForConnectionWithRegisteredName: service host: @"*"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RETAIN((id)the_server) != nil)
|
||||||
|
{
|
||||||
|
NSConnection *conn = [(id)the_server connectionForProxy];
|
||||||
|
Protocol *p = @protocol(GSPasteboardSvr);
|
||||||
|
|
||||||
|
[(id)the_server setProtocolForProxy: p];
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
addObserver: self
|
||||||
|
selector: @selector(_lostServer:)
|
||||||
|
name: NSConnectionDidDieNotification
|
||||||
|
object: conn];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static BOOL recursion = NO;
|
||||||
|
static NSString *cmd = nil;
|
||||||
|
static NSArray *args = nil;
|
||||||
|
|
||||||
|
if (cmd == nil && recursion ==NO)
|
||||||
|
{
|
||||||
|
#ifdef GNUSTEP_BASE_LIBRARY
|
||||||
|
cmd = RETAIN([[NSSearchPathForDirectoriesInDomains(
|
||||||
|
GSToolsDirectory, NSSystemDomainMask, YES) objectAtIndex: 0]
|
||||||
|
stringByAppendingPathComponent: @"gpbs"]);
|
||||||
|
#else
|
||||||
|
cmd = RETAIN([[@GNUSTEP_INSTALL_PREFIX
|
||||||
|
stringByAppendingPathComponent: @"Tools"]
|
||||||
|
stringByAppendingPathComponent: @"gpbs"]);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (recursion == YES || cmd == nil)
|
||||||
|
{
|
||||||
|
NSLog(@"Unable to contact pasteboard server - "
|
||||||
|
@"please ensure that gpbs is running for %@.", description);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NSLog(@"\nI couldn't contact the pasteboard server for %@ -\n"
|
||||||
|
@"so I'm attempting to to start one - which will take a few seconds.\n"
|
||||||
|
@"Trying to launch gpbs from %@ or a machine/operating-system subdirectory.\n"
|
||||||
|
@"It is recommended that you start the pasteboard server (gpbs) when\n"
|
||||||
|
@"your windowing system is started up.\n", description,
|
||||||
|
[cmd stringByDeletingLastPathComponent]);
|
||||||
|
if ([host length] > 0)
|
||||||
|
{
|
||||||
|
args = [[NSArray alloc] initWithObjects:
|
||||||
|
@"-NSHost", host, nil];
|
||||||
|
}
|
||||||
|
[NSTask launchedTaskWithLaunchPath: cmd arguments: args];
|
||||||
|
[NSTimer scheduledTimerWithTimeInterval: 5.0
|
||||||
|
invocation: nil
|
||||||
|
repeats: NO];
|
||||||
|
[[NSRunLoop currentRunLoop] runUntilDate:
|
||||||
|
[NSDate dateWithTimeIntervalSinceNow: 5.0]];
|
||||||
|
recursion = YES;
|
||||||
|
[self _pbs];
|
||||||
|
recursion = NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return the_server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creating and Releasing an NSPasteboard Object
|
||||||
|
*/
|
||||||
|
+ (NSPasteboard*) _pasteboardWithTarget: (id<GSPasteboardObj>)aTarget
|
||||||
|
name: (NSString*)aName
|
||||||
|
{
|
||||||
|
NSPasteboard *p = nil;
|
||||||
|
|
||||||
|
[dictionary_lock lock];
|
||||||
|
p = [pasteboards objectForKey: aName];
|
||||||
|
if (p != nil)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* It is conceivable that the following may have occurred -
|
||||||
|
* 1. The pasteboard was created on the server
|
||||||
|
* 2. We set up an NSPasteboard to point to it
|
||||||
|
* 3. The pasteboard on the server was destroyed by a [-releaseGlobally]
|
||||||
|
* 4. The named pasteboard was asked for again - resulting in a new
|
||||||
|
* object being created on the server.
|
||||||
|
* If this is the case, our proxy for the object on the server will be
|
||||||
|
* out of date, so we swap it for the newly created one.
|
||||||
|
*/
|
||||||
|
if (p->target != (id)aTarget)
|
||||||
|
{
|
||||||
|
AUTORELEASE(p->target);
|
||||||
|
p->target = RETAIN((id)aTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* For a newly created NSPasteboard object, we must make an entry
|
||||||
|
* in the dictionary so we can look it up safely.
|
||||||
|
*/
|
||||||
|
p = [self alloc];
|
||||||
|
if (p != nil)
|
||||||
|
{
|
||||||
|
p->target = RETAIN((id)aTarget);
|
||||||
|
p->name = RETAIN(aName);
|
||||||
|
[pasteboards setObject: p forKey: aName];
|
||||||
|
AUTORELEASE(p);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* The AUTORELEASE ensures that the NSPasteboard object we are
|
||||||
|
* returning will be released once our caller has finished with it.
|
||||||
|
* This is necessary so that our RELEASE method will be called to
|
||||||
|
* remove the NSPasteboard from the 'pasteboards' array when it is not
|
||||||
|
* needed any more.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
[dictionary_lock unlock];
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id) _target
|
||||||
|
{
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -74,6 +74,7 @@
|
||||||
NSString *val;
|
NSString *val;
|
||||||
NSData *data;
|
NSData *data;
|
||||||
|
|
||||||
|
*err = nil;
|
||||||
types = [pb types];
|
types = [pb types];
|
||||||
if (![types containsObject: NSStringPboardType])
|
if (![types containsObject: NSStringPboardType])
|
||||||
{
|
{
|
||||||
|
@ -106,6 +107,7 @@
|
||||||
NSString *browser;
|
NSString *browser;
|
||||||
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
|
||||||
|
|
||||||
|
*err = nil;
|
||||||
types = [pb types];
|
types = [pb types];
|
||||||
if (![types containsObject: NSStringPboardType])
|
if (![types containsObject: NSStringPboardType])
|
||||||
{
|
{
|
||||||
|
@ -143,6 +145,7 @@
|
||||||
NSString *out;
|
NSString *out;
|
||||||
NSArray *types;
|
NSArray *types;
|
||||||
|
|
||||||
|
*err = nil;
|
||||||
types = [pb types];
|
types = [pb types];
|
||||||
if (![types containsObject: NSStringPboardType])
|
if (![types containsObject: NSStringPboardType])
|
||||||
{
|
{
|
||||||
|
@ -171,6 +174,7 @@
|
||||||
NSString *out;
|
NSString *out;
|
||||||
NSArray *types;
|
NSArray *types;
|
||||||
|
|
||||||
|
*err = nil;
|
||||||
types = [pb types];
|
types = [pb types];
|
||||||
if (![types containsObject: NSStringPboardType])
|
if (![types containsObject: NSStringPboardType])
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue