mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-05-31 00:20:49 +00:00
This is an implementation of the NSPasteboard class - it's purpose is
mainly to communicate with the pasteboard server (gpbs). There are some extensions to permit a history of pasteboard items. This is currently largely untested (call it pre-alpha) and also depends on bug fixes and changes to gstep-base which have not yet been rolled into the main release. Anyone interested in using the pastboard - please contact me richard@brainstorm.co.uk git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@2402 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
1405f859bd
commit
5923ede6a7
1 changed files with 502 additions and 24 deletions
|
@ -1,12 +1,13 @@
|
|||
/*
|
||||
NSPasteboard.m
|
||||
|
||||
Description...
|
||||
Description... Implementation of class for communicating with the
|
||||
pasteboard server.
|
||||
|
||||
Copyright (C) 1996 Free Software Foundation, Inc.
|
||||
Copyright (C) 1997 Free Software Foundation, Inc.
|
||||
|
||||
Author: Scott Christley <scottc@net-community.com>
|
||||
Date: 1996
|
||||
Author: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
||||
Date: 1997
|
||||
|
||||
This file is part of the GNUstep GUI Library.
|
||||
|
||||
|
@ -27,9 +28,59 @@
|
|||
*/
|
||||
|
||||
#include <AppKit/NSPasteboard.h>
|
||||
#include <AppKit/PasteboardServer.h>
|
||||
#include <Foundation/NSArray.h>
|
||||
#include <Foundation/NSData.h>
|
||||
#include <Foundation/NSDictionary.h>
|
||||
#include <Foundation/NSConnection.h>
|
||||
#include <Foundation/NSDistantObject.h>
|
||||
#include <Foundation/NSNotification.h>
|
||||
#include <Foundation/NSException.h>
|
||||
#include <Foundation/NSLock.h>
|
||||
#include <Foundation/NSProcessInfo.h>
|
||||
#include <Foundation/NSSerialization.h>
|
||||
|
||||
// Pasteboard Type Globals
|
||||
NSString *NSStringPboardType = @"NSStringPboardType";
|
||||
NSString *NSColorPboardType = @"NSColorPboardType";
|
||||
NSString *NSFileContentsPboardType = @"NSFileContentsPboardType";
|
||||
NSString *NSFilenamesPboardType = @"NSFilenamesPboardType";
|
||||
NSString *NSFontPboardType = @"NSFontPboardType";
|
||||
NSString *NSRulerPboardType = @"NSRulerPboardType";
|
||||
NSString *NSPostScriptPboardType = @"NSPostScriptPboardType";
|
||||
NSString *NSTabularTextPboardType = @"NSTabularTextPboardType";
|
||||
NSString *NSRTFPboardType = @"NSRTFPboardType";
|
||||
NSString *NSTIFFPboardType = @"NSTIFFPboardType";
|
||||
NSString *NSDataLinkPboardType = @"NSDataLinkPboardType";
|
||||
NSString *NSGeneralPboardType = @"NSGeneralPboardType";
|
||||
|
||||
// Pasteboard Name Globals
|
||||
NSString *NSDragPboard = @"NSDragPboard";
|
||||
NSString *NSFindPboard = @"NSFindPboard";
|
||||
NSString *NSFontPboard = @"NSFontPboard";
|
||||
NSString *NSGeneralPboard = @"NSGeneralPboard";
|
||||
NSString *NSRulerPboard = @"NSRulerPboard";
|
||||
|
||||
//
|
||||
// Pasteboard Exceptions
|
||||
//
|
||||
NSString *NSPasteboardCommunicationException
|
||||
= @"NSPasteboardCommunicationException";
|
||||
|
||||
@interface NSPasteboard (Private)
|
||||
+ (id<PasteboardServer>) _pbs;
|
||||
+ (NSPasteboard*) _pasteboardWithTarget: (id<PasteboardObject>)aTarget
|
||||
name: (NSString*)aName;
|
||||
- (id) _target;
|
||||
@end
|
||||
|
||||
@implementation NSPasteboard
|
||||
|
||||
static NSLock *dictionary_lock = nil;
|
||||
static NSMutableDictionary *pasteboards = nil;
|
||||
static id<PasteboardServer> the_server = nil;
|
||||
|
||||
|
||||
//
|
||||
// Class methods
|
||||
//
|
||||
|
@ -39,24 +90,138 @@
|
|||
{
|
||||
// Initial version
|
||||
[self setVersion:1];
|
||||
dictionary_lock = [[NSLock alloc] init];
|
||||
pasteboards = [[NSMutableDictionary alloc] initWithCapacity:8];
|
||||
}
|
||||
}
|
||||
|
||||
+ _lostServer: notification
|
||||
{
|
||||
id obj = the_server;
|
||||
|
||||
the_server = nil;
|
||||
[NSNotificationCenter removeObserver: self
|
||||
name: NSConnectionDidDieNotification
|
||||
object: [notification object]];
|
||||
[obj release];
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (id<PasteboardServer>) _pbs
|
||||
{
|
||||
if (the_server == nil) {
|
||||
NSString* host = [[NSProcessInfo processInfo] hostName];
|
||||
|
||||
the_server = (id<PasteboardServer>)[NSConnection
|
||||
rootProxyForConnectionWithRegisteredName: PBSNAME
|
||||
host: host];
|
||||
if ([(id)the_server retain]) {
|
||||
NSConnection* conn = [(id)the_server connectionForProxy];
|
||||
|
||||
[NSNotificationCenter
|
||||
addObserver: self
|
||||
selector: @selector(_lostServer:)
|
||||
name: NSConnectionDidDieNotification
|
||||
object: conn];
|
||||
}
|
||||
}
|
||||
return the_server;
|
||||
}
|
||||
|
||||
//
|
||||
// Creating and Releasing an NSPasteboard Object
|
||||
//
|
||||
+ (NSPasteboard *)generalPasteboard
|
||||
+ (NSPasteboard*) _pasteboardWithTarget: (id<PasteboardObject>)aTarget
|
||||
name: (NSString*)aName
|
||||
{
|
||||
return nil;
|
||||
NSPasteboard* p = nil;
|
||||
|
||||
[dictionary_lock lock];
|
||||
p = [pasteboards objectForKey: aName];
|
||||
if (p) {
|
||||
/*
|
||||
* 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 != aTarget) {
|
||||
[p->target autorelease];
|
||||
p->target = [(id)aTarget retain];
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* For a newly created NSPasteboard object, we must make an entry
|
||||
* in the dictionary so we can look it up safely.
|
||||
*/
|
||||
p = [NSPasteboard alloc];
|
||||
if (p) {
|
||||
p->target = [(id)aTarget retain];
|
||||
p->name = [aName retain];
|
||||
[pasteboards setObject:p forKey:aName];
|
||||
[p autorelease];
|
||||
}
|
||||
/*
|
||||
* The [-autorelease] message 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;
|
||||
}
|
||||
|
||||
+ (NSPasteboard *)pasteboardWithName:(NSString *)name
|
||||
+ (NSPasteboard *)generalPasteboard
|
||||
{
|
||||
return [self pasteboardWithName: NSGeneralPboard];
|
||||
}
|
||||
|
||||
+ (NSPasteboard *)pasteboardWithName:(NSString *)aName
|
||||
{
|
||||
id<PasteboardObject> anObj = nil;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
anObj = [[self _pbs] pasteboardWithName: aName];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
if (anObj) {
|
||||
return [self _pasteboardWithTarget:anObj name:aName];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSPasteboard *)pasteboardWithUniqueName
|
||||
{
|
||||
id<PasteboardObject> anObj = nil;
|
||||
NSString *aName = nil;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
anObj = [[self _pbs] pasteboardWithUniqueName];
|
||||
aName = [anObj name];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
if (anObj) {
|
||||
return [self _pasteboardWithTarget:anObj name:aName];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
@ -66,40 +231,126 @@
|
|||
+ (NSPasteboard *)pasteboardByFilteringData:(NSData *)data
|
||||
ofType:(NSString *)type
|
||||
{
|
||||
id<PasteboardObject> anObj = nil;
|
||||
NSString *aName = nil;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
anObj = [[self _pbs] pasteboardByFilteringData: data
|
||||
ofType: type
|
||||
isFile: NO];
|
||||
aName = [anObj name];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
if (anObj) {
|
||||
return [self _pasteboardWithTarget:anObj name: aName];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSPasteboard *)pasteboardByFilteringFile:(NSString *)filename
|
||||
{
|
||||
id<PasteboardObject> anObj = nil;
|
||||
NSString* aName = nil;
|
||||
NSData* data = [NSData dataWithContentsOfFile:filename];
|
||||
NSString* type = NSCreateFileContentsPboardType([filename pathExtension]);
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
anObj = [[self _pbs] pasteboardByFilteringData: data
|
||||
ofType: type
|
||||
isFile: YES];
|
||||
aName = [anObj name];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
if (anObj) {
|
||||
return [self _pasteboardWithTarget:anObj name: aName];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSPasteboard *)pasteboardByFilteringTypesInPasteboard:(NSPasteboard *)pboard
|
||||
{
|
||||
id<PasteboardObject> anObj = nil;
|
||||
NSString *aName = nil;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
anObj = [pboard _target];
|
||||
anObj = [[self _pbs] pasteboardByFilteringTypesInPasteboard: anObj];
|
||||
aName = [anObj name];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
if (anObj) {
|
||||
return [self _pasteboardWithTarget:anObj name: aName];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSArray *)typesFilterableTo:(NSString *)type
|
||||
{
|
||||
return nil;
|
||||
NSArray* types = nil;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
types = [[self _pbs] typesFilterableTo: type];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
return types;
|
||||
}
|
||||
|
||||
//
|
||||
// Instance methods
|
||||
//
|
||||
|
||||
- (id) _target
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
//
|
||||
// Creating and Releasing an NSPasteboard Object
|
||||
//
|
||||
- (void)releaseGlobally
|
||||
{}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[target release];
|
||||
[name release];
|
||||
[self dealloc];
|
||||
}
|
||||
|
||||
- (void) releaseGlobally
|
||||
{
|
||||
[target releaseGlobally];
|
||||
[pasteboards removeObjectForKey: name];
|
||||
}
|
||||
|
||||
//
|
||||
// Referring to a Pasteboard by Name
|
||||
//
|
||||
- (NSString *)name
|
||||
{
|
||||
return nil;
|
||||
return name;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -108,36 +359,118 @@
|
|||
- (int)addTypes:(NSArray *)newTypes
|
||||
owner:(id)newOwner
|
||||
{
|
||||
return 0;
|
||||
int count = 0;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
count = [target addTypes: newTypes
|
||||
owner: newOwner
|
||||
pasteboard: self
|
||||
oldCount: changeCount];
|
||||
if (count > 0) {
|
||||
changeCount = count;
|
||||
}
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
return count;
|
||||
}
|
||||
|
||||
- (int)declareTypes:(NSArray *)newTypes
|
||||
owner:(id)newOwner
|
||||
{
|
||||
return 0;
|
||||
NS_DURING
|
||||
{
|
||||
changeCount = [target declareTypes: newTypes
|
||||
owner: newOwner
|
||||
pasteboard: self];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
return changeCount;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hack to ensure correct release of NSPasteboard objects -
|
||||
* If we are released such that the only thing retaining us
|
||||
* is the pasteboards dictionary, remove us from that dictionary
|
||||
* as well.
|
||||
*/
|
||||
- (void) release
|
||||
{
|
||||
if ([self retainCount] == 2) {
|
||||
[dictionary_lock lock];
|
||||
[super retain];
|
||||
[pasteboards removeObjectForKey: name];
|
||||
[super release];
|
||||
[dictionary_lock unlock];
|
||||
}
|
||||
[super release];
|
||||
}
|
||||
|
||||
- (BOOL)setData:(NSData *)data
|
||||
forType:(NSString *)dataType
|
||||
{
|
||||
return NO;
|
||||
BOOL ok = NO;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
ok = [target setData: data
|
||||
forType: dataType
|
||||
isFile: NO
|
||||
oldCount: changeCount];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
return ok;
|
||||
}
|
||||
|
||||
- (BOOL)setPropertyList:(id)propertyList
|
||||
forType:(NSString *)dataType
|
||||
{
|
||||
return NO;
|
||||
NSData* d = [NSSerializer serializePropertyList: propertyList];
|
||||
|
||||
return [self setData: d forType: dataType];
|
||||
}
|
||||
|
||||
- (BOOL)setString:(NSString *)string
|
||||
forType:(NSString *)dataType
|
||||
{
|
||||
return NO;
|
||||
return [self setPropertyList: string forType: dataType];
|
||||
}
|
||||
|
||||
- (BOOL)writeFileContents:(NSString *)filename
|
||||
{
|
||||
return NO;
|
||||
NSData* data = [NSData dataWithContentsOfFile:filename];
|
||||
NSString* type = NSCreateFileContentsPboardType([filename pathExtension]);
|
||||
BOOL ok = NO;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
ok = [target setData: data
|
||||
forType: type
|
||||
isFile: YES
|
||||
oldCount: changeCount];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
return ok;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -145,12 +478,43 @@
|
|||
//
|
||||
- (NSString *)availableTypeFromArray:(NSArray *)types
|
||||
{
|
||||
return nil;
|
||||
NSString *type = nil;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
type = [target availableTypeFromArray: types
|
||||
changeCount: &count];
|
||||
changeCount = count;
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
return type;
|
||||
}
|
||||
|
||||
- (NSArray *)types
|
||||
{
|
||||
return nil;
|
||||
NSArray *result = nil;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
result = [target typesAndChangeCount: &count];
|
||||
changeCount = count;
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -158,28 +522,67 @@
|
|||
//
|
||||
- (int)changeCount
|
||||
{
|
||||
return 0;
|
||||
NS_DURING
|
||||
{
|
||||
int count;
|
||||
|
||||
count = [target changeCount];
|
||||
changeCount = count;
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
return changeCount;
|
||||
}
|
||||
|
||||
- (NSData *)dataForType:(NSString *)dataType
|
||||
{
|
||||
return nil;
|
||||
NSData* d = nil;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
d = [target dataForType: dataType
|
||||
oldCount: changeCount
|
||||
mustBeCurrent: useHistory];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
return d;
|
||||
}
|
||||
|
||||
- (id)propertyListForType:(NSString *)dataType
|
||||
{
|
||||
return nil;
|
||||
NSData* d = [self dataForType: dataType];
|
||||
|
||||
return [NSDeserializer deserializePropertyListFromData: d
|
||||
mutableContainers: NO];
|
||||
}
|
||||
|
||||
- (NSString *)readFileContentsType:(NSString *)type
|
||||
toFile:(NSString *)filename
|
||||
{
|
||||
return nil;
|
||||
NSData* d;
|
||||
|
||||
if (type == nil) {
|
||||
type = NSCreateFileContentsPboardType([filename pathExtension]);
|
||||
}
|
||||
d = [self dataForType: type];
|
||||
if ([d writeToFile: filename atomically: NO] == NO) {
|
||||
return nil;
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
- (NSString *)stringForType:(NSString *)dataType
|
||||
{
|
||||
return nil;
|
||||
return [self propertyListForType: dataType];
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -193,3 +596,78 @@ provideDataForType:(NSString *)type
|
|||
{}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSPasteboard (GNUstepExtensions)
|
||||
|
||||
/*
|
||||
* Once the '[-setChangeCount:]' message has been sent to an NSPasteboard
|
||||
* the object will gain an extra GNUstep behaviour - when geting data
|
||||
* from the pasteboard, the data need no longer be from the latest
|
||||
* version but may be a version from a previous representation with
|
||||
* the specified change count.
|
||||
*/
|
||||
- (void) setChangeCount: (int)count
|
||||
{
|
||||
useHistory = YES;
|
||||
changeCount = count;
|
||||
}
|
||||
|
||||
- (void) setHistory: (unsigned)length
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
[target setHistory: length];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
[NSException raise: NSPasteboardCommunicationException
|
||||
format: @"%s", [[localException reason] cString]];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
@end
|
||||
|
||||
static NSString* contentsPrefix = @"NSPBFileCont";
|
||||
static NSString* namePrefix = @"NSPBFileName";
|
||||
|
||||
NSString *NSCreateFileContentsPboardType(NSString *fileType)
|
||||
{
|
||||
return [NSString stringWithFormat:@"%@%@", contentsPrefix, fileType];
|
||||
}
|
||||
|
||||
NSString *NSCreateFilenamePboardType(NSString *filename)
|
||||
{
|
||||
return [NSString stringWithFormat:@"%@%@", namePrefix, filename];
|
||||
}
|
||||
|
||||
NSString *NSGetFileType(NSString *pboardType)
|
||||
{
|
||||
if ([pboardType hasPrefix: contentsPrefix]) {
|
||||
return [pboardType substringFromIndex: [contentsPrefix length]];
|
||||
}
|
||||
if ([pboardType hasPrefix: namePrefix]) {
|
||||
return [pboardType substringFromIndex: [namePrefix length]];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSArray *NSGetFileTypes(NSArray *pboardTypes)
|
||||
{
|
||||
NSMutableArray *a = [NSMutableArray arrayWithCapacity: [pboardTypes count]];
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < [pboardTypes count]; i++) {
|
||||
NSString* s = NSGetFileType([pboardTypes objectAtIndex:i]);
|
||||
|
||||
if (s && ! [a containsObject:s]) {
|
||||
[a addObject:s];
|
||||
}
|
||||
}
|
||||
if ([a count] > 0) {
|
||||
return [[a copy] autorelease];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue