Programming Guide: Storage Issues for Data Transfer

# Programming Guide

## Storage Issues for Data Transfer

This section introduces some of the storage-related concepts used in clipboard transfer, drag and drop, and linking, which are described in detail later in this chapter.

For the purposes of this section, the term data-transfer object means any of the following: the clipboard or drag and drop object (when reading or writing data), a link-source object (when writing linked data), or a link object (when reading linked data).

### Data Configuration

When a part places data on the clipboard or other data-transfer object, it writes a portion or all of its intrinsic content to the data-transfer object, and it also can cause the contents of one or more embedded parts to be written.

Whatever the nature of the data, once it is written to the data-transfer object it can be considered an independent part. It has its own intrinsic content and it may contain embedded parts. In writing or reading the data, your part is directly concerned only with the characteristics (such as part kind) of the intrinsic content of the transferred part. All embedded parts are transferred unchanged, as embedded parts, during the process.

The storage unit for the data-transfer object's intrinsic content is called its content storage unit. It is the main storage unit for the content of the data-transfer object, equivalent to a part's main storage unit (see "Main and Auxiliary Storage Units"). Any embedded parts in the transferred data, and any other OpenDoc objects, have their own storage units.

Note:

> A link object or a link-source object is a persistent object, and as such has its own main storage unit. That storage unit is not the same as its content storage unit. Always access the content of a data-transfer object by calling its GetContentStorageUnit method.

More specifically, the data-transfer object's intrinsic content is stored in the contents property (type kODPropContents) of the content storage unit. When writing to a data-transfer object, your part can store data in multiple formats in different values of the contents property. Just as with a part, each value in the contents property must be complete; it must not depend on other properties or other values of the property.

Your part accesses other properties besides the contents property in a data-transfer object. For example, when your part writes data to a data-transfer object, it may also write, into a separate property, a frame object or a frame shape for the destination part to use when constructing a display frame. See "Annotations" for more information.

Objects in memory take precedence over their stored versions in data transfer. If the user cuts or copies a part's frame to the clipboard or other data-transfer object, the moved data represents the current state of the part, including any edits that have not yet been saved to disk.

### Annotations

This section discusses the items, in addition to part content, that you can write to or read from the storage unit of a data-transfer object.

#### Link Specification

When copying content to the clipboard or drag and drop object, a part advertises the ability to create a link by writing a link specification in addition to content. When you copy data to the clipboard or drag and drop object, you should create a link specification, using your draft's CreateLinkSpec method, if the user chooses to link to the data when pasting or dropping it in the destination. "Writing Intrinsic Content" illustrates when to write a link specification.

You should write the link specification onto the content storage unit of the clipboard or drag and drop object as a property with the name kODPropLinkSpec. The data in a link specification is private to the part writing it. The data you place in your link specification is returned to your part if and when your part's CreateLink method is called to create the link. All that your part needs from the link specification data is sufficient information to identify the selected content.

Because the link specification is valid only for the duration of the current instantiation of your part, the link specification can contain pointers to information that you maintain.

Link specifications are not necessary in the following situations:

- When you place content on the clipboard as a result of a cut operation. You cannot link data that is no longer in your part. (Because you cannot know at the start whether a drag will be a move or a copy, you should always write a link specification when you write data to the drag and drop object.)

