mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-22 17:52:42 +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
3e87f17811
commit
e12ac6ef5d
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>
|
||||
|
||||
* Headers/gnustep/gui/NSTextView.h, Source/NSTextView.m: Add
|
||||
|
|
|
@ -86,7 +86,5 @@
|
|||
- (void) updateServicesMenu;
|
||||
@end
|
||||
|
||||
id GSContactApplication(NSString *appName, NSString *port, NSDate *expire);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -411,16 +411,21 @@ APPKIT_EXPORT NSString *NSApplicationWillUpdateNotification;
|
|||
* Determine Whether an Item Is Included in Services Menus
|
||||
*/
|
||||
APPKIT_EXPORT int
|
||||
NSSetShowsServicesMenuItem(NSString *item, BOOL showService);
|
||||
NSSetShowsServicesMenuItem(NSString *name, BOOL enabled);
|
||||
|
||||
APPKIT_EXPORT BOOL
|
||||
NSShowsServicesMenuItem(NSString *item);
|
||||
NSShowsServicesMenuItem(NSString *name);
|
||||
|
||||
/*
|
||||
* Programmatically Invoke a Service
|
||||
*/
|
||||
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
|
||||
|
|
|
@ -67,7 +67,7 @@ static GSServicesManager *manager = nil;
|
|||
* messing with us. This is responsible for forwarding service
|
||||
* requests and other communications with the outside world.
|
||||
*/
|
||||
@interface GSListener : NSObject
|
||||
@interface GSListener : NSProxy
|
||||
+ (id) connectionBecameInvalid: (NSNotification*)notification;
|
||||
+ (GSListener*) listener;
|
||||
+ (id) servicesProvider;
|
||||
|
@ -77,13 +77,10 @@ static GSServicesManager *manager = nil;
|
|||
- (void) release;
|
||||
- (id) retain;
|
||||
- (id) self;
|
||||
- (void) performService: (NSString*)name
|
||||
withPasteboard: (NSPasteboard*)pb
|
||||
userData: (NSString*)ud
|
||||
error: (NSString**)e;
|
||||
@end
|
||||
|
||||
static NSConnection *listenerConnection = nil;
|
||||
static NSMutableArray *listeners = nil;
|
||||
static GSListener *listener = nil;
|
||||
static id servicesProvider = 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
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
@implementation GSListener
|
||||
|
||||
|
@ -179,15 +176,39 @@ NSRegisterServicesProvider(id provider, NSString *name)
|
|||
return self;
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
static BOOL beenHere = NO;
|
||||
|
||||
if (beenHere == NO)
|
||||
{
|
||||
beenHere = YES;
|
||||
listeners = [NSMutableArray new];
|
||||
}
|
||||
}
|
||||
|
||||
+ (GSListener*) listener
|
||||
{
|
||||
if (listener == nil)
|
||||
{
|
||||
listener = (id)NSAllocateObject(self, 0, NSDefaultMallocZone());
|
||||
[listeners addObject: 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
|
||||
{
|
||||
return servicesProvider;
|
||||
|
@ -204,6 +225,11 @@ NSRegisterServicesProvider(id provider, NSString *name)
|
|||
}
|
||||
}
|
||||
|
||||
- (id) autorelease
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
- (Class) class
|
||||
{
|
||||
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
|
||||
{
|
||||
SEL aSel = [anInvocation selector];
|
||||
NSString *selName = NSStringFromSelector(aSel);
|
||||
id delegate;
|
||||
|
||||
/*
|
||||
* 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:"])
|
||||
{
|
||||
[anInvocation invokeWithTarget: servicesProvider];
|
||||
return;
|
||||
}
|
||||
if ([servicesProvider respondsToSelector: aSel] == YES)
|
||||
{
|
||||
NSPasteboard *pb;
|
||||
|
||||
/*
|
||||
* If the applications delegate can handle the message - forward to it.
|
||||
*/
|
||||
delegate = [[NSApplication sharedApplication] delegate];
|
||||
if ([delegate respondsToSelector: aSel] == YES)
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[anInvocation invokeWithTarget: delegate];
|
||||
return;
|
||||
}
|
||||
id delegate = [[NSApplication sharedApplication] delegate];
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
[anInvocation invokeWithTarget: manager];
|
||||
return;
|
||||
}
|
||||
if ([selName hasPrefix: @"application:"] == YES)
|
||||
{
|
||||
if ([delegate respondsToSelector: aSel] == YES)
|
||||
{
|
||||
[anInvocation invokeWithTarget: delegate];
|
||||
return;
|
||||
}
|
||||
else if ([manager respondsToSelector: aSel] == YES)
|
||||
{
|
||||
[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
|
||||
format: @"method %@ not implemented", selName];
|
||||
}
|
||||
|
||||
- (void) performService: (NSString*)name
|
||||
withPasteboard: (NSPasteboard*)pb
|
||||
userData: (NSString*)ud
|
||||
error: (NSString**)e
|
||||
/**
|
||||
* 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
|
||||
* of messages specified by the GSPermittedMessages user default.
|
||||
*/
|
||||
- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector
|
||||
{
|
||||
id obj = servicesProvider;
|
||||
SEL msgSel = NSSelectorFromString(name);
|
||||
IMP msgImp;
|
||||
NSMethodSignature *sig = nil;
|
||||
NSString *selName = NSStringFromSelector(aSelector);
|
||||
|
||||
/*
|
||||
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])
|
||||
if ([selName hasSuffix: @":userData:error:"])
|
||||
{
|
||||
msgImp = [obj methodForSelector: msgSel];
|
||||
if (msgImp != 0)
|
||||
sig = [servicesProvider methodSignatureForSelector: aSelector];
|
||||
}
|
||||
else
|
||||
{
|
||||
id delegate = [[NSApplication sharedApplication] delegate];
|
||||
|
||||
if ([selName hasPrefix: @"application:"] == YES)
|
||||
{
|
||||
(*msgImp)(obj, msgSel, pb, ud, e);
|
||||
return;
|
||||
if ([delegate respondsToSelector: aSelector] == YES)
|
||||
{
|
||||
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];
|
||||
if (obj != nil && [obj respondsToSelector: msgSel])
|
||||
- (BOOL) respondsToSelector: (SEL)aSelector
|
||||
{
|
||||
if ([self methodSignatureForSelector: aSelector] != nil)
|
||||
{
|
||||
msgImp = [obj methodForSelector: msgSel];
|
||||
if (msgImp != 0)
|
||||
{
|
||||
(*msgImp)(obj, msgSel, pb, ud, e);
|
||||
return;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
*e = @"No object available to provide service";
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void) release
|
||||
|
@ -1250,14 +1298,29 @@ static NSString *disabledName = @".GNUstepDisabled";
|
|||
* if necessary. Returns the proxy to the remote application, or nil
|
||||
* on failure.
|
||||
* </p>
|
||||
* The value of expire provides a timeout in case the application cannot
|
||||
* be contacted promptly.
|
||||
* <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>
|
||||
* <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
|
||||
GSContactApplication(NSString *appName, NSString *port, NSDate *expire)
|
||||
{
|
||||
id app;
|
||||
|
||||
if (port == nil)
|
||||
{
|
||||
port = [[appName lastPathComponent] stringByDeletingPathExtension];
|
||||
}
|
||||
if (expire == nil)
|
||||
{
|
||||
expire = [NSDate datyeWithTimeIntervalSinceNow: 30.0];
|
||||
}
|
||||
if (providerName != nil && [port isEqual: providerName] == YES)
|
||||
{
|
||||
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.
|
||||
* 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
|
||||
{
|
||||
[provider performService: selName
|
||||
withPasteboard: pboard
|
||||
userData: userData
|
||||
error: &error];
|
||||
const char *name = [selName UTF8String];
|
||||
SEL sel = GSSelectorFromNameAndTypes(name, 0);
|
||||
NSMethodSignature *sig = [provider methodSignatureForSelector: sel];
|
||||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -31,7 +31,12 @@
|
|||
<p>
|
||||
The pasteboard system is the core of OpenStep inter-application
|
||||
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>
|
||||
<section>
|
||||
<heading>Cut and Paste</heading>
|
||||
|
@ -401,6 +406,109 @@
|
|||
</deflist>
|
||||
</desc>
|
||||
</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>
|
||||
</chapter>
|
||||
*/
|
||||
|
@ -419,6 +527,7 @@
|
|||
#include <Foundation/NSMapTable.h>
|
||||
#include <Foundation/NSNotification.h>
|
||||
#include <Foundation/NSException.h>
|
||||
#include <Foundation/NSInvocation.h>
|
||||
#include <Foundation/NSLock.h>
|
||||
#include <Foundation/NSPathUtilities.h>
|
||||
#include <Foundation/NSPortNameServer.h>
|
||||
|
@ -440,7 +549,7 @@ static NSString *namePrefix = @"NSTypedFilenamesPboardType:";
|
|||
/*
|
||||
* A pasteboard class for lazily filtering data
|
||||
*/
|
||||
@interface FilteredPasteboard : NSPasteboard
|
||||
@interface GSFiltered : NSPasteboard
|
||||
{
|
||||
@public
|
||||
NSArray *originalTypes;
|
||||
|
@ -450,7 +559,7 @@ static NSString *namePrefix = @"NSTypedFilenamesPboardType:";
|
|||
}
|
||||
@end
|
||||
|
||||
@implementation FilteredPasteboard
|
||||
@implementation GSFiltered
|
||||
|
||||
/**
|
||||
* Given an array of types, produce an array of all the types we can
|
||||
|
@ -489,11 +598,58 @@ static NSString *namePrefix = @"NSTypedFilenamesPboardType:";
|
|||
- (void) dealloc
|
||||
{
|
||||
DESTROY(originalTypes);
|
||||
DESTROY(file);
|
||||
DESTROY(data);
|
||||
DESTROY(pboard);
|
||||
[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.
|
||||
*/
|
||||
|
@ -791,10 +947,31 @@ static NSString *namePrefix = @"NSTypedFilenamesPboardType:";
|
|||
*/
|
||||
NS_DURING
|
||||
{
|
||||
[provider performService: selName
|
||||
withPasteboard: tmp
|
||||
userData: userData
|
||||
error: &error];
|
||||
const char *cName;
|
||||
SEL sel;
|
||||
NSMethodSignature *sig;
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -821,6 +998,8 @@ static NSString *namePrefix = @"NSTypedFilenamesPboardType:";
|
|||
|
||||
|
||||
@interface NSPasteboard (Private)
|
||||
+ (void) _localServer: (id<GSPasteboardSvr>)s;
|
||||
+ (id) _lostServer: (NSNotification*)notification;
|
||||
+ (id<GSPasteboardSvr>) _pbs;
|
||||
+ (NSPasteboard*) _pasteboardWithTarget: (id<GSPasteboardObj>)aTarget
|
||||
name: (NSString*)aName;
|
||||
|
@ -864,9 +1043,15 @@ static NSMutableDictionary *pasteboards = nil;
|
|||
static id<GSPasteboardSvr> the_server = nil;
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
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];
|
||||
originalTypes = [NSArray arrayWithObject: type];
|
||||
types = [GSFiltered _typesFilterableFrom: originalTypes];
|
||||
p = (GSFiltered*)[GSFiltered pasteboardWithUniqueName];
|
||||
p->originalTypes = [originalTypes copy];
|
||||
p->data = [data copy];
|
||||
[p declareTypes: types owner: p];
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the general pasteboard found by calling +pasteboardWithName:
|
||||
* with NSGeneralPboard as the name.
|
||||
* <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*) 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <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
|
||||
* can be produced by registered filter services.<br />
|
||||
|
@ -1290,55 +1269,6 @@ static NSMapTable *mimeMap = NULL;
|
|||
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
|
||||
* of the pasteboard. Use only after -declareTypes:owner: has been called
|
||||
|
@ -1456,6 +1386,79 @@ static NSMapTable *mimeMap = NULL;
|
|||
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 -
|
||||
* If we are released such that the only thing retaining us
|
||||
|
@ -1853,6 +1856,212 @@ static NSMapTable *mimeMap = NULL;
|
|||
|
||||
@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;
|
||||
NSData *data;
|
||||
|
||||
*err = nil;
|
||||
types = [pb types];
|
||||
if (![types containsObject: NSStringPboardType])
|
||||
{
|
||||
|
@ -106,6 +107,7 @@
|
|||
NSString *browser;
|
||||
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
*err = nil;
|
||||
types = [pb types];
|
||||
if (![types containsObject: NSStringPboardType])
|
||||
{
|
||||
|
@ -143,6 +145,7 @@
|
|||
NSString *out;
|
||||
NSArray *types;
|
||||
|
||||
*err = nil;
|
||||
types = [pb types];
|
||||
if (![types containsObject: NSStringPboardType])
|
||||
{
|
||||
|
@ -171,6 +174,7 @@
|
|||
NSString *out;
|
||||
NSArray *types;
|
||||
|
||||
*err = nil;
|
||||
types = [pb types];
|
||||
if (![types containsObject: NSStringPboardType])
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue