Ink Services Tasks

This section shows how you can use the Ink Services API to accomplish the following tasks:

Obtaining Parameters from Ink Text and Gesture Events

If you want to perform any of the other tasks described in this chapter, you will need to obtain one or more event parameters from Ink-related Carbon events. This section shows you how to obtain the parameters associated with Ink text and gesture events. Before you read this section, you should be familiar with the events and event parameters discussed in Ink-Related Carbon Events.

Ink Services generates Carbon events of class kEventClassInk. When Ink Services recognizes a phrase as text, it generates the event kEventInkText. You can extract the associated parameters—kEventParamInkTextRef and kEventParamInkTextKeyboardShortcut—by calling the Carbon Event Manager function GetEventParameter, as shown in Listing 2-1. Checking for the kEventParamInkTextKeyboardShortcut parameter provides an easy way for you to determine if the InkTextRef is likely to be a keyboard equivalent for a command instead of text. If this parameter is false, the you can use the function InkTextCreateCFString to obtain the recognized text associated with the Ink text object (InkTextRef). When you pass 0 to this function, you obtain the most likely interpretation.

Listing 2-1  Extracting parameters for the Ink text event

OSStatus status = noErr;
InkTextRef myInkTextRef;
Boolean  myKeyboardShorcut;
 
status = GetEventParameter (myEvent,
                kEventParamInkTextRef,
                typePtr,
                NULL,
                sizeof (Ptr),
                NULL,
                &myInkTextRef);
 
status = GetEventParameter (myEvent,
                kEventParamInkTextKeyboardShortcut,
                typeBoolean,
                NULL,
                sizeof (Boolean),
                NULL,
                &myKeyboardShortcut);
 
if (myKeyboardShortcut == false)
        return (eventNotHandledErr);
InkTextCreateCFString (myInkTextRef, 0);
// Your code to insert the text into the application document.

When Ink Services recognizes an Ink phrase as a gesture, Ink Services generates the event kEventInkGesture. You can extract the associated parameters—kEventParamInkGestureKind, kEventParamInkGestureBounds, and kEventParamInkGestureHotspot—using the code shown in Listing 2-2.

Listing 2-2  Extracting parameters for the Ink gesture event

OSStatus status = noErr;
UInt32 myGestureKind;
HIRect myGestureBounds;
HIPoint myGestureHotspot;
 
status = GetEventParameter (myEvent,
                kEventParamInkGestureKind,
                typeUInt32,
                NULL,
                sizeof (UInt32),
                NULL,
                &myGestureKind);
 
status = GetEventParameter (myEvent,
                kEventParamInkGestureBounds,
                typeHIRect,
                NULL,
                sizeof (HIRect),
                NULL,
                &myGestureBounds);
 
status = GetEventParameter (myEvent,
                kEventParamInkGestureHotspot,
                typeHIPoint,
                NULL,
                sizeof (HIPoint),
                NULL,
                &myGestureHotspot);

Handling Phrase Termination

The default behavior is for Ink Services to terminate a phrase when one of the following events occur:

You can use the function InkSetPhraseTerminationMode if your application does not want the default behavior or wants complete control over when Ink phrases are terminated. If you turn off automatic phrase termination, you must make sure you manage phrase termination appropriately for your application.

If you want to control phrase termination in your application, you must perform the following tasks:

Listing 2-3 shows an example of a handler for a hypothetical application that provides users with a “terminate phrase” button. The application must first install the handler (which, in this case, handles the event kEventInkPoint) and call the function InkSetPhraseTermination with the parameter kInkTerminationNone. A detailed explanation for each numbered line of code appears following the listing.

Listing 2-3  Code that handles phrase termination

{
 
    GetEventParameter (myInkPointEventRef, kEventParamEventRef,
                     typeEventRef, NULL, sizeof (EventRef), NULL,
                     &myMouseEventRef);// 1
 
    if (GetEventKind (myMouseEventRef) == kEventMouseDown)// 2
    {
        if (MyTestForButtonHit (myMouseEventRef) == true)// 3
        {
            if (InkIsPhraseInProgress() == true)// 4
            {
                InkTerminateCurrentPhrase(kInkSourceUser);// 5
                return (noErr);
            }
        }
    }
    return (eventNotHandledErr);// 6
 
}