- When you write all or a portion of a link destination (or any of your part's content, if your part itself is in a link destination) to the clipboard or drag and drop object. Creating a link in that situation could make your link destination a source of another link. That configuration is technically possible but generally a bad practice. (see Table 11 for an explanation).

- When your draft permissions (see "Drafts") are read-only. You would not be able to establish links to other parts of the same (read-only) document, and any links to other documents would be broken when your draft closed.

- When you are writing to a link-source object. Link specifications are for the clipboard or drag and drop object only. Link specifications are transitory, whereas links are persistent.

When you write a link specification to the clipboard, obtain and save the clipboard update ID (see "Clipboard Update ID"). You must remove a link specification from the clipboard if your source data changes so that the clipboard data no longer reflects it, and you need the update ID to test for that situation. See "Removing a Link Specification from the Clipboard".

You never need to remove a link specification from the drag and drop object, because it is valid only during the course of a single drag operation.

Note:

> The kODPropLinkSpec property is not copied when the storage unit containing it is cloned.

#### Frame Shape or Frame Annotation

When you place intrinsic content (with or without embedded frames) on the clipboard or other data-transfer object, there is no frame associated with that content. You should, nevertheless, write a frame shape to the data-transfer object to accompany the content; the shape is a suggestion to any part that reads the data and must embed it as a separate part. You write the frame shape into a property named kODPropSuggestedFrameShape in the data-transfer object's content storage unit.

Likewise, if your part receives a paste or drop from the data-transfer object and embeds the instrinsic data, your part should examine the kODPropSuggestedFrameShape property to get the source part's suggested frame shape for the data. If the kODPropFrameShape property does not exist, use your own part-specific default shape.

If you place one of your embedded parts (in the form of a single embedded frame) on the clipboard or other data-transfer object, you must place the frame object there also. You write the frame into a property with the name kODPropContentFrame in a storage unit of the data-transfer object's draft.

Likewise, if your part receives a paste or drop from the data-transfer object, you can note from the presence of the kODPropContentFrame property that the data represents a single frame that should be embedded, and you can retrieve the frame from the property.

When you transfer a single embedded frame, you can specify the frame location relative to your part's content (that is, the offset specified in the external transform of the frame's facet) by incorporating the offset into the frame shape that you write. Then, the receiving part of the paste or drop can, if appropriate to its content model, use that offset to construct an external transform.

Note:

> Neither the kODPropSuggestedFrameShape nor kODPropContentFrame property is copied when the storage unit containing it is cloned.

#### Proxy Content

When you write a single embedded frame to a data-transfer object, you can optionally write any intrinsic data that you want to associate with the frame. The intrinsic data might be a drop shadow or other visual adornment for the frame, or it might be information needed to reconstruct the frame as a link source or link destination.

This information is called proxy content; to write it, you add a the kODPropProxyContents property to the data-transfer object, and write your data into it as a value that your part recognizes. If the transferred part is subsequently embedded into a part that also recognizes that value and knows how to interpret it, the added frame characteristics can be duplicated.

If your part receives a paste or drop and embeds the single part, that proxy content for that frame exists. The proxy content exists due to the presence of the kODPropPrxoyContents property. If your part understands the format of the proxy content, you can read it and duplicate the frame characteristics. To determine if you frame understand the proxy content format, examine the value types in the property.

Note:

> The kODPropProxyContents property is not copied when the storage unit containing it is cloned.

#### Cloning-Kind Annotation

When a single embedded part is written to a data-transfer object, its containing part writes a property with the name kODPropCloneKindUsed to the data-transfer object. The property specifies the kind of cloning transaction used to clone the embedded part. If the embedded part writes a promise instead of actual data to the data-transfer object, it uses the information in the kODPropCloneKindUsed property when it later fulfills the promise.

For more information, see "Fulfilling a Promise".

#### Mouse-Down Offset Annotation

If your part initiates a drag operation (see "Initiating a Drag"), you need to create a property named kODPropMouseDownOffset in the drag and drop object's storage unit. Write into that property a value that specifies the location of the mouse-down event that triggered the drag. The value should be of type ODPoint and should contain the offset from the origin of the content being dragged.

If your part receives a drop, it should likewise check for the presence of the kODPropMouseDownOffset property. If the property exists, and if taking it into account is consistent with your part's content model, use it to locate the dropped content in relation to the mouse-up event that marks the drop.

Note:

> The kODPropMouseDownOffset property is not copied when the storage unit containing it is cloned.

#### Drag and Drop Annotations (For OS/2)

Each content storage unit of the drag and drop object contains a PM DRAGITEM structure corresponding to a drag item in the current drag and drop operation. It is written by the drag and drop object under the kODDragitem value of the kODPropContents property. Usually parts don't have to interact with this value other than in their Drop method, when they have to create a storage unit view for it and pass it to the GetDataFromDragManager method of the drag and drop object to perform data rendering.

