Programming Guide: Linking

# Programming Guide

## Linking

Linking is a mechanism for placing information into a part and allowing that information to be updated, based on changes to source information in another part or document.

Linking support in OpenDoc is a combination of event-handling code and storage code. It includes a set of notification calls that keep the transferred data synchronized with its source.

Some aspects of linking are closely related to the other data-transfer objects. Link specifications, used in writing data to the clipboard or drag and drop object, are described in "Link Specification". The interactions between cutting and pasting and the preservation of link-related objects are listed in "Transfer Rules for Links and Link Sources".

### Link Concepts

Linking requires the cooperation of one or two parts with several linking-related objects. Figure 67 is a schematic illustration of the objects and data involved in linking. The figure shows a link between two separate parts (which can be in the same document or in different documents), although links can also occur within a single part.

Figure 67. Objects and Data Involved in Linking

View figure.

The user creates a link by requesting it during a paste or drop operation. The part that contains the source of the information to be linked is called the source part. The content that is to be copied and sent to another part is called the source content or source. The source part creates an object, the link source, that contains a copy of the source data; that copy is stored in the same document as the source part.

The part that contains the destination of the information to be linked is called the destination part. The content that is actually copied into the destination part is called the destination content or destination. The draft object of the destination part creates an object, the link, that is stored in the same document as the destination part.

The link-source object contains references to all link objects for which it is the source. The user is free to edit the source of a link; when a change occurs in the source part and the link needs to be updated, the source part copies the source content into the link-source object. The destination part then copies the content from the link object into the destination itself.

OpenDoc links are unidirectional; changes are propagated from the source to the destination only. You should not permit users to edit any link destinations that you maintain; such edits would be overwritten when the link is updated.

Whether it contains the source or the destination of a link, your part is responsible for defining the exact content that constitutes the linked data and for knowing what visual area it covers in your part's display. Linked data, whether source or destination, is part of your own content; you store and manipulate it according to your own content model and under your own control.

#### Link Update ID

Each link source and each link destination must have an associated update ID. A link source or link destination is a region of content. The source part determines the update ID associated with the link and updates the ID each time the source changes. The destination part stores the update ID of the link that was current at the time the destination was last updated through the link.

Whenever the content in your part changes, you need to assign a new update ID to all link sources directly affected by that change. All link sources affected by a given change must have the same update ID. You obtain a new update ID by calling the session object's UniqueChangeID method.

When you update a link source, you can save the update ID in your source content and use it later to determine whether your source content and the content of the link-source object are identical.

A circular link or recursive link can occur when changes to a link's destination directly or indirectly affect its source. To avoid endless recursive updates, it is important that your part take these steps:

- Whenever your part updates a link destination that also is contained in a link source for which automatic updating is specified, it must propagate the update ID passed to it. Your part must update the affected source with the update ID passed to the destination, rather than calling UniqueChangeID.

  It is possible that your part containing the link destination is embedded in the link source content of another part. In that case, you must call your frame's ContentUpdated method so that the part owning the link source can be notified that your content has changed and be given the update ID of the change.

- Whenever a content change to your part results from an update to a link destination, your part must, when it calls its frame's ContentUpdated method, propagate the update ID passed.

- Whenever your part updates a link source manually (on explicit user instruction), it should not propagate an existing update ID. It must call UniqueChangeID to get a new update ID.

#### Automatic and Manual Propagation of Updates

OpenDoc informs destination parts when the sources of their links have been updated. The update notification can be:

Automatic | Immediately (if the source is in the same document as the destination) or whenever the user saves the source document (if the source is in a different location)
Manual | Only when instructed to do so by the user

The user selects whether updating is to be automatic or manual, either in the Paste As dialog box when the link is first created, or in the Link Destination Info dialog box after the link exists.

The user initiates a manual update by pressing the Update Now button in the Link Destination Info dialog box. Pressing the Update Now button in the Link Destination Info dialog box updates only the link destination from the link object.

To be notified automatically of changes to the source of a link, your destination part calls the link object's RegisterDependent method, supplying it the update ID of the last content it read from the link. Each link object maintains a private registry of dependent parts and calls their LinkUpdated methods whenever the link source changes. Your part should respond to this method call by updating its destination content from the link object, as described in "Updating a Link at the Destination".

If updating is to be automatic, your part should register for update notification when it first creates the link (passing an update ID of kODUnknownUpdate), and whenever your part reads itself from storage (passing the appropriate stored update ID from its link info structure). Be sure you are prepared to receive a notification when you call RegisterDependent, because your LinkUpdated method may be called before RegisterDependent returns. You can unregister and re-register as desired, but be careful not to register more than once at a time with the same link.

If your draft permissions are read-only, you should not register for update notification. Even if you do register, you do not receive notifications.

##### Updating a Manual Link Without User Instruction

