Programming Guide: Focus Transfer

# Programming Guide

## Focus Transfer

Part activation is the process of making a part and frame ready for editing. As noted previously, activation typically occurs when the user clicks the mouse button when the pointer is within a frame that is not currently active, but also happens when a window opens, when a window is activated, and as a result of drag-and-drop operations.

In the OpenDoc model of part activation, part editors use the concept of focus to activate and deactivate themselves (rather than being activated and deactivated by OpenDoc) and to arbitrate the transfer of several types of shared resources among themselves.

### Focus Types

A part makes itself the recipient of a certain type of user event or other action by obtaining the focus for it. A focus is a designation of ownership of a given shared resource, feature, or event type; for example, the frame that owns the keystroke focus receives all keystroke events until it passes ownership of the keystroke focus to another frame.

Focus types are defined as ISO strings, and the standard set defined by OpenDoc includes those in Table 3.

Table 3. Focus Types

Constant | ISO String | Description
--- | --- | ---
kODKeyFocus | "Key" | Keystroke events are sent to the frame with this focus.
kODMenuFocus | "Menu" | Menu events are sent to the frame with this focus.
kODSelectionFocus | "Selection" | Shift-click, Ctrl-click and Alt-click mouse events are sent to the frame with this focus. OpenDoc draws the active frame border around all facets of this frame.
kODModalFocus | "Modal" | The frame that owns this focus is notifying other frames that it is the only current modal frame.
kODScrollingFocus | "Scrolling" | Scrolling-specific keystroke events (such as Page Up and Page Down) are sent to the frame with this focus.
kODClipboardFocus | "Clipboard" | The frame that owns this focus has access to the clipboard.
kODMouseFocus | "Mouse" | The frame that owns this focus receives all mouse-within events whenever the pointer moves, and all mouse-down and mouse-up events, regardless of which facet the pointer is within.

To obtain event focuses, part editors request them by name from the arbitrator. (You obtain access to the arbitrator by calling the GetArbitrator method of the session object).

You need to convert focus names into tokens before using them in any method calls. You call the Tokenize method of the session object to convert ISO strings to tokens.

Focuses may be manipulated singly or in groups called focus sets. A focus set is an OpenDoc object (of class ODFocusSet) listing a group of focuses that a part editor wants to obtain or release as a group.

Focuses are owned by frames. In general, mouse events anywhere within the content area of an OpenDoc window always go to the most deeply embedded frame that encloses the click point. However, Shift-click, Ctrl-click or Alt-click events, regardless of their location, are sent to the frame with the selection focus to allow for extending selections.

OpenDoc does not require that the same frame own the selection focus, keystroke focus, and menu focus, although this is most often the case. Nor does OpenDoc require that the selection focus be in an active window, although this is usually the case.

In most cases, when a frame is activated, the part editor for that frame requests the selection focus, keystroke focus, and menu focus. A frame with scroll bars might also request the scrolling focus. Your part editor might create a focus set ahead of time, perhaps during part initialization, that includes the tokenized names of the focuses that your part expects to request when it becomes active. You use the arbitrator's CreateFocusSet method to create the focus set.

A simple part, such as a small text-entry field in a dialog box, might request only the selection focus and keystroke focus on receiving a mouse-up event within its frame area. An even simpler part, such as a button, might not even request the selection focus. It might simply track the mouse until the button is released and then run a script, never having changed the menu bar, put up palettes or rulers, or become active.

Your part editor can define additional focus types as needed. You can define other kinds of focus, perhaps to handle other kinds of user events (such as input from new kinds of devices). To create a new kind of focus, you need to create a new kind of focus module, the OpenDoc object that the arbitrator uses to determine focus ownership. "Direct Scripting" describes how to use focus modules to extend OpenDoc's focus management.

Focuses may be exclusive or nonexclusive. All of the standard focuses defined by OpenDoc are exclusive, meaning that only one frame at a time can own a focus. But if you create a new kind of focus, you can make it nonexclusive, meaning that several frames could share ownership of it.

### Arbitrating Focus Transfers

This section discusses how to request or relinquish focuses to activate or deactivate your frames.

#### Requesting Focuses

A part can request, for one of its frames, ownership of a single focus or a set of focuses. You request a focus by calling the arbitrator's RequestFocus method; you request a focus set by calling the arbitrator's RequestFocusSet method. If the request succeeds, your part's frame obtains the focus or focus set.

The arbitrator's RequestFocus and RequestFocusSet methods perform a two-stage transaction in transferring a focus or focus set:

1. The arbitrator first asks the current owning frame of each focus if it is willing to relinquish the focus, by calling the BeginRelinquishFocus method of the frame's part.

2. If any owner of the focus is unwilling to relinquish it, the arbitrator cancels the request by calling each part's AbortRelinquishFocus method. In this case, RequestFocus or RequestFocusSet returns false.

3. If all focus owners are willing to relinquish, the arbitrator calls each part's CommitRelinquishFocus method. In this case, RequestFocus or RequestFocusSet returns true.

#### Relinquishing Focuses

A part can relinquish focuses either on request or when a change to its state (such as the closure of its frame or a completion of a method) warrants it. An active part might unilaterally relinquish certain focuses (such as the clipboard focus) as soon as it is finished handling an event, but it might not relinquish other focuses (such as the selection focus) until another part asks for them. Nevertheless, most parts willingly relinquish the common focuses when asked.

Relinquishing focuses on request is a two-step process, because multiple focuses requested as a focus set must all be provided to the requestor simultaneously; if one is not available, none need be relinquished. Your part editor participates in the process through calls to its BeginRelinquishFocus, CommitRelinquishFocus, and AbortRelinquishFocus methods.

1. In your BeginRelinquishFocus method, you need do nothing other than return kODTrue or kODFalse, based on the type of focus and the identities of the frames (current and proposed focus owners) passed to you. In most cases, you can simply return kODTrue, unless your part is displaying a dialog box and another part is requesting the modal focus. In that case, because you do not want to yield the modal focus until your dialog box window closes, you return kODFalse. See "Acquiring and Relinquishing the Modal Focus" for more information.

2. Your part's CommitRelinquishFocus method, verifies that you have actually relinquished the focus type you responded to in BeginRelinquishFocus. The method should take appropriate action, such as removing menus or palettes, disabling menu items, removing highlighting, and performing whatever other tasks are part of losing that type of focus. Remember that the focus may possibly be moving from one frame to another of your part, so the exact actions can vary.

3. If, after your part responds with kODTrue to BeginRelinquishFocus, the focus is actually not transferred from your frame, OpenDoc calls your part's AbortRelinquishFocus method. If your part has done anything more than return the Boolean result in BeginRelinquishFocus, it can undo those effects in the AbortRelinquishFocus method.

4. If your part is one of several focus owners called to relinquish the focuses of a focus set, and if you return kODFalse to BeginRelinquishFocus, your CommitRelinquishFocus method is not called (because you chose not to give up the focus). However, your AbortRelinquishFocus method is still called (because all owners of a focus set are notified if any one refuses to relinquish the focus).

Your part does not relinquish its focus on request only. For example, in your part's DisplayFrameClosed and DisplayFrameRemoved methods, you should include a call to the arbitrator's RelinquishFocus or RelinquishFocusSet method to unilaterally relinquish any focus owned by the frame that you are closing. When your part closes, its ReleaseAll method should likewise relinquish all of its focuses. When your part finishes displaying a modal dialog box, it should relinquish or transfer the modal focus; when your part finishes accessing the clipboard, it should relinquish the clipboard focus.

#### Transferring Focus without Negotiation

There are some situations in which the normal process of requesting and relinquishing focuses is not used. Another piece of software interrupts your part's execution, and your part loses a focus without being given a chance to relinquish it, or gains focus without having asked for it. To handle those situations, your part editor must implement versions of the methods FocusAcquired and FocusLost. The arbitrator calls these methods when your part has just acquired, or just lost, a specified focus without having negotiated the transaction.

For example, a containing part, to support keyboard navigation, might call FocusAcquired in turn on each of its embedded parts as the user makes successive keystrokes. Or, if a custom input device with its own focus type were in use and then became detached, the part using the device might receive a call to its FocusLost method.