The drag and drop object supports data rendering for two mechanisms: DRM_SHAREDMEM and DRM_OS2FILE. If either one of these mechanisms has been selected (RMF selection is done by calling drag and drop CanEmbed or CanIncorporate methods from within the part's DragEnter method), the storage unit rendered by GetDataFromDragManager will contain the rendered data with no kODDragitem value. If the selected rendering mechanism is DRM_OS2FILE and this is not an OPENDOC initiated drag, the rendered storage unit will contain the following values for its kODPropContents property:

Selected kind | Part kind that the selected RMF was mapped to; no value is set for this kind. The actual data has to be read from the dropped file.
kODFileType | Name of the dropped file; value type is ODISOStr.
kODFileTypeEA | Any extended attributes that are associated with the file; value type is ODISOStr.

If the selected rendering mechanism is not handled by the drag and drop object, the target part has to perform the data rendering. In this case, the kODPropContents in the storage unit rendered by GetDataFromDragManager will contain a set of values necessary for the part to carry out the rendering conversation:

kODDragitem | DRAGITEM structure; value type is DRAGITEM.
kODSelectedRMF | Selected rendering mechanism and format pair; value type is ODISOStr.
kODSelectedKind | Part kind that the selected RMF was mapped to; value type is ODISOStr.
kODDragOperation | PM drag operation from the DRAGINFO structure associated with this drag; value type is ODUShort.

#### Clonable Data Annotation Prefix

In some situations an entity may need to store properties in the storage unit of a part or other object without the knowledge or cooperation of the part itself. For example, a service such as a spell checker might store a dictionary of exceptions as a property of a part's storage unit. The part is unaware of the existence of that property, but the spell checker would want the dictionary cloned whenever the part is cloned.

When a storage unit itself is cloned (see "Cloning"), all its properties are copied, no matter who wrote them into the storage unit. However, the in-memory version of an object is given preference over its storage unit during cloning, because recent, unsaved changes to the object should be included in the cloning operation. Unfortunately, when an in-memory object clones itself, any of its properties that the object itself is unaware of are not cloned, because it does not know to write them into the destination storage unit.

To support this capability, OpenDoc defines the property prefix constant kODPropPreAnnotation, whose value is OpenDoc:Annotation:. When that prefix is part of a property name (for example, OpenDoc:Annotation:Exceptions) in an object's storage unit, OpenDoc always copies that property when the object is cloned, even if the object being cloned is in memory and regardless of whether the object is aware of the existence of the property.

Therefore, if your part stores data in another object that the object itself does not use but that you want to be cloned along with the object, make sure you store it in a property whose name starts with the string defined by kODPropPreAnnotation.

### Cloning

You should always transfer data to and from the clipboard or other data-transfer object in the context of cloning. This section describes how cloning works, what the scope of a cloning operation is, and how to implement your part editor's CloneInto method.

Cloning an object is copying that objec, all objects it references, and all objects they in turn reference. Copies are made of all storage units or their equivalent proxies that are references with strong persistent references. Storage units referenced with weak persistent references are not copied. For more information on how strong and weak persistent references affect cloning, see "Persistent References" and Figure 61.

Actually, each object cloned during the operation decides, if it is in memory, which of its storage-unit's properties and which of its referenced objects should be included. If the object is not currently instantiated, all of its storage unit's properties and all of its strongly referenced objects are copied.

#### Cloning Sequence

All persistent objects have a CloneInto method by which they clone themselves, but your part editor should not call the CloneInto method of any object directly. Instead, you clone an object by calling the Clone (or WeakClone) method of its draft object. The Clone method in turn calls the CloneInto method of the object involved. (Your parts, as persistent objects, must provide a CloneInto method. See "CloneInto Method of Your Part Editor" for more information.)

Cloning is a multistep transaction, designed so that it can be terminated cleanly if it fails at any point. You perform a clone by calling three methods, in this order:

##### BeginClone

First, call the BeginClone method of the draft object of the data to be cloned. If you are transferring data from your part, call your part's draft object; if you are transferring data from a data-transfer object, call that object's draft object. BeginClone sets up the cloning transaction.

(If you are cloning as a result of pasting or dropping data into your part, you must also specify the destination frame, the display frame of your part that is receiving the paste or drop.)

When you call BeginClone, you are returned a draft key, a number that identifies that specific cloning transaction. You pass that key to the other cloning methods that you call during the transaction. You also specify in the kind parameter to the BeginClone method the kind of cloning operation that is to be performed, so that OpenDoc can maintain the proper behavior for linked data that is being transferred. Table 9 lists the kinds of cloning transactions that OpenDoc recognizes.

Table 9. Kinds of Cloning Operations

kind parameter | Meaning
--- | ---
kODCloneCopy | Copy object from source to data-transfer object
kODCloneCut | Cut object from source to data-transfer object
kODClonePaste | Paste object from data-transfer object to destination
kODCloneDropCopy | Copy object to the destination of a drop
kODCloneDropMove | Move object to the destination of a drop
kODCloneToLink | Copy object from source to update a link source
kODCloneFromLink | Copy object from link to update a destination
kODCloneAll | Clone all objects (your part should not use this)

Even when transferring only intrinsic content (and not actually cloning any objects), you should still bracket your transfer with calls to BeginClone and EndClone. That way, you notify OpenDoc of the kind of operation (such as cut or copy) that is being performed and you ensure that the right actions occur at both the source and destination of the transfer.

##### Clone

For each object that you are cloning, call the draft's Clone method. Clone allows the draft object to specify and recursively locate all objects that are to be cloned. It calls the CloneInto method of the object to be copied, which results in calls to the CloneInto methods of all referenced objects, and so on. For example, when Clone calls the CloneInto method of a part, the part clones its embedded frames; the embedded frames, in turn, clone the parts they display, and so on.

(You sometimes call the draft's WeakClone method instead of Clone, especially when you are cloning within the context of your own CloneInto method. See "CloneInto Method of Your Part Editor" for more information.)

Take these steps when calling the Clone method:

1. First, obtain an object ID to pass to Clone.

   - If you are cloning from a data-transfer object into your draft, make sure that the object you are cloning is valid. Starting with the persistent reference that specifies the object to be cloned, call the IsValidStorageUnitRef method of the storage unit or storage unit view that contains the persistent reference. Never assume that a persistent reference is valid.

     Then, get the object's ID (See "Storage-Unit IDs") from the persistent reference by calling the GetIDFromStorageUnitRef method of the storage unit.

   - If you are cloning from your draft into a data-transfer object, your access to the objects to be cloned may be through object references instead of persistent references. In that case, get an object ID by calling the referenced object's GetID method.

2. Pass the object ID to the Clone method.

3. Save the resultant object ID that Clone returns, along with the IDs returned from other calls to Clone, until cloning is complete and you have called EndClone (See "EndClone").

   (If you are not actually cloning objects but simply reading or writing intrinsic data, this is the point at which to read or write, instead of calling Clone.)

In cloning, the in-memory version of an object takes precedence over its stored version. For this reason, an object does not need to be written to storage prior to being cloned. If the object is in memory, its CloneInto method is called to perform the clone; if the object is not in memory, its storage unit performs the clone operation.

This convention also means that, if an object is in memory, properties attached to its storage unit that the object itself does not know about might not be copied, unless they are specially named; see "Clonable Data Annotation Prefix" for an explanation.

###### Null IDs When Cloning Links

The Clone method returns a value of kODNullID if the desired object cannot be cloned. For example, Clone does not allow you to clone a link object or link-source object into a link, and it returns kODNullID if you attempt to do so. In this case, because you cannot clone the object, you should delete any data associated with it that you have written into the data-transfer object. However, in the case of a link or link source you should still write the content formerly associated with the object, but as unlinked content.

###### Annotation Properties Not Cloned

When data is cloned from a data-transfer object, most of the annotation properties (such as kODPropLinkSpec) that the source part may have added to the content storage unit are not transferred, because they make no sense as properties outside of the data-transfer object. If you clone a storage unit from a data-transfer object and need these properties, you must read them from the data-transfer object's storage unit, not from the cloned storage unit.

##### EndClone

Finally, call the draft's EndClone method. EndClone commits to and actually performs the cloning operation. After EndClone completes, you can then use or reconnect the cloned objects.

1. Pass each returned object ID that you have saved to your draft's IsValidID method to determine if the object was cloned.

2. If IsValidID returns true, you can at this point reconstruct either the cloned object in memory or a persistent reference to it:

   - If you are cloning from a data-transfer object into your draft and IsValidID returns true, call your draft's AcquireObject method to read in the object and obtain a reference to it.

   - If you are cloning from your draft into a data-transfer object and if IsValidID returns true, call the GetStrongStorageUnitRef or GetWeakStorageUnitRef method of the cloned storage unit, as appropriate.

3. If IsValidID returns false, the object was not cloned and you should exclude it from the data you are reading or writing.

   If, at any time after calling BeginClone, the operation cannot be completed, you can terminate the transaction by calling the AbortClone method instead of EndClone.

Note:

> You cannot instantiate and use any cloned object until after the entire cloning operation is complete. If you are cloning several parts and link objects, for example, you cannot call AcquirePart or AcquireLink until after you have cloned all of the objects and EndClone has successfully returned.

#### Scope of a Clone Operation

For cloning, the scope defines the set of objects that are to be included in the cloning operation. Scope is expressed in terms of a frame object or its storage unit.

Because a part can have more than one display frame, and because each frame can include a separate set of embedded frames and parts, it is important to specify the frame whose enclosed objects are to be cloned. Otherwise, extra embedded parts or other objects not needed by the copy may be included unnecessarily. (You can specify null for the scope of a clone if you want all objects copied, regardless of what display frame they are associated with.)

In the example shown in the following figure, the user has selected some content in the root part that includes display frame 1 of embedded part A. The root part writes its intrinsic content and then clones part A, passing it a scope of frame A1. Any content that belongs only to frame A2 (such as part C) is not included in the clone.

Figure 62. Scope in Cloning

View figure.

Scope changes during the course of a clone. Continuing the example shown in the previous figure, the next figure shows how part A clones itself in the context of the scope (frame A1) specified by the root part. Part A writes the intrinsic content of its display frame A1 and then clones part B twice, first passing it a scope of frame B1 and then a scope of frame B2. Part B thus gets called to clone itself twice, with different scopes. Any content of B within frames B1 and B2 is included, but any content that belongs only to frame B3 is not.

Figure 63. Change in Scope as More Deeply Embedded Parts Are Cloned

View figure.

#### CloneInto Method of Your Part Editor

If your part is an embedded part that is written to the clipboard (or other data-transfer object), your part's override of its inherited CloneInto method is called by your draft's Clone method. This is the interface to CloneInto:

    void CloneInto(in ODDraftKey key,
                    in ODStorageUnit toSU,
                    in ODFrame scope);

Your CloneInto method is passed a draft key for the clone operation and a frame that defines the scope of the cloning. The method should write your part's intrinsic content to the provided storage unit, and it should clone in turn any objects (such as embedded frames or auxiliary storage units) that it references. It needn't clone any objects or write any data that is outside of the scope.

Note:

> Do not implement your CloneInto method by writing your part to storage and then cloning your storage unit. Doing so would levy performance penalties because of the extra time needed to store your data. Also, it could result in the copying of unneeded objects because the scope of the clone would be ignored.

To support efficient data transfer, your part should, if possible, write a promise (see next section) instead of writing its actual intrinsic data when CloneInto is called. It is possible to write a promise only when your part is placed into the data-transfer object as a single, standalone frame with no surrounding intrinsic content of its containing part. In any other situation, your CloneInto method might have been called to help fulfill a promise, in which case writing a promise would be inappropriate. You can determine whether you can write a promise by examining the provided storage unit. If it contains a property with the name kODPropContentFrame, your part is the sole content of the storage unit's contents property, and you can write a promise instead of actual data.

When you write a promise, be sure to identify (to yourself) the display frame or frames of your part that are within the scope of the clone operation, so that your FulfillPromise method can write the proper content when it is called.

Take these general steps in your CloneInto method:

1. Check to see if your part has already been cloned in this operation. Because an object can be referenced more than once, its CloneInto method can be called more than once in a single cloning. In general, if a contents property already exists in the storage unit passed in the method, your part has already been cloned and there is no need to repeat the process.

   An exception to this rule occurs when scope is significant. If your part is called to clone itself with different scopes during the same operation (see, for example, the previous figure), it may have to write additional data each time its CloneInto method is called.

2. Call your inherited CloneInto method.

3. If it does not already exist, add a property named kODPropContents to the provided storage unit. (You do not have to call BeginClone; that method will already have been called. Also, you do not have to add properties other than the contents property; OpenDoc will add your part's name and any other needed annotations.)

4. Check whether you can write a promise. Look for a property with the name kODPropContentFrame in the storage unit. (It is not required that any value yet exist in the property; the caller of your CloneInto method may write the value after cloning completes.)

5. Focus on the contents property, and write a value for each part kind you support. Either write a promise or the data of your part itself, using either the storage unit's SetPromiseValue or SetValue method, respectively.

   (For data transfer, it is especially important to write a standard format in addition to your own native part kind, because the ultimate destination of the transferred data is unknown.)

6. Clone all objects that your part references (that are within the scope of the cloning operation) as follows:

   - For each object to which your part has a strong persistent reference, call your draft's Clone method to clone the object, passing the same draft key that was passed to your CloneInto method. Pass a new scope, if appropriate; for example, if you are cloning an embedded part (and thus its part), that frame is the new scope.

   - For each object to which your part has a weak persistent reference, call your draft's WeakClone method, passing the appropriate draft key and scope.

     (Calling WeakClone does not by itself cause an object to be copied; it only ensures that, if the object ends up being copied because of an existing strong persistent reference to it, your part's weak persistent reference will be maintained across the cloning operation.)

#### ClonePartInfo Method of Your Part Editor

Whenever your part's display frame is cloned during data transfer, the frame calls your part's ClonePartInfo method:

    void ClonePartInfo(in ODDraftKey key,
                               in ODType partInfo,
                               in ODStorageUnitView,
                               in ODFrame scope);

You should respond to this method call by writing your part info into the provided storage unit view (regardless of the state of your part-info dirty flag), and cloning any objects referenced in your part info data.

### Promises

Clipboard transfer, drag and drop, and linking can make use of promises. A promise is a specification of data to be transferred at a future time. If a data transfer involves a very large amount of data, the source part can choose to write a promise instead of actually writing the data to a storage unit. When another part retrieves the data, the source part must then fulfill the promise. The destination part does not even know that a promise is being fulfilled; it simply accepts the data as usual.

The format of a promise is completely determined by the source part. The only restriction is that the promise must be able to be written to a storage-unit value.

You are encouraged to write promises in place of actual data in most cases; it minimizes memory requirements and increases performance.

#### Writing a Promise

Your part can follow these steps to write a promise when it is the source of a data transfer. The part is the data transfer source when it responds to a mouse-down event that initiates a drag, when it copies data to the clipboard, or when it updates a link source.

1. Gain access to the storage unit of the data-transfer object. See, for example, the initial steps under "Copying or Cutting to the Clipboard", "Initiating a Drag", and "Updating a Link at the Source". Focus the storage unit to the contents property (kODPropContents).

2. Prepare your promise. It can have any content and format you decide; you must be able to read it later and reconstruct the exact data that is to be transferred.

3. Write the promise into a single value of the storage unit, using the storage unit's SetPromiseValue method. Your promise must include at least this information:

   - A specification of the actual content that is to be delivered later.

   - A specification of the display frame (or frames) of your part involved in the data transfer. When you fulfill the promise, you can then supply the proper scope for the cloning operation.

   - A specification of the proper kind of cloning transaction (such as kODCloneCopy or kODCloneCut) to apply when you fulfill the promise.

4. Write other needed information. You do not need to clone any frames or read any actual content into the data-transfer object at this stage. However, you should create a link specification, obtain an update ID, write a suggested frame shape, and so on, as described in "Writing Intrinsic Content".

Each promise you write is for a single part kind. You can write several promises representing data of several kinds, so that the destination part has a better chance of being able to incorporate the data instead of embedding it. Because promises are private data, the actual content of all your promises can be the same, regardless of the part kind being promised. When you are called to fulfill the promise, you can inspect the provided storage-unit view object to find out which part kind is needed.

Because a promise is valid only for the duration of the current instantiation of your part, the promise can contain pointers to information that you maintain.

#### Getting Data from a Value Containing a Promise

When the Drop method of a destination part retrieves the data from a drop, or when a destination part reads data from the clipboard (using the GetValue or GetSize methods of the dragged data's or clipboard's storage unit), the source part is called to fulfill the promise. The destination part does not even know that a promise is being fulfilled; it follows the procedures described in "Reading from a Data-Transfer Object" and uses the same code whether the value contains a promise or not.

#### Fulfilling a Promise

OpenDoc calls your source part's FulfillPromise method when a promise must be fulfilled, passing it a storage-unit view object that contains the data of the promise. This is its interface:

    void FulfillPromise(in ODStorageUnitView promiseSUView);

In your implementation of the method, take steps similar to these:

1. Examine the private information that you wrote into the promise earlier, to determine what data to write.

2. Look for a property named kODPropCloneKindUsed in the storage-unit view passed to you. Your containing part may have placed the property there if your part was being cloned as a single embedded frame when your part wrote the promise. If the property exists, it contains a value of type kODCloneKind that specifies the kind of cloning transaction (such as kODCloneCopy or kODCloneCut) that you must use in fulfilling the promise.

   If the property does not exist, use information that you saved when you wrote the promise to determine the kind of cloning transaction to use.

3. Begin a cloning operation (call to BeginClone).

4. Retrieve the actual data that the promise represents and write it into the provided storage-unit view object, following the steps described in "Cloning Sequence". You can clone frames and other objects as usual at this time.

5. End the cloning operation (call EndClone).

When your FulfillPromise method completes, the destination part receives the data.

When you fulfill a promise, you must be sure to supply the source content that was selected at the time the promise was written, even if that content no longer exists in your part.

If fulfilling your promise requires cloning, you must specify the scope and the appropriate kind of cloning transaction. If you have saved that information in the promise itself, extract it and pass it to your draft's BeginClone and Clone methods, respectively.

There are some times when your part may have to fulfill a promise on its own, without its FulfillPromise method having been called. For example, when your part closes, your part's ReleaseAll method (see "Closing Your Part") must fulfill all outstanding promises. To fulfill a promise in that manner, your part needs to have kept a record of the promises (and their update IDs) that it has written. Then, in ReleaseAll, it can:

- Access the clipboard content storage unit and verify the clipboard update ID against the part's stored update ID

- Access each value that it has written and verify that it is a promise by calling the storage unit's IsPromiseValue method

- Extract the promise from the value, fulfill it, and write the fulfilled data back into the value.

You can also force fulfillment of one of your promises by focusing on the promised value and calling the GetSize method of its storage unit. That causes your FulfillPromise method to be called immediately.

### Handling Cut Data

The user can remove data from your part by selecting the Cut command or by using drag and drop to move (rather than copy) data from your part into another part. In this event, you need to take extra steps to account for the fact that the data is still valid but is no longer in your part, and also to allow the action to be undone. Keep these points in mind:

- Cutting must be an undoable action. If the data you cut from your part includes references to objects such as embedded frames or link objects, you should retain those references in an undo action (see "Adding an Action to the Undo History"). However, your part's content should release its references to the objects. Set the in-limbo flag of each cut embedded frame appropriately (see Table 5).

  Once the objects are no longer needed for undo support, OpenDoc calls your DisposeActionState method. You should then either release them entirely or remove them from your draft, depending on whether other valid references to them remain. (Follow the guidelines listed in Table 5.) See also "Undo for Clipboard" and "Undo for Drag and Drop" for related information.

- When you cut an embedded frame to a data-transfer object, call the SetContainingFrame method of the cut frame, setting its containing frame to null and severing it from your part's embedding hierarchy.

- Remove the facets of the cut frame. To access the facets of a frame that the user has drag-moved out of your part, use the embedded-facet iterator of your own display facet. For each facet to be removed, call its containing facet's RemoveFacet method and then delete the facet object itself, as described in "Removing a Facet".

- If you have more than one frame displaying an embedded part, remember that removing the embedded part from one of your display frames does not automatically remove it from the others. If the removed frame is synchronized with embedded frames in your other display frames, for example, you must remove those embedded frames also.

- Do not create a link specification for the data you have cut; the data no longer belongs to your part.

- If the data you cut to the data-transfer object includes a link source, you must call the SetSourcePart method of the link source object in your own draft, passing it a null value. That action relinquishes your part's ownership of the link source (except that you retain a reference to it in your undo data). Even if you write a promise instead of actual data, call SetSourcePart at this time; don't wait until the promise must be fulfilled.

- If you write a promise to the clipboard when cutting data from your part, you can later fulfill that promise using either a kODCloneCut or kODCloneCopy transaction (unless the storage-unit view contains a kODPropCloneKindUsed property; see "Fulfilling a Promise"). You have this flexibility because the same promise may need to be fulfilled during a paste immediately following the cut, or during subsequent pastes that do not follow the cut. Therefore, you must take all cut-specific actions at the time you write the promise (or, if the cut is undone, when you handle the undo action.)

- If you have cut data from your part and cloned it to the clipboard or drag and drop object, OpenDoc could use the actual objects that were cut from your part. Therefore, release, rather than delete, objects that you cut from your part. If you paste data into your part and then the user selects Undo, release, rather than delete, the objects you removed in the course of reversing the paste operation.

### Handling Pasted or Dropped Data

When data is pasted or dropped into a part, the part receiving the data can either embed the data as a separate part or incorporate the data as intrinsic content. The part may also, in some circumstances, translate the data, or it might even refuse to accept the pasted data.

This section discusses how OpenDoc and your part editor make these decisions, both with and without explicit user intervention. It also discusses when your part editor might explicitly translate data.

#### Default Conventions

In the absence of other instructions, OpenDoc expects your part to follow these specific conventions when pasting data from the clipboard or when accepting dropped data during a drag and drop operation.

Note:

> All parts may not behave in this way depending on how they were created.

- If the transferred data consists of an arbitrary portion of the source part's intrinsic content and any embedded parts, the destination part incorporates or embeds that outer intrinsic content, according to these rules:

  - If any of the representations of the outer data are of a part kind directly readable by the destination part editor, the editor should incorporate the outer data into the intrinsic content of the destination part, and embed any parts that were embedded in the outer data.

  - If none of the representations of the outer data is readable by the destination part editor, the editor should transfer the data as a single part (plus any embedded parts), and embed the new part in a frame in the destination.

- If the transferred data represents a single embedded frame that was cut or copied (or dragged), the destination part editor should embed the data as a separate part into the destination part, regardless of whether its part kind is different from or identical to that of the destination.

The destination part should be written to place clipboard data at the insertion point; it should place dropped data at the point where the user releases the mouse button. Note also that a pasted or dropped part takes on the view type that its containing part prefers it to have.

#### Handling the Paste As Dialog Box

The Paste As dialog box allows the user to override OpenDoc and specify whether transferred data is to be embedded, incorporated, or translated and then embedded or incorporated. It also allows the user to create a link to the source of the transferred data.

When the user chooses Paste as in the Edit menu (see "Edit Menu"), the active part should only call the ShowPasteAsDialog method on the OS/2 or Windows platforms to display the Paste As dialog box, shown in Figure 64. On the AIX platform, the part may treat a user's "Paste As" selection as a link automatically because there is no dialog to call. Also, when the user presses Alt in a drag and drop operation, the active part should call the ODDragAndDrop::ShowPasteAsDialog method (for the OS/2 and Windows platforms). See "Dropping" for more information.

Figure 64. Paste As Dialog Box

View figure.

The user can select the following options from the dialog box:

- Paste with link. The user can request that a link be created between the source and destination data. This option is not available if no link specification accompanies the data to be pasted. Disable this option if your part does not support linking.

- Get Updates (of a link) Automatically/Manually. If the user creates a link, the user selects one of these options to specify whether updates are to be automatic or manual. See "Automatic and Manual Propagation of Updates" for more information.

- Merge with contents. If the user chooses this option, the destination part editor is expected to incorporate the data, if at all possible, even if doing so requires translation and great loss of information (such as converting text to a bitmap).

  When your part editor displays the dialog box, it can check the part kinds available in the data-transfer object and specify whether this option is enabled and whether it is checked by default. Disable this option if your part cannot incorporate the data, even after translation.

- Embed As. If the user chooses this option, the source data must be embedded in the destination as a separate part, even if incorporation is possible.

  When your part editor displays the dialog box, it can check the part kinds available in the data-transfer object and specify whether this option is enabled and whether it is checked by default. Disable this option if your part does not support embedding.

  If the user selects Embed As, the user can also make the following selections:

  - Kind (of pasted data). With this option, the user can override OpenDoc's default pasting decision and explicitly specify a destination part kind from a pop-up menu. The user specifies the specific translation to perform by selecting a new part kind for the part in the kind pop-up menu. The kind pop-up menu allows the user to select a part kind to translate to.

    This option (choosing part kind) is not available if the transferred data consists of a single frame that is being moved (not copied) from its original source. The moved frame may not be the only frame displaying its part, and changing its kind would then affect the display in the other parts, which may not be what the user intended.

  - Editor. The read-only Editor field displays the editor being used by the content being pasted.

  - View type. From a pop-up menu, the user can choose the view type the embedded part is to have: frame, large icon, small icon, or thumbnail.

On the OS/2 platform and the Windows platform, the ShowPasteAsDialog method returns the results of the user's choices to your part in an ODPasteAsResult structure:

    struct ODPasteAsResult
    {
            ODBoolean           pasteLinkSetting;
            ODBoolean           autoUpdateSetting;
            ODBoolean           mergeSetting;
            ODTypeToken         selectedView;
            ODType              selectedKind;
            ODType              translateKind;
            ODEditor            editor;
    };

Depending on the contents of the returned structure, your part either embeds or incorporates the transferred data, accepts it, and either creates a link to its source or does not.

- If the user has chosen to create a link, respond as described in "Creating a Link at the Destination". If you create a link, take into account the automatic/manual update setting, as well as the other Paste As settings chosen by the user.

- If the user is not creating a link, read the transferred data in either of two ways:

  - If your part can directly read any of the part kinds in the data-transfer object's contents storage unit, and if the user has not selected "Embed As", you incorporate the data. Follow the instructions in "Incorporating Intrinsic Content".

  - If your part cannot directly read any of the part kinds in the storage unit, or if the user has selected "Embed As", follow the instructions in "Embedding a Single Part".

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