If you maintain a link source set for manual updating, the state of your source content at any given time may be different from the contents of the link-source object. If another destination is added to the link, you must update the link-source object so the new destination and all existing destinations show the same content.

#### Automatic and Manual Updating of Link Source Content

OpenDoc allows owners of source parts to either specify that changes to link source content become visible as soon as the content changes (automatic), or only when explicitly requested (manually) by the source part. The source part calls the IsAutoUpdate method to determine the update mode. In the automatic mode (default), the part should update the Link Source Content Storage Unit (CSU) as soon as possible after the editing operation, which changed the content completely. In the manual mode, the parts should suppress updating the Link Source Content Storage Unit until asked (by the user pressing the Update Now button in the Link Source Info dialog box.)

#### Frame Link Status

All frames have a link status that describes the frame's participation in links. Parts are responsible for setting the link status of all frames that they embed; the link status indicates whether the frame is in the source of a link, in the destination of a link, or not involved in any link, or both. Parts can use this information to decide whether to allow editing of linked data, and whether to allow creation of a link within an existing link; see Table 11 for a list of the possible combinations.

##### When to Change Link Status

In general, any time that you create a link involving an embedded frame or add an embedded frame to your part, you should set the frame's link status.

- When your part creates a link source, it should call the ChangeLinkStatus method of each of the embedded frames within the content area of the link source, passing it the value kODInLinkSource.

- If you add embedded frames to a link source, call ChangeLinkStatus for each new frame, passing it the value kODInLinkSource.

- If pasting and creating a link destination in your part involves adding embedded frames at the destination, call ChangeLinkStatus for each new frame, passing it the value kODInLinkDestination. (This situation may arise either during a paste-with-link involving embedded frames, or during an update of a link destination that includes embedded frames.)

- Any time you embed a frame outside of any link source or destination that you maintain, set its status to kODNotInLink.

- If you break a link but keep the embedded data at the source or destination, set the status of each frame that was formerly linked to kODNotInLink (unless it is also in a source; see next item).

- Destinations take precedence over sources. If one of your embedded frames is contained in a link destination that you maintain, and that destination is itself contained in a link source that you maintain, set the embedded frame's status to kODInLinkDestination. If you later break that link destination (but not its enclosing link source), set the frame's status to kODInLinkSource rather than kODNotInLink.

Note:

> If you do not support linking, you must nevertheless set the link status of your embedded frames (to kODNotInLink).

The ChangeLinkStatus method changes the value of the frame's link status, if necessary, and calls the LinkStatusChanged method of the frame's part, so that the part can change the link status of any of its own embedded frames.

When you set the link status of a frame in any of these situations, you need to take into account only the links that your part maintains; you can ignore your own display frame's link status. OpenDoc automatically adjusts the link status of frames that you embed to account for your display frame's status.

You can examine the link status of a frame by calling its GetLinkStatus method.

##### The LinkStatusChanged Method of your Part Editor

Your own part's LinkStatusChanged method is called by the ChangeLinkStatus method of any of your display frames, whenever the display frame's link status is changed. This is its interface:

    void LinkStatusChanged (in ODFrame frame);

Your implementation of LinkStatusChanged should iterate through all of your part's embedded frames, calling each one's ChangeLinkStatus method.

- The link status of embedded frames already in links that you maintain cannot change; sources will remain sources, and destinations will remain destinations. Therefore, you do not need to call ChangeLinkStatus for them.

- You can set the link status of embedded frames not involved in links that you maintain to be equal to your display frame's link status. However, you can also just set their link status to kODNotInLink, letting OpenDoc adjust their link status if necessary.

It is important to make this call to all of your embedded frames, so that they can in turn call their parts' LinkStatusChanged method to change the link status of more deeply embedded frames, and so on.

You need not call the ChangeLinkStatus method of all of your embedded frames immediately upon receiving a call to ChangeLinkStatus. If you instantiate frame objects only when needed for display (see "Lazy Instantiation"), you can internally record which frames are affected, and the set their link status when you bring them into memory.

Note:

> If the user attempts to edit the content of any of your display frames whose link status is kODInLinkDestination, you should disallow the attempt.

See "Editing a Link Destination" for details.

#### Content Changes in Embedded Frames

Because linked content can contain embedded frames, there must be a way for an embedded part to inform its containing part that its content has changed, so that the link can be updated.

##### The ContentUpdated Method

Any time a content change occurs in one of your part's display frames, you should follow the procedures described in "Making Content Changes Known". Part of the procedure is to call the frame's ContentUpdated method. The ContentUpdated method then calls the EmbeddedFrameUpdated method of your part's containing part, informing it that the content of one of its embedded frames has changed. If your display frame is involved in a link source maintained by your part's containing part, the containing part can then choose to update the link-source object with the new data.