These are the interfaces to FocusAcquired and FocusLost:

    void FocusAcquired(in ODTypeToken focus,
                       in ODFrame ownerFrame);

    void FocusLost(in ODTypeToken focus,
                   in ODFrame ownerFrame);

Your FocusAcquired and FocusLost methods should perform any actions your part editor deems appropriate in response to having just acquired or lost a focus.

The arbitrator's methods TransferFocus and TransferFocusSet allow you to initiate a transfer of focus ownership without negotiation. A part can use these calls to transfer focus among parts and frames that it controls directly. For example, in a modal dialog box consisting of several parts, these methods can be used to transfer a focus from the outer part (the dialog box) directly to an inner part (such as a text field) and back.

When focus is transferred with TransferFocus or TransferFocusSet, the arbitrator calls the FocusAcquired method of the new frame's part and the FocusLost method of the previous frame's part. If the frame performing the transfer is the frame receiving or losing the focus, its FocusAcquired or FocusLost method is not called.

##### Calling Your Own FocusAcquired and FocusLost

It might seem natural to call your own FocusAcquired method when your request for focuses succeeds, or to call your own FocusLost method from your own CommitRelinquishFocus method. A better practice, however, is to have related methods call a shared private method, so that you maintain a clear separation between public and private interfaces.

### Recording Focus Transfers

Different frames may need different sets of focuses when activated. Selection focus, keystroke focus, and menu focus are commonly needed together. However, a frame with scroll bars might also need the scrolling focus, and a frame for a modeless dialog box might not even want the selection focus.

OpenDoc does not save or restore focus assignments. Therefore, during deactivation of windows and frames, and during closing of windows, you can record the state of focus ownership in order to restore it at a later activation or reopening. Your display frame's part info is an appropriate place to keep that information. Your part's initialization method might create a focus set with those focuses, to use whenever your display frames become active.

#### Frame Activation

When an inactive frame in a window becomes active, the part editors should negotiate the focus transfer and record the selection focus change for the respective frames. If you maintain that information, your activation and deactivation routines can check the state and exit quickly if not needed.

- If you are activating your part's frame, you might record, in a Boolean flag with a name such as fHasRequiredFoci in the frame's part info data, the fact that the frame has the selection focus. You can perform this action in your FocusAcquired method, after its call to the arbitrator's RequestFocusSet method succeeds.

- If you are deactivating your part's frame, you might set the fHasRequiredFoci flag in the frame's part info data to false. You can perform this action in your FocusLost method or your CommitRelinquishFocus method.

#### Window Activation

As mentioned in "Activate Events", all parts displayed in a window receive an activate event when the window becomes active, and a deactivate event when the window becomes inactive.

When an active facet of a frame becomes inactive through window deactivation, your part's HandleEvent method can store a flag in the facet's part info field to note that the facet was active before window deactivation. Your part then can also maintain, as a background selection, any selection it had been displaying.

Conversely, when a facet of a frame of your part receives an activate event because of window activation, your part's HandleEvent method can examine the state of the flag in the part info field to decide whether or not it was the active part when the window became inactive. If so, it should request the selection focus, reset the flag, and convert any background selection it may have maintained into a foreground selection.

#### On Closing and Reopening Documents

The root part of a newly opened window should activate itself. However, if an embedded part had the selection focus when the window closed, the root part can allow the embedded part to recapture that focus when the window reopens.

When the state of a window is saved in a document and the document is subsequently reopened, the root part recreates the window. If you want to restore the selection-focus state of your part (plus perhaps the selection itself), you can save the selection and the state of the selection-focus flag in your frame's part info data when the window is closed, and restore them when the window is opened (when your part's DisplayFrameConnected method is called).

If your part is the root part in this situation, you can either allow the embedded part's request for selection focus at this time, or you can acquire the selection focus yourself, when your own DisplayFrameAdded or DisplayFrameConnected method is called. (The root part is called last).

[ Top | Previous | Next | Contents | Index | Documentation Homepage ]