Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

< Previous PageNext Page > Hide TOC

Handling Translations Using Pasteboards

The Pasteboard Manager supports data translators that present additional flavors available on a pasteboard. For example, a text-editing application may make two data flavors available when it places selected text onto the Clipboard pasteboard, plain and rich (RTF). A registered filter service can also make available another flavor, say com.apple.mytranslatorapp.uppercasetext that is translated from the plain text flavor. That is, when the receiving application obtains the array of data flavors to determine if it can handle them, the upper-case option is included as a promised flavor. These services are automatically available on any pasteboard you create or reference using the Pasteboard Manager.

If the receiving application chooses to obtain the upper-case text, the plain text is placed onto a new pasteboard and the filter service is invoked. The service obtains the raw data from the pasteboard, translates it accordingly, and then puts the translated data back on the pasteboard. The translated text is then placed back onto the Clipboard pasteboard where the the receiving application can obtain it.

If your application simply uses pasteboards for data sharing, any translation of pasteboard data occurs transparently. However, if you want to make a translation filter available, you need to write and register a filter service.

For example, say you want to implement a service that handles data translation (also called filtering) from standard text to all upper-case lettering. You must register this translation service by defining an NSFilter service in your application’s Info.plist file, such as follows:

<key>NSServices</key>
    <array>
        <dict>
            <key>NSFilter</key>
            <string>UpperCaseTranslation</string>
            <key>NSReturnTypes</key>
            <array>
                <string>com.apple.mytranslatorapp.uppercasetext</string>
            </array>
            <key>NSSendTypes</key>
            <array>
                <string>public.plain-text</string>
            </array>
            <key>NSSupportsDataTranslation</key>
            <string></string>
        </dict>
    </array>

This defines an NSFilter that can take data of type public.plain-text and translate it to data of type com.apple.mytranslatorapp.uppercasetext. Of course, the returned data can be any standard type as well.

Whenever data is placed onto a pasteboard, the Services Manager scans the flavors and checks them against its list of registered translators. If a translator exists for a given flavor, it can promise the translated flavor on the pasteboard. However, translators are not transitive; if translators exist to convert type A to B and type B to C, this does not mean that a translator from A to C exists. If the receiving application requests a translated flavor, the service that offers that translation receives a kEventServicePerform event.

The scope of the translation is entirely up to the filter creator. For example, you could create a filter to change plain text to Unicode text, turn encapsulated PostScript(EPS) images into GIFs, or even translate English into French. The only restriction is that you must be able to present the translation option as a unique UTI.

“Listing 1-7” shows how you might implement a filter service that offers text translation to either all upper-case or all lower-case lettering.

Listing 2-7  A filter service handler

OSStatus HandlePerformService( EventRef inEvent )// 1
{
    OSStatus            err = noErr;
    CFStringRef         serviceName, returnType;
    PasteboardRef       pasteboard;
    PasteboardItemID    item;
    CFDataRef           sourceData;
    CFIndex             sourceSize;
    CFMutableDataRef    returnData = NULL;
    const UInt8*        sourceBytes;
    UInt8*              returnBytes;
 
    err = GetEventParameter( inEvent, kEventParamServiceMessageName,// 2
         typeCFStringRef, NULL, sizeof( CFStringRef ), NULL, &serviceName );
    require_noerr( err, CantGetServiceName );
 
    err = GetEventParameter( inEvent, kEventParamPasteboardRef,// 3
         typePasteboardRef, NULL, sizeof( PasteboardRef ), NULL,
         &pasteboard );
    require_noerr( err, CantGetPasteboardRef );
 
    err = PasteboardGetItemIdentifier( pasteboard, 1, &item );// 4
    require_noerr( err, CantGetItemIdentifier );
 
    err = PasteboardCopyItemFlavorData( pasteboard, item,// 5
                CFSTR("public.plain-text"), &sourceData );
    require_noerr( err, CantGetSourceData );
 
    sourceSize = CFDataGetLength( sourceData );// 6
 
    returnData = CFDataCreateMutable( kCFAllocatorDefault, sourceSize );// 7
    require_action( returnData != NULL, CantCreateReturnData,
                    err = memFullErr );
 
    sourceBytes = CFDataGetBytePtr( sourceData );
    returnBytes = CFDataGetMutableBytePtr( returnData );
    CFDataSetLength( returnData, sourceSize );
 
    if( CFStringCompare( serviceName, CFSTR("UpperCaseTranslation"), 0 )
            == kCFCompareEqualTo )
    {
        returnType = CFSTR("com.apple.pasteboardpeeker.uppercasetext");// 8
 
        for( CFIndex i=0; i<sourceSize; i++ )// 9
            returnBytes[i] = (UInt8)toupper( sourceBytes[i] );
    }
    else
    {
        returnType = CFSTR("com.apple.pasteboardpeeker.lowercasetext");// 10
 
        for( CFIndex i=0; i<sourceSize; i++ )
            returnBytes[i] = (UInt8)tolower( sourceBytes[i] );
    }
 
    err = PasteboardClear( pasteboard );// 11
    require_noerr( err, CantClearPasteboard );
 
    // add the translated data
    err = PasteboardPutItemFlavor( pasteboard, item, returnType, // 12
            returnData, 0 );
    require_noerr( err, CantAddTranslatedData );
 
CantAddTranslatedData:
CantClearPasteboard:
 
    CFRelease( returnData );
 
CantCreateReturnData:
 
    CFRelease( sourceData );
 
CantGetSourceData:
CantGetItemIdentifier:
CantGetPasteboardRef:
CantGetServiceName:
 
    return err;
}

Here is how the code works:

  1. The HandlePerformService function receives an event reference from the kEventServicePerform event handler that calls it.

  2. Call GetEventParameter to obtain the service message that generated the perform event.

  3. For service events, a pasteboard storing the data to be filtered is stored in the event. Call GetEventParameter specifying the kEventParamPasteboardRef parameter to obtain it. This pasteboard was allocated for you, so you do not need to release it when you are done with it.

  4. This example assumes that the data to be filtered is the first item in the pasteboard.

  5. This example also assumes that the data in the item is of type public.plain-text.

  6. Call the Core Foundation function CFGetDataLength to determine the size of the data. As the text contains single-byte characters, this is also the length of the text string.

  7. Call the Core Foundation function CFDataCreateMutable to allocate memory to hold the translated string data.

  8. If the service request is to translate to upper-case, set the return type of the translated text to a unique UTI reflecting this. You must do this, because Translation Services looks for the translated type on the pasteboard (not the original) when retrieving the data.

  9. Use the toupper operator to convert the source text to all upper-case lettering.

  10. Handle the lower-case option in a similar manner as the upper-case option.

  11. Clear the pasteboard in preparation for adding the translated text.

  12. Call PasteboardPutItemFlavor to add the translated text to the pasteboard. After your service handler completes, the Services Manager puts the translated text back onto the original pasteboard so that the receiving application can retrieve it.

For more information about building and installing services, see Setting Up Your Carbon Application to Use the Services Menu.



< Previous PageNext Page > Hide TOC


Last updated: 2005-07-07




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.
Get information on Apple products.
Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Copyright © 2007 Apple Inc.
All rights reserved. | Terms of use | Privacy Notice