(This ContentUpdated method is unrelated to the ContentUpdated method of ODLinkSource, discussed in "Updating a Link at the Source". However, you pass the same update ID to both ContentUpdated methods when a content change to your part is entirely due to a content change to its link source.)

##### The EmbeddedFrameUpdated Method of your Part Editor

Your part's EmbeddedFrameUpdated method is called by the ContentUpdated method of any of your embedded frames, whenever the content of the frame's part has changed. This is its interface:

    void EmbeddedFrameUpdated(in ODFrame frame,
                              in ODUpdateID change);

The method is passed a reference to the embedded frame and an update ID identifying the modification. You should respond by saving the Update ID and updating any link-source objects that you maintain that involve that frame (and whose updating is automatic). When updating the link source, pass it the Update ID that you received in the call to this method.

Your EmbeddedFrameUpdated method should also call the ContentUpdated method of your own display frames that contain the embedded frame. In this way, the change is propagated upward throughout the embedding hierarchy.

#### Link Borders

If your part contains the source or destination of a link, you are responsible for drawing an appropriate border around the linked content area when requested to do so.

Whenever you draw your part's content in a facet, first call the ShouldShowLinks method of the facet's window. If ShouldShowLinks returns kODTrue, draw borders around any link sources and destinations.

#### Link Info

The link info structure (type ODLinkInfo) contains fields that hold information about the nature of a link, such as the part kind of its intrinsic data, its creation date, and its update ID. Your part should allocate and maintain a link info structure for every link destination that it contains.

Here is the structure's definition:

    struct ODLinkInfo
    {
            ODType                          kind;
            ODTime                          creationTime;
            ODTime                          changeTime;
            ODUpdateID                      change;
            ODBoolean                       autoUpdate;
    };

#### Linking and Undo

Just as the basic data-transfer actions (cutting, pasting, and dropping) should be undoable, so should their variations that involve linked data. The user should be able to undo (and redo) any of these actions:

- Pasting or dropping content and creating a link to its source.

- Pasting or dropping content that contains existing linked data.

- Deleting or cutting content that includes one or more link sources or destinations.

- Breaking a link at its source or its destination (through the Link Info dialog boxes).

When a link is created, the part receiving the data and creating the link destination adds a begin action and end action to the undo action history (see "Adding Multistage Actions"), whereas the source part adds a single action to the history when it creates the link source.

Edits to the source content of a link must be undoable. However, updates to a link source object from its source content do not need to be undoable.

- If the link source is updated manually, undoing or redoing changes to source content has no effect on the link source object. A manually updated link source always reflects the state of the source content at the last update.

- If the link source is updated automatically, changes to source content through an undo or redo action should cause you to update the link source. Use the update ID associated with the restored or redone content when updating.

  Note:

  > Automatic updating is not a user action and cannot be undone.

Likewise, updates to the destination content of a link from its link object do not need to be undoable:

- Editing the content at the destination of a link is not generally permitted. Because undoing or redoing an update to a destination would constitute editing the destination, you should never put the update action in the Undo or Redo stacks.

- You can, however, allow changes to the destination content that can be maintained across a link update, such as a style applied to the entire destination. Such non-editing changes can be undoable or ignorable, depending on your part's content model.

When you delete or cut content that includes a link source or destination, or when you break a link, follow the procedures outlined in "Breaking and Cutting Links" to make sure that you can undo the actions.

### Manipulating Links

This section describes some of the basic procedures you follow in creating links, reading and writing linked data, and updating links.

Related information is described as part of general data-transfer considerations earlier in this chapter. Creating and removing link specifications are described in "Link Specification" and "Removing a Link Specification from the Clipboard".

Both the source and destination parts of a link, or even separate destinations of a single source, may attempt to access linked data simultaneously. Therefore, many methods of the classes ODLinkSource and ODLink require that you provide a key before accessing the storage unit containing the content of a link. You obtain the key by first calling the Lock method of the link-source object or link object involved.

#### Creating a Link at the Destination

A link is created when the user decides, using the Paste As dialog box, to link rather than statically transfer data during a paste. The destination part, the part receiving the paste or drop, retrieves the link specification from the clipboard or drag and drop object and calls the destination draft's AcquireLink method for the part. The source's part draft calls the CreateLink method of the source part that placed the data in the clipboard or drag and drop object.

If your part is the destination part that creates a link to pasted data from the data's source, you can use the following steps. It assumes that you called the ShowPasteAsDialog (OS/2 and Windows) method of the clipboard or drag and drop object (see "Handling the Paste As Dialog Box"), and are ready to act on the results of the Paste As dialog box. For clipboard transfer, this means that you have already acquired the clipboard focus (see "Acquiring and Relinquishing the Clipboard Focus").

