[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Data Exchange refers to the many high-level options GNUstep provides for allowing different applications to exchange common types of data. The sorts of services include "cut and paste", "drag and drop", service applications, filter services and distributed objects.
We begin our discussion with an explanation of pasteboards, which form the basis of data exchange in GNUstep. We will then go on to explain how your application can expose or consume these different sorts of data exchange services. However you receive data, it will most likely involve the use of pasteboards, hence the next section is very important.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A pasteboard is the helper object used to exchange data between applications. It is an instance of the NSPasteboard
class. Data is written to the pasteboard in different forms that it can be represented, so that the application or service receiving the data can use it.
There is a pasteboard server, a service provided with GNUstep which handles pasting between GNUstep applications. You may recognise it as the gpbs
application.
Every pasteboard has a name that can be used to identify it. This is a string, which should be unique, but some standard pasteboard names are defined for certain uses:
NSGeneralPboard
NSFontPboard
NSRulerPboard
NSFindPboard
NSDragPboard
You can retreive a pasteboard by name using the +pasteboardWithName:
method, or with a guaranteed unique name with the +pasteboardWithUniqueName
method.
All pasteboards also have any number of types. A type is simply one form of data that the pasteboard will contain, such as HTML data or text data. The supported data types are listed below:
Finally a pasteboard may or may not have an owner. An owner is an object implmenting the NSPasteboardOwner
informal protocol that can provide the pasteboard with data of a certain type upon request. If you don't supply an owner object, you should store the data onto the pasteboard straight away.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
You can get a pasteboard using the +pasteboardWithName:
method with one of the standard names above, or the +pasteboardWithUniqueName:
for a pasteboard with a name that is unique to the pasteboard server. You can also get a pasteboard based on the available filter services by calling +pasteboardByFilteringFile:
for a pasteboard containing file, accessible by all the data types that it can be filtered to. If you know the source data type, you can use +pasteboardByFilterData:ofType:
specifying a data object for a pasteboard that can convert data to different types that can be filtered from your data's type.
If you are constructing a pasteboard, you will want to call -setData:forType:
method to put the associated data in the pasteboard for another object to read it out. Use -declareTypes:owner:
to declare the types that this pasteboard will contain, and an owner object that will supply the data for those types that you don't explicitly write to the pasteboard.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
You can provide a pasteboard owner by implementing the NSPasteboardOwner
informal protocol. This is used for the "lazy" provision of data. The pasteboard will call methods on the owner when it can't find the data being requested already stored on it.
The first method to implement is -pasteboard:provideDataForType:
. This is called when the pasteboard doesn't have the data specified by type. You give it to the pasteboard by calling -setData:forType:
on the pasteboard.
We can also implement -pasteboardChangedOwner:
, which informs us that the owner has been changed and we no longer have to provide data to the pasteboard. GNUstep also has an extension, the -pasteboard:provideDataForType:andVersion:
which should be implemented when data of a certain version as well is required.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Cut, copy and paste is the most common service you will want to provide in your application. Thankfully, all standard GNUstep objects handle copying and pasting where commonly appropriate, such as the NSText variety of objects. However, in some cases it may be useful to provide copying and pasting services, especially for your own views or on customised GNUstep views.
The first thing to to is to provide two methods on your object called -cut:
, -copy:
, and/or -paste:
both taking an object (the sender) as their first parameter. This will enable Gorm's standard "Cut", "Copy" and "Paste" menu items if you place them in your interface.
You will usually use the general pasteboard for cut and paste, which can be retreived by going:
NSPasteboard* generalPB = [NSPasteboard pasteboardWithName:NSGeneralPboard]; |
The implementation of these methods should then follow. For cutting and copying:
Usually we use the general pasteboard, but you can create one with your own name if you like.
The next thing to do is specify which types of data you will provide on the pasteboard. Use the -declareTypes:owner:
method, passing an array of types, and optionally, an owner object.
You supply data to the pasteboard for pasting by using the -setData:forType:
method. If you have used an owner, make sure that it implements the NSPasteboardOwner
protocol and that it can return data in the form(s) specified in the previous step.
If you decide to provide data in a number of types, it is often recommended you supply the richest type directly to the pasteboard, and use an owner to supply more basic data types. Simply use if/else if
statements in the -pasteboard:provideDataForType:
method on your owner.
Pasting data is much simpler. Simply retreive the general pasteboard, and call the -stringForType:
or -propertyListForType:
method, passing in a type.
Make sure that you declare the types your pasteboard supports with the -declareTypes:owner:
method. You can specify nil for the owner if you are not using lazy data provision.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Drag and drop is often more complex. Many different standard views provide their own delegate protocols for receiving drag and drop events, and you should refer to the documentation for those (especially tableviews and outline views) before following the instructions in this section. However, this is still useful in explaining some important concepts.
Such operations consist of both a drag and a drop(16). The drag occurs when the user clicks their mouse button on a visible GUI element, and begins to mouse the mouse away from it. A drop occurs when the user moves the mouse over another GUI element and releases the mouse button. Obviously, dragging and dropping can only occur on visible elements of the screen that take up some real estate.
Below, we discuss dragging sources and dragging destinations, and what is required to make your views responsive as such.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
When a drag event is initiated, the -dragImage:at:offset: event:pasteboard: source:slideBack:
method is called on your subclass of NSView. In this method, you need to supply a dragging image, a pasteboard to hold the data, and and a dragging source object (specified by the source:
parameter).
The dragging source object should implement the NSDraggingSource
protocol. The main method to implement is -draggingSourceOperationMaskForLocal:
, whereas the others are used for dragging session events (and are otherwise optional). In this method, you should return the set of binary or-ed values corresponding to the permitted drag operations on this displayed image representation, listed below:
NSDragOperationNone
NSDragOperationCopy
NSDragOperationLink
NSDragOperationGeneric
NSDragOperationPrivate
NSDragOperationMove
NSDragOperationDelete
NSDragOperationAll
NSDragOperationEvery
You can specify more than one of the above by binary or-ing them together (the single pipe operator). Note that if you permit the NSDragOperationMove
or NSDragOperationDelete
methods, you must implement the -draggedImage:endedAt:operation:
method, which is called when a dragging operation is finished so that your source can cleanup any visual or internal data in the source (such as making the source image disappear).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A view or window that is to act as a dragging destination should be sent the message -registerForDraggedTypes:
with an array of the accepted dragging types. The view or window should then implement some of the methods in the NSDraggingDestination
informal protocol.
Some of these methods are listed below:
- draggingEntered:
sender
.
- prepareForDragOperation:
- performDragOperation:
- concludeDragOperation:
- draggingUpdated:
Hence to act as a dragging destination, you need to at least implement -draggingEntered:
and -performDragOperation
. Make sure that when you actually perform the drag operation, that you retreive the pasteboard being used from the dragging destination (see below), as opposed to just retreiving the NSDragPboard
named pasteboard, as you cannot be certain which pasteboard the dragging source has used for the drag operation.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Both NSDraggingSource
and NSDraggingDestination
use objects implementing the NSDraggingInformation
protocol to convey information about the drag'n'drop operation. You can use this to make better decisions in many of the above mentioned methods with relation to permitting/disallowing different drag types and drag operations. You never implement this protocol, however.
The pasteboard being used for the drag operation can be retreived via the -draggingPasteboard
method. The image being used for the drag operation and its location can be retrieved via the -draggingImage
and -draggingImageLocation
methods respectively. If you need to snap the image during the drag operation, use the -slideDraggedImageTo:
method, but only do this during -prepareForDragOperation:
in the destination object.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A service is a special type of application or tool that can be used to process data outside of the application. An application can both take advantage of services, or provide them to other applications. Like "cut and paste" and "drag'n'drop", services use pasteboard to receive data and send it back to the calling application.
A user can usually make use of a service by selecting something in your application (such as some text or an object) and selecting a service from the "Services" menu. You can also invoke services programmatically.
One way is to put a Services sub-menu in your interface file's menu using Gorm
(as mentioned above. The other way is to call the NSPerformService()
function. It takes two parameters, a service name and a pasteboard. If the service invocation is successful, the pasteboard will contain the output data from the service. The latter method is useful for filter services (described below).
A service becomes available to any NSResponder object in your application's interface. Most GNUstep classes are setup to consume services, but if you have your own NSView
or NSWindow
subclasses, you will need to implement extra methods so that it can make use of services. A service that isn't available to an object will not appear available in the Services menu.
Providing services is a little bit different, and requires a bit more work. You can implement a service as a normal GNUstep application, or as a special command-line type using the service.make
template in your GNUmakefile. Either way, you need to also provide extra information in your `Info-gnustep.plist' file that describes what services your application provides.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
There are two types of services you can provide: normal services and filter services. Normal services may either send, receive or both send and receive data. They are often useful for initiating outside processes based on simple string information, such as loading up a "New Message" window in your email client with an email address that the user has highlighted in your application. Such as service wouldn't need to return any data. These services also are registered to appear in the "Services" menu of applications.
For example, the user would highlight an email address in a text box, and then select "Send Email" from the services menu. GNUstep would then locate the associated service and put the email address on a pasteboard. The pasteboard is sent to the service application (and loaded if necessary), which processes it accordingly.
On the other hand, a filter service is much more specific. They are designed to convert data from one type to another, and are only ever invoked programatically i.e. they don't appear in the "Services" menu.
We begin making our application or command-line service ready for acting as a service by calling -setServicesProvider:
on the NSApplication
object, or by calling the NSRegisterServicesProvider()
function. Both take an object, which will provide the service, as a parameter, and the latter also takes a port name as a string, which will be used to contact the application. NSApplication
uses the name of your application as the port name.
Secondly, the object that will provide the service needs to implement a method in the form of: [methodname]:userData:error:
, where methodname is a custom, arbitrary name of the method. For example if you were to create a service that encrypts data and you want to call it something like -encryptData
, the method would take the form:
- (void) encryptData:(NSPasteboard*)pBoard userData:(NSString*)userData error:(NSString**)error; |
As you can see, the first part is arbitrary, but the rest must be the same for all services. It is the first part that you will use in the NSMessage
key below.
Lastly, all services need a special addition to their Info-gnustep.plist file, which should be included as a RESOURCE
in your `GNUmakefile'. See the GNUstep Makefile Manual for more details.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
As mentioned before, normal services may either send data, receive data, or both. Their Info-gnustep.plist file must have a top-level key named NSServices
, which becomes an array of dictionaries. This array has one dictionary per service that is provided.
Each service dictionary has the following keys:
-randomData:userData:error:
, this key should take a string value equal to "randomData"
.
NSStringPboardType
.
Services
and the menu item. It is useful for grouping related services in a sub-menu.
textToHtml:
. It accepts string data, and publishes the HTML back in string form.
Our example Info-gnustep.plist array could be:
{ .. (application specific keys) .. NSServices = ( { NSPortName = WebSiteEditor; NSMessage = textToHtml; NSSendTypes = ( NSStringPboardType ); NSReturnTypes = ( NSStringPboardType ); NSMenuItem = { default = "Convert to HTML"; }; NSTimeout = 25000; NSKeyEquivalent = { default = H; }; NSUserData = "NoBodyTags"; } .. (More service definitions) ); } |
As can be seen above, NSServices is an array containing one dictionary, which corresponds to one service. The service appears in the menu as "Convert to HTML", which expects string data.
A possible code implementation may be:
- (void) textToHTML:(NSPasteboard*)pboard userData:(NSString*)userData error:(NString**)error { NSString* data, *convertedData; if ([[pboard types] containsObject:NSStringPboardType]) { // Extract string data from pasteboard data = [pboard stringForType:NSStringPboardType]; // Convert to HTML as a string //.. // Put the result back onto the pasteboard [pboard declareTypes:[NSArrayWithObject:NSStringPboardType] owner:nil [pboard setString:convertedData forType:NSStringPboardType]; } else *error = Ïncorrect data type provided to textToHTML: service."; } |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
As mentioned before, filter services are not initiated by the user, but are initiated by programme's to convert data from one type to another. They also have entries in an application's `Info-gnustep.plist' NSServices array. These entries are dictionaries as well, but they contain the following keys:
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
An application registers the object that will be providing service(s) by calling -setServicesProvider:
on their NSApplication
object. Tool applications must call NSRegisterServicesProvider()
, which is a function that takes the service object and the port name (as specified by NSMessage
or NSFilter
in the `Info-gnustep.plist' file).
Once that is in code and your application has been installed, you also need to execute make_services
, which is a script that comes with GNUstep. It locates the Info-gnustep.plist file and builds a list of services. This list of services becomes available to applications started after the script is executed.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
For the most part, AppKit objects are implemented to take advantage of most service types where appropriate, especially in regard to string data. However, there are situations where you will want to register for service consumption yourself, or where you want to allow your custom views to consume services.
An NSResponder object must first register the pasteboard types it supports. Then, when a user tries to invoke a service, GNUstep first checks the responder chain for an object that can handle the service's input type, and then it queries the object for the data to be processed by the service. If data is returned from the service, GNUstep then gives the pasteboard back to the object for it's own processing.
Your object must at some point register it's ability to consume services of certain pasteboard types. It does this by calling -registerServicesMenuSendTypes: returnTypes:
with an array of send types (the pasteboard types the object can send to a service) and return types (the pasteboard types the object can receive from a service).
This method is to be only called once for your subclass. It is convenient to put it in your class' +initialize
method, which is usually called after your class is loaded into the runtime (and hence only once).
When sending data to a service, GNUstep must first check that your object can send those types of data before it requests the pasteboard from your object.
So that GNUstep can check whether your object is able to export the pasteboard types requested by the service, you must implement the -validRequestorForSendType:returnType:
method. It is passed a send pasteboard type and a return pasteboard type.
Your implementation should return an object if it is capable of handling that combination of send and return type (and is ready to do so), or return nil if it can't. It usually returns self
.
If GNUstep gets a positive answer to this method, it will then call -writeSelectionToPasteboard:types
on your object. You should implement this method to fill the pasteboard with data (or use lazy provision, as discussed earlier in this chapter). It should return YES
if it suceeds, or NO
if it fails.
If the service being invoked returns data, GNUstep will call -readSelectionFromPasteboard:
on your object when the service returns. This method should retrieve the service data from the pasteboard and use that data to update it's object's state.
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |