[ < ] | [ > ] | [ << ] | [ 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
The general pasteboard, often used with copy and paste.
NSFontPboard
Used for the exchange of font data.
NSRulerPboard
Used for the exchange of ruler data.
NSFindPboard
Used for "Find and Replace" editing.
NSDragPboard
Used in the exchange of drag’n’drop data.
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:
See the AppKit manual for more information about storing these types of data on a pasteboard.
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
No drag operations are permitted with this data.
NSDragOperationCopy
This data can be copied.
NSDragOperationLink
This data can be "shared". FIXME: WTF does this mean.
NSDragOperationGeneric
The type of drag operation that this is can be defined by the dragging destination.
NSDragOperationPrivate
This type of drag operation is defined privately by the source and destination objects, and hence negotiated between them.
NSDragOperationMove
The data represented by this drag operation can be moved to the destination.
NSDragOperationDelete
The destination can be responsible for deleting the data.
NSDragOperationAll
NSDragOperationEvery
All the above drag operations are acceptable.
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:
This is method is called when the user drags something into the frame of your window or view. Use it to return what dragging types you will permit for the dragging info passed in sender
.
- prepareForDragOperation:
This is called just after the user has dropped the dragged object. Use this method to make any preparations for the drop. Return YES to cancel the drop.
- performDragOperation:
This method is called so that you can perform the drop operation. This method is a must to implement.
- concludeDragOperation:
This is again optional, and can be used to perform any cleanup or post-drop operations.
- draggingUpdated:
This is called periodically as the drag image is moved within your frame. It can be optionally implemented to update the drag operation with different drag types (returned) as the user moves the drag images over various parts of your view. It may be useful if drag operations are context sensitive with respect to the graphical elements that your view displays.
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:
This is the first part of the method name (as described above). For example, if your services provider object implements a method called -randomData:userData:error:
, this key should take a string value equal to "randomData"
.
This key contains an array of the types of data that your service provider can handle (the types of data that may be sent to it). These types are the string values defined earlier for pasteboard types. Simply use the same name as used in the source code, e.g. NSStringPboardType
.
This is an array of string values that contains the types your service provider can return.
A service must be contacted via a distributed objects port, and this string value must contain the name of the port your application will be listening on for message. Unless you are not writing an application, this is usually set to the name of the application.
This is a dictionary used to set the menu item name for this service. Each key in the dictionary is the name of a language, with the value set to a string that will be displayed as the menu item for this service in the set language. You can also provide a default key, which will be displayed if none of the translations you have provided match the user’s language settings. You may also place one forward slash character (’/’) in the menu item name, which will be used to split the item into a sub-menu of Services
and the menu item. It is useful for grouping related services in a sub-menu.
This key is optional, and is set to a string value which may be whatever you like. It is passed to the method implementing the service. Use this if you want the one method to handle a number of service implementations, which are selected based on this string.
This is an optional dictionary which contains the key equivalents to the menu items you have listed in NSMenuItem. Each dictionary key is the name of a language (or default as described above) with its value set to a single letter that corresponds to a keyboard key.
This key is optional, and specifies how long the system should wait for the service provider to complete providing the service. It is a number in milliseconds. By default, the system waits 30 seconds.
This is an optional string value that contains the path of the executable which should be launched if the service is not already running. You will not usually need this for normal applications.
We want to provide a service that turns ordinary string data into coded HTML text. Our service application is called "WebSiteEditor" and the method that provides the HTML translation is called 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:
This is the equivalent of the NSMessage key used for normal services. It is the name of the distributed objects port that the filter service will listen on for messages. It again is usually set to the name of the application, but as filter services are more likely to be standalone tools, this one can differ somewhat.
This is an optional key that specifies a string value corresponding to a different input mechanism than the usual distributed objects message passing. These values may be:
The data is placed on a pasteboard. It is not changed.
The data is the name of a file. The contents of this file will be placed on the pasteboard instead.
The data is the name of a file. This file is passed as an argument to a command-line programme, which is executed. The stdout of the programme is placed on the pasteboard instead.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Before a service can be consumed by applications, it must be registered programatically and on the command line.
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] | [ ? ] |
This document was generated by Adam Fedor on December 24, 2013 using texi2html 1.82.