1. Focus the clipboard or drag and drop storage unit on the link specification it contains. Use your draft's CreateLinkSpec method to instantiate the link specification, with the destination part as the part, and null for the data. Then call the link specification's ReadLinkSpec method to have it read itself from the storage unit.

2. This link-creation procedure should be undoable. If the data transfer for this link is a paste from the clipboard, add a beginning action to start a multistage transaction. That way, if the user decides to reverse the paste, both the paste and this link creation will be undone together. (Do nothing here if this data transfer is a drop.)

3. Pass the link specification to your draft's AcquireLink method to construct the link object from the link specification with 0 as the ID.

4. Create a link-info structure (type ODLinkInfo) to associate with the link destination, as described in "Link Info". Initialize its update ID (to kODUnknownUpdate), set its creation time to the current time, set its modification time to the modification time of the link object, and give it a part kind and auto-update setting that reflect the user's choices in the Paste As dialog box.

5. Add this link object to whatever private list of link destinations you maintain. Store the information you need to associate this link object with its link-info structure and with the link-destination content in your part.

6. If the user has specified auto-updating in the Paste As dialog box and if your part has not already registered with this link object in connection with other link destinations, call the link's RegisterDependent method. Otherwise, manually update the link at this time by performing whatever actions your part's LinkUpdated method would perform (see "Updating a Link at the Destination"). To ensure that this link-creation procedure is undoable, take either of these steps:

   - If the data transfer for this link was a paste from the clipboard, add an end action to the action history at this time, to complete the two-stage action started in step 2.

   - If the data transfer for this link is a drop, you only need to add a single action to the undo action history. If the user undoes the drop, this link will be deleted also.

7. Call the ChangeLinkStatus method of any of your part's newly embedded frames that are within the linked content area, passing them the value kODInLinkDestination.

Never read the link content from the storage unit of the clipboard or drag and drop object when creating the link; always read link content from the link object's storage unit.

Every time the link is updated, your part (the destination part) must discard the data in the link destination and read it again from the link object. This process can entail discarding embedded parts and creating them anew from the updated link content. See "Updating a Link at the Destination" for details.

Your destination part must be able to draw a border around the link content area when asked to do so, as described in "Link Borders". Parts embedded in linked data are not involved in the maintenance of the link; even in the case of a link destination that consists of a single embedded part, you draw the link border around the embedded part's frame.

Only the draft should call CreateLink; if your part is a destination part that needs a link to be created, you should call your draft's AcquireLink method.

#### Creating a Link at the Source

When the user decides to create a link to data that your part placed on the clipboard or drag and drop object, the destination part's draft calls the CreateLink method of your part (the source part). The draft passes back the data of the link specification that your part originally wrote when it placed the data on the clipboard or drag and drop object. This is the interface to CreateLink:

    ODLinkSource CreateLink(in ODByteArray data);

When your source part's CreateLink method is called, the method should duplicate in a link-source object all of the content that your part originally wrote (or promised) to the clipboard or drag and drop object.

In your source part's CreateLink method, you can follow these general steps:

1. Examine your own structures to see if the link source object already exists. CreateLink may have been called as a consequence of another destination being added to this link source, as described in "Replacing Link-Source Content in CreateLink".

   - If the link-source object does exist, it may not contain a complete set of promises or data. In that case, you need to write the remaining part kinds to it. You can do that by updating the link source in a particular manner. Skip to step 5.

     (If the link-source object does exist and does contain a complete set of promises or data, you could return the existing link-source object and take no further action. However, it may be simpler just to rewrite all part kinds than to test to see if you have a complete set.)

   - If the link-source object does not yet exist, continue with step 2.

2. Create a link source object by calling your draft's CreateLinkSource method.

3. Add this link-source object to whatever private list of link sources you maintain. Store the update ID and any other information you need to associate this link-source object with the link-source content in your part.

4. Call the ChangeLinkStatus method of any of your part's embedded frames that are within the linked content area, passing them the value kODInLinkSource.

5. Update the link source. Follow the steps listed in "Updating a Link at the Source".

6. Creating a link source needs to be undoable. Add a single action to the undo action history so that you can remove the link source if the user decides to undo its creation.

7. Unlock the link-source object.

8. Return a reference to the link-source object as the method result. (The caller is responsible for releasing the link-source object when it is no longer needed.)

##### Replacing Link-Source Content in CreateLink

If your part's CreateLink method is called and the link-specification data provided describes an existing link source that you maintain, your part must ensure that all content kinds that you support are made available to the destination. If your part writes promises to the link source, you can ensure that all kinds are promised by replacing the current content in your link-source object. The procedure for replacing is similar to the regular updating procedure, except that you pass an existing update ID to the link-source object's Clear method, and you do not call the link-source object's ContentUpdated method. See "Updating a Link at the Source" for details.

#### Updating a Link at the Destination

