/**
The pasteboard system is the primary mechanism for data exchange * between OpenStep applications. It is used for cut and paste of data, * as the exchange mechanism for services (as listed on the * services menu), for communicating with a spelling server in order to * perform spell checking, and for filter services which convert * data of one typ to another transparently. *
*Pasteboards are identified by names, some of which are standard * and are intended to exist permenantly and be shared between all * applications, others are temporary or private and are used to handle * specific services. *
*All data transferred to/from pasteboards is typed. Mostly * using one of several standard types for common data or using standardised * names which identify particular kinds of files and their contents * (see the NSCreateFileContentsPboardType() an * NSCreateFilenamePboardType() functions for details). It is also possible * for cooperating applications to use their own private types ... any string * value will do. *
*Each pasteboard has an owner ... an object which declares the
* types of data it can provide. Unless versions of the pasteboard data
* corresponding to all the declared types are written to the pasteboard,
* the owner is responsible for producing the data for the pasteboard when
* it is called for (lazy provision of data).
* The pasteboard owner needs to implement the methods of the
* NSPasteboardOwner informal protocl in order to do this.
*
Returns the pasteboard for the specified name. Creates a new pasreboard * if (and only if) one with the given name does not exist. *
* Standard pasteboard names are - * */ + (NSPasteboard*) pasteboardWithName: (NSString*)aName { NS_DURING { idCreates 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.
* The type of data in the file is inferred from the file extension.
*
No filtering is actually performed until some object asks the * pasteboard for the data, so calling this method is quite inexpensive. *
*/ + (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; } /** *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.
* The type of data in the file is inferred from the file extension.
*
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. *
*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. *
*/ + (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.Adds newTypes to the pasteboard and declares newOwner to be the owner * of the pasteboard. Use only after -declareTypes:owner: has been called * for the same owner, because the new owner may not support all the types * declared by a previous owner. *
*Returns the new change count for the pasteboard, or zero if an error * occurs. *
*/ - (int) addTypes: (NSArray*)newTypes owner: (id)newOwner { int count = 0; NS_DURING { count = [target addTypes: newTypes owner: newOwner pasteboard: self oldCount: changeCount]; if (count > 0) { changeCount = count; } } NS_HANDLER { count = 0; [NSException raise: NSPasteboardCommunicationException format: @"%@", [localException reason]]; } NS_ENDHANDLER return count; } /** *Sets the owner of the pasteboard to be newOwner and declares newTypes
* as the types of data supported by it.
* This invalidates existing data in the pasteboard (except where the GNUstep
* -setHistory: extension allows multi-version data to be held).
*
The value of newOwner may be nil, but if it is, data should * immediately be written to the pasteboard for all the value in newTypes * as a nil owner cannot be used for lazy supply of data. *
*This increments the change count for the pasteboard and the new
* count is returned, or zero is returned if an error occurs.
* Where -setChangeCount: has been used, the highest count to date
* is incremented and returned, rather than the last value specified
* by the -setChangeCount: method.
*
The types you declare can be arbitrary strings, but as at least two
* applications really need to be aware of the same type for it to be
* of use, it is much more normal to use a predefined (standard) type
* or a type representing the name or content of a particular kind of
* file (returned by the NSCreateFilenamePboardType() or
* NSCreateFilenamePboardType() function).
* The standard type for raw data is
* NSGeneralPboardType
*
Writes data of type dataType to the pasteboard server so that other
* applications can read it. The dataType must be one of the types
* previously declared for the pasteboard.
* All the other methods for writing data to the pasteboard call this one.
*
Returns YES on success, NO if the data could not be written for some * reason. *
*/ - (BOOL) setData: (NSData*)data forType: (NSString*)dataType { BOOL ok = NO; NS_DURING { ok = [target setData: data forType: dataType isFile: NO oldCount: changeCount]; } NS_HANDLER { ok = NO; [NSException raise: NSPasteboardCommunicationException format: @"%@", [localException reason]]; } NS_ENDHANDLER return ok; } /** *Serialises the data in the supplied property list and writes it to the * pasteboard server using the -setData:forType: method. *
*Data written using this method can be read by -propertyListForType: * or, if it was a simple string, by -stringForType: *
*If the data is retrieved using -dataForType: then it needs to be * deserialized into a property list. *
*/ - (BOOL) setPropertyList: (id)propertyList forType: (NSString*)dataType { NSData *d = [NSSerializer serializePropertyList: propertyList]; return [self setData: d forType: dataType]; } /** *Writes string it to the pasteboard server using the * -setPropertyList:forType: method. *
*The data may subsequently be read from the reciver using the * -stringForType: or -propertyListForType: method. *
*If the data is retrieved using -dataForType: then it needs to be * deserialized into a property list. *
*/ - (BOOL) setString: (NSString*)string forType: (NSString*)dataType { return [self setPropertyList: string forType: dataType]; } /** *Writes the contents of the file filename to the pasteboard server
* after declaring the type NSFileContentsPboardType as well as a type
* based on the file extension (given by the NSCreateFileContentsPboardType()
* function) if those types have not already been declared.
* If the filename has no extension, only NSFileContentsPboardType is used.
*
Data written to a pasteboard by this method should be read using * the -readFileContentsType:toFile: or -readFileWrapper method. *
*If the data is retrieved using -dataForType: then it needs to be * deserialized by the NSFileWrapper class. *
*/ - (BOOL) writeFileContents: (NSString*)filename { NSFileWrapper *wrapper; NSData *data; NSArray *types; NSString *ext = [filename pathExtension]; BOOL ok = NO; wrapper = [[NSFileWrapper alloc] initWithPath: filename]; data = [wrapper serializedRepresentation]; RELEASE(wrapper); if ([ext length] > 0) { types = [NSArray arrayWithObjects: NSFileContentsPboardType, NSCreateFileContentsPboardType(ext), nil]; } else { types = [NSArray arrayWithObject: NSFileContentsPboardType]; } if ([[self types] isEqual: types] == NO) { if ([self declareTypes: types owner: owner] == 0) { return NO; // Unable to declare types. } } NS_DURING { ok = [target setData: data forType: NSFileContentsPboardType isFile: YES oldCount: changeCount]; } NS_HANDLER { ok = NO; [NSException raise: NSPasteboardCommunicationException format: @"%@", [localException reason]]; } NS_ENDHANDLER return ok; } /** *Writes the contents of the file wrapper to the pasteboard server * after declaring the type NSFileContentsPboardType as well as a type * based on the file extension of the wrappers preferred filename if * those types have not already been declared. *
*Raises an exception if there is no preferred filename. *
*Data written to a pasteboard by this method should be read using * the -readFileContentsType:toFile: or -readFileWrapper method. *
*If the data is retrieved using -dataForType: then it needs to be * deserialized by the NSFileWrapper class. *
*/ - (BOOL) writeFileWrapper: (NSFileWrapper *)wrapper { NSString *filename = [wrapper preferredFilename]; NSData *data; NSArray *types; NSString *ext = [filename pathExtension]; BOOL ok = NO; if (filename == nil) { [NSException raise: NSInvalidArgumentException format: @"Cannot put file on pastboard with " @"no preferred filename"]; } data = [wrapper serializedRepresentation]; if ([ext length] > 0) { types = [NSArray arrayWithObjects: NSFileContentsPboardType, NSCreateFileContentsPboardType(ext), nil]; } else { types = [NSArray arrayWithObject: NSFileContentsPboardType]; } if ([[self types] isEqual: types] == NO) { if ([self declareTypes: types owner: owner] == 0) { return NO; // Unable to declare types. } } NS_DURING { ok = [target setData: data forType: NSFileContentsPboardType isFile: YES oldCount: changeCount]; } NS_HANDLER { ok = NO; [NSException raise: NSPasteboardCommunicationException format: @"%@", [localException reason]]; } NS_ENDHANDLER return ok; } /** * Returns the first type listed in types which the receiver has been * declared (see -declareTypes:owner:) to support. */ - (NSString*) availableTypeFromArray: (NSArray*)types { NSString *type = nil; NS_DURING { int count = 0; type = [target availableTypeFromArray: types changeCount: &count]; changeCount = count; } NS_HANDLER { type = nil; [NSException raise: NSPasteboardCommunicationException format: @"%@", [localException reason]]; } NS_ENDHANDLER return type; } /** * Returns all the types that the receiver has been declared to support.Obtains data of the specified dataType from the pasteboard, deserializes * it to the specified filename and returns the file name (or nil on failure). *
*This method should only be used to read data written by * the -writeFileContents: or -writeFileWrapper method. *
*/ - (NSString*) readFileContentsType: (NSString*)type toFile: (NSString*)filename { NSData *d; NSFileWrapper *wrapper; if (type == nil) { type = NSCreateFileContentsPboardType([filename pathExtension]); } d = [self dataForType: type]; if (d == nil) { d = [self dataForType: NSFileContentsPboardType]; if (d == nil) return nil; } wrapper = [[NSFileWrapper alloc] initWithSerializedRepresentation: d]; if ([wrapper writeToFile: filename atomically: NO updateFilenames: NO] == NO) { RELEASE(wrapper); return nil; } RELEASE(wrapper); return filename; } /** *Obtains data of the specified dataType from the pasteboard, deserializes * it and returns the resulting file wrapper (or nil). *
*This method should only be used to read data written by * the -writeFileContents: or -writeFileWrapper method. *
*/ - (NSFileWrapper*) readFileWrapper { NSData *d = [self dataForType: NSFileContentsPboardType]; if (d == nil) return nil; return AUTORELEASE([[NSFileWrapper alloc] initWithSerializedRepresentation: d]); } /** *Obtains data of the specified dataType from the pasteboard, deserializes * it and returns the resulting string (or nil). *
*The string should have been written using the -setString:forType: or * -setPropertyList:forType: method. *
*/ - (NSString*) stringForType: (NSString*)dataType { NSString *s = [self propertyListForType: dataType]; if ([s isKindOfClass: [NSString class]] == NO) { s = nil; } return s; } @end /** * GNUstep specific extensions ...GNUstep adds a mechanism for mapping between OpenStep pasteboard * types and MIME types. This is useful for interopration with other * systems, as MIME types have come into common usage (long after the * OpenStep specification was created). *
*The other extension to the pasteboard system produced by GNUstep * is the ability to keep a history of recent items placed in a * pasteboard, and retrieve data from that history rather than just * the current item. *
*/ @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. *
*The value of count must be one which has previously been returned * by -declareTypes:owner: and should not be further in the past than * specified by the -setHistory: method. *
*/ - (void) setChangeCount: (int)count { useHistory = YES; changeCount = count; } /** * Sets the number of changes for which pasteboard data is kept.Returns a standardised pasteboard type for file contents, * formed from the supplied file extension. *
*Data written to a pasteboard with a file contents type should * be written using the [NSPasteboard-writeFileContents:] or * [NSPasteboard-writeFileWrapper:] method. Similarly, the data should * be read using the [NSPasteboard-readFileContentsType:toFile:] or * [NSPasteboard-readFileWrapper] method. *
*/ NSString* NSCreateFileContentsPboardType(NSString *fileType) { NSString *ext = [fileType pathExtension]; if ([ext length] == 0) { ext = fileType; } return [NSString stringWithFormat: @"%@%@", contentsPrefix, ext]; } /** *Returns a standardised pasteboard type for file names, * formed from the supplied file extension. *
*Data written to a pasteboard with a file names type should
* be a single name writen using [NSPasteboard-setString:forType:] or
* an array of strings written using
* [NSPasteboard-setPropertyList:forType:].
* Similarly, the data should be read using
* the [NSPasteboard-stringForType:] or
* [NSPasteboard-propertyListForType:] method.
*
See also the NSGetFileType() and NSGetFileTypes() functions.
*/
NSString*
NSCreateFilenamePboardType(NSString *fileType)
{
NSString *ext = [fileType pathExtension];
if ([ext length] == 0)
{
ext = fileType;
}
return [NSString stringWithFormat: @"%@%@", namePrefix, ext];
}
/**
* Returns the file type (fileType extension) corresponding to the
* pasteboard type given.
* This is a counterpart to the NSCreateFilenamePboardType() function.
*/
NSString*
NSGetFileType(NSString *pboardType)
{
if ([pboardType hasPrefix: contentsPrefix])
{
return [pboardType substringFromIndex: [contentsPrefix length]];
}
if ([pboardType hasPrefix: namePrefix])
{
return [pboardType substringFromIndex: [namePrefix length]];
}
return nil;
}
/**
* Returns the file types (filename extensions) corresponding to the
* pasteboard types given.
*/
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 AUTORELEASE([a copy]);
}
return nil;
}