< Previous PageNext Page > Hide TOC

Using Services

The default nib file created for new Cocoa applications contains a Services menu in the application menu, so there is nothing else you need to do for your application to work with the services facility; your application automatically has access to all appropriate services provided by other applications. If you need to construct menus programmatically, you simply designate the NSMenu that you want as your Services menu with NSApplication’s setServicesMenu: method.

If you subclass NSView or NSWindow (or any other subclass of NSResponder), you need to implement it such that it interacts properly with the services facility. Tying custom NSViews or NSWindows into the services facility falls into the following steps:

  1. Registering your user-interface objects for services

  2. Validating the Services menu items for the current selection

  3. Sending the current selection to the service

  4. Receiving data from the service to replace the current selection

These steps are illustrated in Figure 1.


Figure 1  Using services

Using services

When a pure Service provider is invoked (in other words, no send types), step 3 is skipped. When a pure Service processor is invoked (in other words, no return types), step 4 is skipped.

The following sections cover each of these steps. A final section, “Invoking a Service Programmatically,” shows how to invoke a service in your code.

Contents:

Registering Objects for Services
Validating Services Menu Items
Sending Data to the Service
Receiving Data from the Service
Invoking a Service Programmatically


Registering Objects for Services

The Services menu does not contain every service offered by other applications. For example, in a text editor a service to invert a bitmapped image is of no use and should not be offered. Which services appear in the Services menu is determined by the data types that the objects in the application—specifically the NSResponder objects—can send and receive through the pasteboard.

An NSResponder registers these data types using the NSApplication Objective-C method registerServicesMenuSendTypes:returnTypes: or Java method registerServicesMenuTypes. Application Kit objects already do this for the basic text services, but your custom NSResponder subclass must do this to expand the list. A convenient location is in your subclass’s initialize class method, which is guaranteed to be invoked by the runtime before any other method of the class. All types used by instances of the class must be registered, even if they are not always available; Services menu items are enabled and disabled dynamically based on what is available at the moment, as described in “Validating Services Menu Items.”

An object does not have to register the same types for both sending and receiving. Suppose you are writing a rich text editor that can send unformatted and rich text, but can only receive unformatted text. Here is a portion of the initialization method for a text-editor’s NSView subclass:

+ (void)initialize
{
    static BOOL initialized = NO;
    /* Make sure code only gets executed once. */
    if (initialized == YES) return;
    initialized = YES;
 
    sendTypes = [NSArray arrayWithObjects:NSStringPboardType,
                    NSRTFPboardType, nil];
    returnTypes = [NSArray arrayWithObjects:NSStringPboardType,
                    nil];
    [NSApp registerServicesMenuSendTypes:sendTypes
                    returnTypes:returnTypes];
    return;
}

Your NSResponder object can register any pasteboard data type, public or proprietary, common or rare. If it handles the public and common types, of course, it has access to more services. See the NSPasteboard class specification for a list of standard pasteboard data types.

Validating Services Menu Items

While your application is running, various types of data can be selected and available for transfer on the pasteboard. If a service does not apply to the type of the selected data, its menu item needs to be disabled. To check whether a service applies, the application object sends validRequestorForSendType:returnType: messages to Objective-C objects, and validRequestorForTypes to Java objects, in the responder chain to see whether they have data of the type used by that service. While the Services menu is visible, this method is invoked frequently—typically many times per event—to ensure that the menu items for all service providers are properly enabled: It is sent for each combination of send and return types supported by each service and possibly for many objects in the responder chain. Because this method is invoked so frequently, it must be fast so that event handling does not fall behind the user’s actions.

The following example shows how this method can be implemented for an object that handles unformatted text:

- (id)validRequestorForSendType:(NSString *)sendType
            returnType:(NSString *)returnType
{
    if ( (!sendType || [sendType isEqual:NSStringPboardType]) &&
        (!returnType || [returnType isEqual:NSStringPboardType]) ) {
        if ( (!sendType || [self selection]) &&
            (!returnType || [self isEditable]) ) {
            return self;
        }
    }
    return [super validRequestorForSendType:sendType
            returnType:returnType];
}

This implementation checks both the types indicated and the state of the object. The object is a valid requestor if the send and return types are unformatted text or simply are not specified, and if the object has a selection and is editable (when send and return types are given). If this object cannot handle the service request in its current state, it invokes its superclass’ implementation.

validRequestorForSendType:returnType: is sent along an abridged responder chain, comprising only the responder chain for the key window and the application object. The main window is excluded.

Sending Data to the Service

When the user chooses a Services menu command, the responder chain is checked with validRequestorForSendType:returnType: and the first object that returns a value other than nil is called upon to handle the service request by providing data (if any is required) with a writeSelectionToPasteboard:types: message. Java objects are sent a writeSelectionToPasteboardOfTypes message. You can implement this method to provide the data immediately or to provide the data only when it is actually requested. Here is an implementation for an object that writes unformatted text immediately:

- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard
types:(NSArray *)types
{
    NSArray *typesDeclared;
 
    if ([types containsObject:NSStringPboardType] == NO) {
        return NO;
    }
    typesDeclared = [NSArray arrayWithObject:NSStringPboardType];
    [pboard declareTypes:typesDeclared owner:nil];
    return [pboard setString:[self selection]
                    forType:NSStringPboardType];
}

This method returns YES if it successfully writes or declares any data and NO if it fails. If you have large amounts of data or you can provide the data in many formats, you should provide the data only on demand. You declare the available types as above, but with an owner object that responds to pasteboard:provideDataForType:. See the NSPasteboard class specification for more details.

Receiving Data from the Service

Once the service requestor writes data to the pasteboard, it waits for a response as the service provider is invoked to perform the operation; if the service does not return data, of course, the requesting application simply continues running and none of the following applies. The service provider reads the data from the pasteboard, works on it, and then returns the result. At this point the service requestor is sent a readSelectionFromPasteboard: message telling it to replace the selection with whatever data came back. (The Java method has the same name.) The simple text object can implement this method as follows:

- (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pboard
{
    NSArray *types;
    NSString *theText;
 
    types = [pboard types];
    if ( [types containsObject:NSStringPboardType] == NO ) {
        return NO;
    }
    theText = [pboard stringForType:NSStringPboardType];
    [self replaceSelectionWithString:theText];
    return YES;
}

This method returns YES if it successfully reads the data from the pasteboard, NO otherwise.

Invoking a Service Programmatically

Though the user typically invokes a standard service by choosing an item in the Services menu, you can invoke it in code using this function:

BOOL NSPerformService( NSString *serviceItem, NSPasteboard *pboard )

This function returns YES if the service is successfully performed, NO otherwise. serviceItem is the name of a Services menu item (in any language). It must be the full name of the service, including the submenu and slash; for example, “Mail/Selection”. pboard contains the data to be used for the service, and when the function returns contains the data resulting from the service. You can then do with the data what you wish.



< Previous PageNext Page > Hide TOC


© 2003, 2002 Apple Computer, Inc. All Rights Reserved. (Last updated: 2002-11-12)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.