When a source part updates its link-source object and calls the object's ContentUpdated method, the link-source object notifies its associated link objects of the change (except for link objects in other documents, which do not receive the notification until the source document is saved). The link destinations receive notification of the change in this manner:

- If the user has selected automatic updating of a link destination associated with one of the notified link objects, the link object associated with the updated source immediately notifies the destination part of the change by calling its LinkUpdated method.

- If the user has selected manual updating, the destination part is not notified. In this case, it updates the destination only on user instruction, as described in "Automatic and Manual Propagation of Updates".

If your part contains the destination of a link, you can update the destination in your part's LinkUpdated method. This is the interface to LinkUpdated:

    void LinkUpdated(in ODLink updatedLink,
                     in ODUpdateID change);

Your part will receive such a call in response to the user's request for an update in the Link Destination Info dialog box.

You can perform automatic updates in your part's LinkUpdated method, and manual updates in response to a user command in the Link Destination Info dialog box.

It is not always necessary to update immediately, during execution of LinkUpdated or when manually instructed to do so by the user. For example, if the destination has scrolled offscreen but is still registered as a dependent of the source, updating does not need to occur until the destination scrolls back into view. Your part editor can, if desired, perform link updates as a background task.

You can take the steps shown here to read the contents of a link object when updating your link destinations.

1. Call the link object's Lock method. If you cannot acquire the lock for the link it may be momentarily in use by another object; wait and try again.

2. Access the link object's storage unit by calling its GetContentStorageUnit method. (If GetContentStorageUnit returns the error kODErrNoLinkContent, the last source update failed; do not update your destination at this time.)

   Incorporate or embed the updated destination data into your part from the link object's contents property:

   - If you are incorporating the linked data, follow the steps in "Incorporating Intrinsic Content".

   - If you are embedding the linked data as a single part, follow the steps in "Embedding a Single Part".

   In any of these cases, the only differences from other data-transfer operations are that you call the Clone method of the link object's storage unit's draft, you specify a clone transaction of kODCloneFromLink, and you read from the link object's content storage unit.

   You must discard any previously embedded parts and frames, replacing them with new ones cloned from the link. Also, you must connect the frames to your frame hierarchy, create facets for the currently visible ones, and change their link status by calling their ChangeLinkStatus method, passing the value kODInLinkDestination.

3. Update the link-info structure for the link, with a new update ID and new change time, obtained by calling the link's GetUpdateID and GetChangeTime methods.

4. Unlock the link.

5. Inspect your part's private structures to see if this change has affected the data of any link source in your part. (It can if this destination is within a source.) If it has, modify the source as shown in "Updating a Link at the Source".

6. Notify OpenDoc and your containing part the there has been a change to your part's content; see "Making Content Changes Known".

If your part maintains the destination of a link whose source has been transferred from one part to another, the new source part might not write its data using all of the same part kinds as the original source part. Your part, therefore, might not be able to obtain updates in the format that it expects. If that situation occurs, your part should read whatever part kind it can, or else break the link.

#### Updating a Link at the Source

The part maintaining the source of a link can rewrite values into the link-source object as necessary to update the link. If your part is the source part, take the steps listed here to update the contents of your link source (or to write it for the first time). The "Automatic and Manual Propagation of Updates". describes how the user chooses whether updating should be automatic or manual. If the user has selected automatic (on-save) updating of the link source, perform this task whenver there is a change to the link-source content. If the user has selected manual updating of the link source, perform this task only on user command. The basic procedure for updating a link source is to first remove all content from the link source by calling its Clear method. By calling Clear, you ensure that all unneeded storage units are removed from the link, thereby saving space in your draft.

The Clear method deletes the contents property of the link-source storage unit and all its data. You need to add a contents property and values back into the storage unit after calling Clear, just as if creating the link source from scratch.

In summary, follow these steps:

1. Call the link-source object's Lock method, and then call its Clear method. If you cannot acquire the lock for the link source, it may be momentarily in use by another object; wait and try again.

   - If you are updating the link source because of content changes in your part, or if you are writing to the link source for the first time, or if this is a manual update, obtain a unique update ID and pass it to Clear.

   - If you are updating the link source because of an update to a link destination within your source content, pass the link destination's existing update ID (the one that was passed to your LinkUpdated method) to Clear.

   - If you are updating the link source because a change to an embedded frame caused your part to receive a call to its EmbeddedFrameUpdated method, pass the existing update ID (the one that was passed to EmbeddedFrameUpdated) to Clear.

   - If you are just adding new part kinds to an unchanged existing link source, pass the link source's existing update ID to Clear. (You must update the link if a new destination has been added and if the link source does not have a complete set of promises for all part kinds that you support.)