Here’s what the code does:

  1. Extracts the mouse event reference from the Ink point event.

  2. Checks to see if the event is a mouse down event.

  3. Checks to see if the mouse down event is in the termination button provided by the application. The MyTestForButtonHit function is an application-defined function that determines if the mouse event is within the bounds of the termination button.

  4. Checks to see if Ink Services has an Ink phrase in progress; otherwise there is nothing to terminate. Note that the function InkIsPhraseInProgress should be called only to check whether a phrase is in progress for an Ink data stream that originate from the user, and not for one that originates from your application. See Ink Services Reference for more information.

  5. Terminates the phrase and returns noErr to indicate the event has been handled. You must pass the constant kInkSourceUser to specify that the function InkTerminateCurrentPhrase should be applied to the Ink data stream that originates from direct user input.

  6. Returns eventNotHandledErr if any of the previous if statements are false. If there is an Ink phrase in progress, it is not terminated.

Supporting Text Editing With Ink Gestures

Before you begin to write any code that supports text editing using Ink gestures, you should be thoroughly familiar with the terms “targeted gesture,” “untargeted gesture,” and “tentative gesture,” because you handle each of these gestures differently. These terms are described in Gestures. You should also be familiar with the Ink-gesture event and event parameters described in Ink Gesture Events, as gesture information is available to your application through Carbon events.

To support text editing with Ink gestures, your application must write and install an event handler for the Carbon event kind kEventInkGesture. The parameters associated with an Ink gesture event are:

Table 2-1  Gesture constants

Gesture

Gesture kind constant

Undo

kInkGestureUndo

Clear

kInkGestureClear

Select All

kInkGestureSelectAll

Escape

kInkGestureEscape

Cut

kInkGestureCut

Copy

kInkGestureCopy

Paste

kInkGesturePaste

Horizontal Space

kInkGestureLeftSpace

Horizontal Space

kInkGestureRightSpace

Tab

kInkGestureTab

Return

kInkGestureLeftReturn

Return

kInkGestureRightReturn

Delete

kInkGestureDelete

Join

kInkGestureJoin

After your event handler obtains the gesture kind from the event parameter kEventParamInkGestureKind (see Listing 2-2), it should handle gestures as follows:

Handling Targeted Gestures

You can use gesture and text relationships to determine the extent of the text modified by the gesture. Most targeted gestures have a defined hot spot that your application can use to determine the area to apply the editing action.

Table 2-2 lists targeted gestures, whether the gesture has a hot spot, and the editing actions you should perform when you handle the gesture.

Table 2-2  Targeted gestures, hot spots, and editing actions

Constant

Has a hot spot

Perform the following action ...

kInkGestureClear

No, use the gesture bounds

delete the text if the gesture bounds overlaps by 50% or more.

kInkGestureCut

Yes, the starting point of the gesture

cut the text (a single word in Roman languages) the hot spot overlaps.

kInkGestureCopy

Yes, the starting point of the gesture

copy the text (a single word in Roman languages) the hot spot overlaps.

kInkGesturePaste

Yes, the starting point of the gesture

paste the Clipboard contents into the location specified by the hot spot. Paste over a word if the hot spot is on a word.

kInkGestureLeftSpacekInkGestureRightSpace

Yes, the topmost point of the gesture

insert a single space character into the location specified by the hot spot.

kInkGestureTab

Yes, the starting point of the gesture

insert a single tab character into the location specified by the hot spot.

kInkGestureLeftReturn

Yes, the leftmost point of the gesture

insert a return (new line) character into the location specified by the hot spot.

kInkGestureRightReturn

Yes, the rightmost point of the gesture

insert a return (new line) character into the location specified by the hot spot.

kInkGestureJoin

No, you must extract two points from the gesture bounds

delete the space between the words specified by the top-left and top-right points of gesture bounds.

Figure 2-1 shows gesture bounds for four editing gestures—Cut, Horizontal Space, Clear, and Join. The Cut and Horizontal Space gestures have a hot spot but the Clear gesture does not. Rather, the bounds of the Clear gesture define its targeting area. The Join gesture doesn’t have a hot spot per se; instead it has two points that specify the two words that should be joined. These two points are contained in the gesture bounds, and your application must extract the points from the bounds.

Figure 2-1  Gesture bounds and hot spots
Gesture bounds and hot spots