2. Access the link-source object's storage unit, create the contents property and the appropriate values, and write your updated source data. Write either data or a promise for all part kinds that you support, including standard part kinds. Update the frame shape annotation and any other needed annotation properties.

   - If the data is intrinsic content follow the procedure given in "Writing Intrinsic Content".

   - If the data consists of a single frame of an embedded part, with no surrounding intrinsic content, follow the procedure given in "Writing a Single Embedded Frame".

   Either way, specify a clone operation of kODCloneToLink. You are encouraged to write one or more promises instead of writing the actual data, to avoid placing unused content into the link source.

3. If you are just adding new part kinds to an unchanged existing link source, skip this step. Otherwise, after you have updated the data of the link-source object, notify it of the change, like this:

   - If the content change originated in your source part, or if this is the first time you are writing to the link, or if this is a manual update, call the link-source object's ContentUpdated method and pass it a new update ID (obtained from the session object's UniqueChangeID method), so that it can in turn send update notifications to all its link destinations.

   - If the content change occurred in a link destination contained within your link source, call the link-source object's ContentUpdated method and pass it the link destination's existing update ID (the one that was passed to your LinkUpdated method), to avoid indefinite updating of circular links.

   - If the content change occurred in an embedded frame and your part's EmbeddedFrameUpdated method was called, call the link-source object's ContentUpdated method and pass it the existing update ID (the one that was passed to EmbeddedFrameUpdated).

4. Unlock the link source.

In general, it is not necessary and not always desirable to perform automatic updates to link-source objects immediately in response to every content change. When content is changing rapidly, such as during text entry, it is reasonable to wait for a pause before updating any affected links.

#### Writing Linked Content to Storage

When your part writes itself to storage (see "Writing a Part to Storage"), or when it writes all or a portion of itself to a data-transfer object (see "Writing to a Data-Transfer Object"), it writes any linked data, whether source or destination. However, it must write information for reconstructing the link and link source objects involved.

##### Writing Links in Externalize

There is no standard way to associate a link-source object or link object with a specific region of your content; how you create that association depends on the nature of your content. However, for all links your Externalize method and your methods that write to data-transfer objects need to write persistent references to the objects themselves, plus additional information. You should write the information in a format that is standard to your part kind; that way, any part editor that reads your part kind can reconstruct links from the stored data.

For the source of a link, you need to include, in addition to a persistent reference to the storage unit of the ODLinkSource object, the update ID associated with the linked content.

For the destination of a link, you need to include a persistent reference to the storage unit of the ODLink object, plus the information in the fields of the ODLinkInfo structure: the part kind, creation time, modification time, update ID, and auto-update setting of the linked data.

Your part may also store extra information for convenience. For example, because your part needs to be able to draw a border around linked data, it may store link-border shapes in a private structure.

##### Writing Links for Data Transfer

If your part writes a single embedded frame (without surrounding intrinsic content) to the clipboard or drag-and-drop object, and if that frame is a link source or link destination, clone the link or link source object into the data-transfer object's draft and add a property with the name kODPropProxyContents to the clipboard or drag and drop storage unit. In that property write a value that references the link or link source object, as well as the link info fields and other necessary information. Any destination part that understands the proxy format can then recover the link.

If you write linked content to a data-transfer object and the destination part does not support linking, the content may be transferred but the links will not be.

#### Reading Linked Content from Storage

When your part reads itself into memory, it may need to read in link-source objects and link objects as well. Likewise, when your part incorporates data that the user has pasted or dropped, your part may need to modify link sources or link destinations contained in that data.

Special care is necessary because OpenDoc can eliminate links during data transfer (see,for example, Table 10). Therefore, you must perform the following two tasks when reading in data that includes links:

- Before you attempt to read in a link-related object, you must ensure that your persistent reference to it is valid.

- For every link-source object that you read in, you must ensure that your part is assigned as its source. The original source of the link may have been in another part, if the source content has been moved or copied.

##### Reading Links in InitPartFromStorage

Here are the steps you can take to handle linked data when reading your part (in InitPartFromStorage). The general procedure for reading a part is described in "The InitPartFromStorage Method".

1. As you encounter a persistent reference to a link object or link-source object in the content you are reading, ensure that the reference is valid. Before trying to read in the referenced storage unit, pass the persistent reference to the IsValidStorageUnitRef method of the storage unit containing the content you are reading.

   - If IsValidStorageUnitRef returns true, Call your draft's AcquireLinkSource or AcquireLink method to read in the object.

   - If IsValidStorageUnitRef returns false, the link-source object or link object no longer exists, and your part should no longer consider the associated portion of its own content as linked. This is not an error situation, and you need not notify the user if it occurs.

2. For each link-source object you read, call its SetSourcePart method to associate it with your part. Add the link-source object to whatever private list of link sources you maintain, and associate it with the specific content in your part that is the source of the link.

3. For each link object you read, associate the link object with the specific content in your part that is the destination of the link. Add the link object to whatever private structures you maintain for link destinations.

4. For each link object you read, be prepared to register for update notification if updating is to be automatic. Either at this point or (preferably) once the linked data becomes visible or affects visible content, pass the last-saved update ID to the link's RegisterDependent method.

##### Reading Links for Data Transfer

Here are the steps you can take to handle linked data when reading (cloning) from the clipboard or drag and drop object. The overall procedure for reading from a data-transfer object is described in "Incorporating Intrinsic Content".

1. After the clone operation completes successfully pass the ID of each cloned link or link source object to your draft's IsValidID method to see if the object is valid. This cloning operation assumes that you followed the cloning steps listed in "Cloning Sequence" and saved all object IDs returned by the Clone method

   If IsValidID returns true, call your draft's AcquireLinkSource or AcquireLink method to read in the object. If IsValidID returns false, the link-source object or link object no longer exists and was not cloned.

2. For each link-source object you read, call its SetSourcePart method to associate it with your part. Add the link-source object to whatever private list of link sources you maintain, and associate it with the specific content in your part that is the source of the link. (That information is included in the contents property of the transferred data and is defined by the format of the part kind your editor is reading.)

3. For each link object you read, associate the link object with the specific content in your part that is the destination of the link. Add the link object to whatever private structures you maintain for link destinations. (That information is included in the contents property of the transferred data and is defined by the format of the part kind your editor is reading.)

4. If you are reading and embedding a single part that is also a link source or destination, there should be a property named kODPropProxyContent in the data-transfer storage unit. The property should contain a persistent reference to the link-source object or link object associated with the embedded part, plus any other needed information such as the fields of the link info structure. If you understand the format of the data in the property, read it and use it.

If you have incorporated an existing link source into your part, you may not be able to write all the data types (part kinds) to the link source object that its previous owner did. That is not an error; simply write the part kinds that you support.

#### Revealing the Source of a Link

Users need to be able to navigate from the destination of a link to the source content. When the user selects the Show Link Source button in the Link Destination Info dialog box the destination part calls the ShowSourceContent method of the link object. OpenDoc, in turn, calls the source part's RevealLink method. This is its interface:

    void RevealLink(in ODLinkSource linkSource);

If your part contains the source of a link, your RevealLink method should display the part content that corresponds to the link source, following these general steps:

1. Using whatever part-specific method you choose, locate a display frame of the source content. If no suitable frame exists, open a part window to display the source.

2. Ensure that the frame displaying the source is visible in the window. Find its containing frame by calling its AcquireContainingFrame method, then call the RevealFrame method of the containing frame's part. (The procedure is unnecessary if your part opened up a separate part window for the link source, because the display frame, being the root frame for the window, is already visible.)

3. Activate the frame, using your part's normal activation procedure (see "How Part Activation Occurs"). Scroll the content in the revealed frame, if necessary, to make the link source visible.

4. Select the content and draw a link border around it.

If your part is the part that needs to reveal an embedded frame containing a link source, you receive a call to your RevealFrame method. This is its interface:

    ODBoolean RevealFrame (in ODFrame embeddedFrame,
                           in ODShape revealShape);

Scroll your content, if necessary, to reveal the portion of the specified embedded frame containing the specified shape. If the embedded frame is not currently visible because your containing part has scrolled your display frame out of view, call your containing part's RevealFrame method, passing it your own display frame and an appropriate shape (such as the shape of the embedded frame to be revealed).

#### Editing a Link Destination

Because OpenDoc links are unidirectional from source to destination, you should restrict the editing that a user can perform within the destination of a link. Basically, changing of content in a link destination is not allowed.

It is the responsibility of the destination part to block attempts to edit its link destinations. However, the part that initially receives an editing event may be a frame embedded within, perhaps deeply embedded within, the destination part. If your part's display frame has the status kODInLinkDestination and the user attempts to edit content within it, your part should call the EditInLink method of your frame. OpenDoc in turn calls the EditInLinkAttempted method of the part that maintains the link destination that includes your frame. This is the interface to EditInLinkAttempted:

    ODBoolean EditInLinkAttempted (in ODFrame frame);

The part that maintains the link destination should, in its EditInLinkAttempted method, take steps similar to these:

1. It should check that it does indeed maintain a link destination involving the specified frame. If not, the method should return false and exit.

2. It should present to the user an alert box that allows the user to forego the editing, break the link, or display its source. In any case, the part should not activate itself.

   - If the user chooses to forego editing, EditInLinkAttempted should simply return true.

   - If the user chooses to display the source, EditInLinkAttempted should follow the instructions listed in "Revealing the Source of a Link", calling the ShowSourceContent method of the link object. If ShowSourceContent fails, display an appropriate alert box notifying the user of the failure to find the link source, and return true from EditInLinkAttempted.

   - If the user chooses to break the link, EditInLinkAttempted should follow the instructions listed in "Breaking and Cutting Links", and return true.