To handle the Clear gesture, which does not have a hot spot, your application should apply the editing action to all of the text the gesture overlaps (to a sufficient degree—say, more than 50% of the onscreen area of each word—on a word-by-word basis). For example, the Clear gesture in Figure 2-2 is written over the words “or insertion point” so your application should delete those words. To make that determination you would first obtain the gesture bounds from the event kInkGestureEvent, then determine the words the gesture is written over, and delete those words.

Figure 2-2  The Clear gesture
The Clear gesture

For targeted gestures that have a hot spot, only the hot spot is relevant to the editing action your application takes; the gesture bounds aren’t. For example, to handle the Horizontal Space gesture your application must obtain the hot spot from the event kInkGestureEvent.The Horizontal Space gesture in Figure 2-3 is positioned between the letters “r” and “g.” To process this gesture, your application would determine the characters on either side of the hot spot, then insert a space in that position.

Figure 2-3  The Horizontal Space gesture
The Horizontal Space gesture

If the gesture hot spot is drawn outside your application’s windows or in a location where the gesture would not apply, the gesture should be treated as an untargeted gesture. That is, you should either return kEventNotHandledErr and let Ink Services handle the gesture for you, or you should apply the editing action to the current text selection or insertion point (if there is no selection).

Handling Untargeted Gestures

Your application does not need to handle untargeted gestures; it can simply return kEventNotHandledErr and let Ink Services handle the gesture for you. If for some reason you decide to handle untargeted gestures, you should perform the actions listed in Table 2-3. Note that the editing action for an untargeted gesture can depend on whether or not there is a selection.

Table 2-3  Actions specified by untargeted Ink gestures

Constant

If there is a selection, specifies to ...

If there is no selection, specifies to ...

kInkGestureUndo

undo the last action.

undo the last action.

kInkGestureClear

clear the current selection.

do nothing.

kInkGestureSelectAll

select all items in the window that has user focus.

select all items in the window that has user focus.

kInkGestureEscape

act as if the Escape key is pressed.

act as if the Escape key is pressed.

kInkGestureCut

cut the current selection.

do nothing.

kInkGestureCopy

copy the current selection.

do nothing.

kInkGesturePaste

paste the Clipboard contents over the current selection.

paste the Clipboard contents into the insertion point.

kInkGestureLeftSpacekInkGestureRightSpace

replace the current selection with a single space character.

insert a single space character at the insertion point.

kInkGestureTab

replace the current selection with a single tab character.

insert a single tab character.

kInkGestureLeftReturnkInkGestureRightReturn

replace the current selection with a return (new line) character.

insert a return (new line) character.

kInkGestureDelete

delete the current selection.

delete the item immediately preceding the insertion point.

Implementing a Correction Model

Ink Services communicates events through the Carbon Event Manager. Once Ink Services interprets an Ink phrase, it sends your application the Carbon event kind kEventInkText whose associated parameter is a reference to an opaque Ink text object (InkTextRef). The Ink text object contains the original Ink entered by the user and a list of interpretations for the Ink in ranked order. Ink Services creates a list of up to five possible interpretations. The most likely interpretation is the first item in the list, with less likely interpretations appearing in rank order after this item. (See Ink Window for details on how Ink orders the interpretations.)

Your application can implement an easy-to-use correction model by performing the following tasks:

You would also have to remove the single separator item identified by 'inks' and the Ink data item identified by 'inkd'. Or you could simply dispose of the old menuRef and create and populate a new one.

When a user selects an item from the list of alternates, Ink Services reorders the internal alternates list within the source Ink text object (InkTextRef). Thus the user’s choice persists in the system data structures without requiring your application to call any additional functions. If it is important for your application to maintain the original order of alternates, then you must use your own internal data structures to keep track of the original list.

Implementing Deferred Recognition

Deferred recognition is the ability to convert pen strokes to text at some time other than when the strokes are first written by the user. If you want to implement deferred recognition, your application must be responsible for collecting Ink input and deciding when recognition occurs. Your application can draw its own Ink by either disabling Ink Services or by requesting that Ink Services doesn’t process events it would otherwise have handled. Your application can then access all relevant data directly from standard mouse events.

To implement deferred recognition, your application must perform the following tasks:

A similar approach may be used to import larger quantities of data, gathered in an offline mode, potentially using a proprietary data format, such as for a digital pen that stores ink written on paper pads. Converting such data into InkPoint arrays and invoking the functions InkAddStrokeToCurrentPhrase and InkTerminateCurrentPhrase would allow such devices to use the recognition services provided by Ink Services.