If EditInLinkAttempted returns true, then EditInLink returns true to your part (the part in whose display frame the edit attempt occurred). If the user has broken the link, your part's display frame now has a different link status and you can therefore allow editing of your content.

If EditInLinkAttempted returns false, then EditInLink returns false and OpenDoc cannot find the part that maintains the link destination or it cannot navigate to the link source. Your part should then put up its own simple alert box informing the user that editing in a link destination is not allowed.

If the user selects more than one link destination in your part and then attempts to edit the data, your part cannot even call EditInLink. It should then disallow editing and display a dialog box, suggesting that the user select a single destination.

#### Breaking and Cutting Links

Users can delete links in several ways. When you delete or cut content that includes a link source, or when you break a link at its source, your part relinquishes ownership of the link source object, as noted in Table 10. You should:

- Disassociate the link-source object from your part's content.

- Set the link's source part to null by calling the link source's SetSourcePart method.

- Save a reference to the link-source object in an undo action, rather than releasing the link source.

- Call the SetChangedFromPrev method of your draft to make sure that this change is saved when the document is closed.

- Change the link status of any affected frames (see "Frame Link Status") if a link has been broken.

It is not necessary at this point to update the link source to delete its contents. If the user subsequently undoes the action, you can reverse the steps listed in the previous paragraph. You must be sure to call the link source object's SetSourcePart method once again to reestablish your part as the link's source.

When you delete or cut content that includes a link destination, or when you break a link at its destination, you should:

- Disassociate the link object from your part's content

- Save a reference to the link object in an undo action, rather than releasing the link immediately

- Call the link object's UnregisterDependent method so that your part will no longer be notified when updates occur, if the link has been registered and if there are no remaining destinations of this link in your part.

- Call the SetChangedFromPrev method of your draft, to make sure that this change is saved when the document is closed

- Change the link status of any affected frames (see "Frame Link Status"), if the link that has been broken

Reversing these actions undoes the break or cut. Your part should hold the references to a cut or broken link source or link until your DisposeActionState method is called, at which time you can release the link or link-source object.

### Transfer Rules for Links and Link Sources

This section summarizes the semantics of data transfer involving link objects and link-source objects. This discussion is for informational purposes only; if you follow the procedures presented earlier in this chapter, the correct semantics will always occur.

During some data-transfer operations, OpenDoc may delete or redirect link or link-source objects. Other operations are permitted by OpenDoc but should not be supported by your part. This section lists the basic data-transfer operations, in terms of:

- What happens when your part moves or copies linked data

- What happens when your part creates a link destination when pasting or dropping data that is already linked

- What configurations of links within links your part should support

OpenDoc enforces the behaviors listed in Table 10 when existing links or link sources and their associated intrinsic content are transferred by drag and drop or with the clipboard, according to the specified kind of cloning operation.

Table 10. Behavior of Existing Links During Data Transfer

Operation | Object | Behavior
--- | --- | ---
Copy (Drag-copy, or Copy followed by Paste) | Link source | Data is transferred; link source is not.
| Link destination | In same document: Data is copied; link destination is copied also. Across documents: Data is copied; link destination is not copied
| Source and destination of same link | Data is copied; link source and destination are both copied (but any uncopied destinations of the original source remain linked to the original source)
Move (Drag-move, or Cut followed by Paste) | Link source | In same document: Data is transferred; link source is transferred also Across documents: Data is transferred; link source is deleted (destinations within original document become unlinked)
| Link destination | In same document: Data is transferred; link destination is transferred also Across documents: Data is transferred; link destination is deleted
| Source and destination of same link | In same document: Data is transferred; link source and destination are both transferred Across documents: Data is transferred; any unmoved destinations of the link source become unlinked.

When pasting or accepting a drop into your part, you can incorporate or embed data that includes existing link sources or destinations and at the same time attempt to make the data a link destination. In that situation, OpenDoc transfers that data and creates a link destination, but does not maintain any link sources or destinations that were included in the transferred data.

You can create links within links; OpenDoc does not prevent you from creating or pasting the source or destination of one link within the source or destination of another link. However, only some configurations make sense, as shown in Table 11.

You can use your display frame's link status (see "Frame Link Status"), as well as private link-status information about your own intrinsic content, to decide whether to allow the creation of a link source or destination in any given situation. In general, you cannot edit a link destination.

Table 11. Creating Links within Links

To create a new... | Within an existing... | Recommendation
--- | --- | ---
Link source | Link source | Okay, result is two separate link sources with partially or completely overlapping content.
Link source | Link destination | No, when the destination updates, it is generally impossible to know which changes should become part of the updated source.
Link destination | Link source | Okay, updates to the destination simply become part of the source.
Link destination | Link destination | No, updates to either link destination will overwrite the other.

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