Programming Guide: Drag and Drop

# Programming Guide

## Drag and Drop

The OpenDoc drag and drop facility allows users to apply direct manipulation to move or copy data. Users can drag items from one location in a part or document (or the desktop) to another location in the same or different part or document (or to the desktop).

### Drag and Drop Concepts

Drag and drop provides a direct-manipulation alternative to the clipboard. The fundamental user-level operations, copying and moving, are are similar in drag and drop and in clipboard transfer and are similar across all platforms.

#### User Interaction

The user typically initiates a drag by positioning the mouse pointer over some selected (or single-click-selectable) content, pressing and holding down the mouse button, and then moving the pointer. The user can hold down a modifier key while pressing the mouse button or while releasing the mouse button to force a drag to be a copy (called a drag-copy) or a move (called a drag-move).

As the user moves the mouse pointer, an outline of the selected item (provided by the source part, drawn by OpenDoc) is dragged to the new location. When the user releases the mouse button, the item is placed (dropped) at the pointer location. The part beneath the pointer is notified that something has been dropped on it. At this point, the destination part behaves essentially as it does when pasting from the clipboard; it makes the same decisions regarding embedding versus incorporating data and handling links. The source part may have to delete the items if it was a move operation or do nothing if it was a copy.

Parts can restrict the dropped content they will accept to specific part kinds. To indicate that it can accept a drop, a part provides appropriate feedback to the user once the mouse pointer enters its facet.

For frames and icons that are being dragged, OpenDoc provides specific behavior and appearance for the item being dragged. For intrinsic content that is being dragged, your part editor is responsible for defining behavior and providing user feedback.

The user can move an active frame by dragging its border. By moving the pointer to the active frame border and pressing the mouse button, the user selects the frame and can start dragging immediately.

The user can move a selected frame by moving the pointer anywhere within it and pressing the button to start a drag. Also, if a frame is bundled, the user can drag it by pressing the mouse button while the pointer is anywhere in the interior of the frame, whether or not the frame is already active or selected.

When the user releases the mouse button, the frame and its part are dropped at the new location. After a frame is dropped, it becomes selected. The destination may adjust the drop location of a frame based on constraints such as gridding (in a graphics part) or text position (in a text part).

#### Move and Copy

OpenDoc follows these conventions for drag and drop:

- When a user drags an item within a document, the item is moved, not copied. A window boundary is not always a document boundary; dragging an item to or from a part window, but still within the same document, results in a move.

- When the user drags an item across a document boundary, the item is copied, not moved.

- When the user employs drag and drop to move a frame, the moved frame displays the original part if the destination is in the same document. However, if the destination is in a different document, the source frame is deleted and a new part (and new frame displaying the contents) is created at the destination. (In contrast, clipboard pasting always creates a new part and frame.)

OpenDoc supports a mechanism for you to allow the user to force a copy or move operation by pressing modifier keys.

##### Dragging Stationery

Stationery parts may be copied exactly like any other part. However, if you drag-move stationery into a part whose preferred view type for embedded parts is frame view, a copy is "torn off" the stationery pad and displayed in a frame. The stationery part remains in its original location, unchanged.

#### Droppable Frames

Each frame has a SetDroppable method, through which the part displayed in the frame controls whether or not the frame can (in general) accept dropped data. When a display frame is added or reconnected to your part, you can call SetDroppable and pass either kODTrue or kODFalse to allow or prohibit dropping. (When initially created, frames are not droppable.) You can call SetDroppable again at any time to change the state of the frame.

OpenDoc calls the IsDroppable method of the frame before making any of the drag-related method calls DragEnter, DragWithin, DragLeave, or Drop to your part. If your display frame is not droppable, your part does not receive any of these calls.

Your part's display frame does not have to be droppable for you to initiate a drag from it.

#### Undo for Drag and Drop

If your part supports dragging and dropping data, it must also support undoing that operation. Data transfer between parts is undoable only if both parts have undo support. Undo support in general is described in "Undo".

For drag and drop, undo involves a multistage action. The part initiating the drag begins the action, the part receiving the drop adds a single action, and the initiating part completes the action when the drop completes. See "Adding Multistage Actions" for more details. If drag and drop involves embedded frames, be sure to set the dragged frames' in-limbo flags appropriately when initiating a drag, when dropping, and when the drag completes. See Table 5 for details.

### Initiating a Drag

In response to a mouse-down event at the appropriate location, the part receiving the mouse event (the source part) has the choice of initiating a drag. You should follow the procedures listed under "Mouse Events, Activation, and Dragging" in deciding whether to initiate a drag operation.

To initiate a drag, you (the source part) should follow these steps:

1. Call the GetDragAndDrop method of the session object to get access to the ODDragAndDrop object. Then call the Clear method and the GetContentStorageUnit method of the drag and drop object to get an empty storage unit into which to copy the dragged data.

   There is no drag-and-drop focus or lock to be acquired. Only one part at a time can initiate and complete a drag.

2. Write the dragged data (or write a promise) into the drag and drop storage unit: When the data is intrinsic content follow the procedure given in "Writing Intrinsic Content".

3. Create a property with the name kODPropMouseDownOffset in the drag and drop storage unit, and write into it a value (of type kODPoint) that specifies the offset of the mouse from the origin of the selection or item that is being dragged. This offset allows the destination part to correctly locate the item in relation to the mouse-up event when it is dropped.

4. If there are frames that should not accept a drop from this drag, call the frames' SetDragging method and pass a value of kODTrue to notify OpenDoc that it should not allow them to be drop targets. A frame should not accept a drop if the frame itself is begin dragged.

5. If the drag involves an embedded frame, follow the instructions listed in Table 5. Set the frames's in-limbo flag to true if a drag-move is possible.

6. Add a beginning action to the undo action history (see "Adding Multistage Actions"), to allow the user to undo the drag if necessary.

7. Initiate the drag by calling the drag and drop object's StartDrag method. When you call StartDrag, you are responsible for providing OpenDoc an image, such as an outline, so the user can see the drag occur.

The StartDrag method completes when the drop occurs; see "Completion of StartDrag".

### Operations While a Drag Is in Progress

As long as the mouse button remains pressed, the drag is in progress. Potential destination parts need to perform certain actions during dragging when the mouse pointer is within their facets.

#### Entering a Part's Facet

Any facet the mouse pointer passes over during a drag represents a potential drop destination, if the facet's frame is droppable. OpenDoc calls a part's DragEnter method when the pointer enters one of its droppable frames' facets during a drag:

    ODDragResult DragEnter (in ODDragItemIterator dragInfo,
                                        in ODFacet facet,
                                        in ODPoint where);

On receiving a call to its DragEnter method, your part (the potential destination part) should take these steps:

1. Examine the part kinds of the dragged data, using the drag-item iterator (class ODDragItemIterator) passed to it. Inspect the part kinds in each item's storage unit and determine whether or not you can accept the dragged data.

   Note:

   > (For the OS/2 platform). As for the clipboard object, the OS/2 implementation of the ODDragAndDrop object provides two extra methods to help parts in determining whether or not they can accept the dragged data; they are called CanEmbed and CanIncorporate.

   > CanEmbed queries the registration manager for a part editor capable of handling the content of the drag item. If a match is found, the corresponding rendering mechanism and format pair will be saved in the drag item storage unit (under the the kODSelectedRMF value of the kODPropContents property) to be used later for rendering in the event a drop ocurrs in this facet. This method should be used by container parts.

   > CanIncorporate determines if the drag item content matches a specific kind. Parts should call this method for the kinds they support. As for CanEmbed, if a match is found, the corresponding rendering mechanism and format pair will be saved in the drag item storage unit.

   If your part cannot accept all the dragged items, you should not accept a drop at all.

2. If your part can accept a drop, provide the appropriate feedback to the user, such as adorning the frame border; otherwise, take no action.

   When the user initiates a drag from within your frame, you should not display destination feedback as long as the mouse pointer has not yet left your frame, although you should provide feedback if the mouse pointer returns to your frame after having left it.

   You can draw the drag feedback on your own, or with the help of platform-specific services.

3. Examine the current state of the user's system, if desired, to obtain any relevant information (such as whether the user has pressed a modifier key since initiating the drag).

   If you return kODFalse from this method, OpenDoc assumes you cannot accept a drop and will not subsequently call your DragWithin, DragLeave, or Drop methods as long as the mouse pointer remains in this facet.

#### Within a Part's Facet

OpenDoc calls the potential destination part's DragWithin method continuously while the mouse pointer remains inside the facet:

    ODDragResult DragWithin (in ODDragItemIterator dragInfo,
                             in ODFacet facet,
                             in ODPoint where);

In response, the part can do any desired processing inside of its display. For example, if a part allows objects to be dropped only in individual hot spots, it may change its feedback based on mouse-pointer location.

Calls to a part's DragWithin method also give the part an additional chance to examine the state of the user's system. For example, the part may want to find out whether the user has pressed a modifier key during the drag operation.

If you return kODFalse from this method, OpenDoc assumes you no longer wish to accept a drop in this facet. It will not make additional calls to your DragWithin method, and will not call your DragLeave or Drop method, as long as the mouse pointer remains in this facet.

#### Leaving a Part's Facet

OpenDoc calls the DragLeave method of a potential destination part when the mouse pointer leaves a droppable facet:

    void DragLeave (in ODFacet facet,
                    in ODPoint where);

In response, the part might remove the adornment on its frame.

### Dropping

If the user releases the mouse button while the pointer is within a facet, OpenDoc calls the Drop method of the facet's part. This is the interface to the Drop method:

    ODDropResult Drop (in ODDragItemIterator dropInfo,
                       in ODFacet facet,
                       in ODPoint where);

The potential destination part then decides whether it can receive the dragged data. If it accepts the data, it either incorporates it or embeds it. This section describes how to design your drop method, how to accept a drop of non-OpenDoc data, and what the source part (the part that initiated the drag) should do after the drop occurs.

#### Drag Attributes and the Drop Method

The destination part can inspect the drag attributes (by calling the GetDragAttributes method of the drag and drop object) to determine how to handle the drop. Drag attributes are bit flags, and more than one can be set at a time.

These are the possible drag attributes for a drop:

Constant | Description
kODDragIsInSourceFrame | The item being dragged has not yet left the source frame of the drag.
kODDragIsInSourcePart | The item being dragged is in the source part of the drag.
kODDropIsInSourceFrame | The drop is occurring in the source frame of the drag.
kODDropIsInSourcePart | The drop is occurring in the part displayed in the source frame of the drag (though not necessarily in the source frame itself).
kODDropIsMove | This drag and drop operation is a move.
kODDropIsCopy | This drag and drop operation is a copy.
kODDropIsLink | This drag and drop operation is a link.
kODDropIsPasteAs | The destination part should display the Paste As dialog box.

Your Drop method might follow steps similar to these:

1. Use the ODDragItemIterator passed to you to determine whether you can handle the drop, as described in "Entering a Part's Facet". (Alternatively, you could set a "can-drop" flag in DragEnter or DragWithin that you inspect at this point.)

2. Examine the drag attributes to see, for example, whether this is a move or a copy, and whether or not to display the Paste As dialog box.

3. (OS/2 implementation only.) Focus the drag item storage unit on the kODDragitem value of the kODPropContents property and create a storage unit view. Then call the ODDragAndDrop object GetDataFromDragManager to render the data. Upon successful completion,this method will place the rendered data in the returned storage unit.

4. On the OS/2 and Windows platforms, if the drag attribute kODDropIsPasteAs is set (that is, if the user held down the Alt key when the drop occurred), take these steps before reading from the drag and drop object:

   - Call the ShowPasteAsDialog method of the drag and drop object to display a Paste As dialog box (see Figure 64).

   - If the ShowPasteAsDialog method returns a result of true, the user has pressed the OK button; use the results of the interaction (passed back as a structure of type ODPasteAsResult) to determine what action to take. "Handling the Paste As Dialog Box" lists the kinds of pasting that the user can specify.

5. Depending on the nature of the data and the user's instructions, you may either incorporate the data or embed it, you may first translate it, and you may create a link to its source.

   - If you are creating a link, follow the steps in "Creating a Link at the Destination". Take into account the automatic/manual update setting, as well as the other Paste As settings chosen by the user.

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

   - If you are simply embedding the data, follow the steps in "Embedding a Single Part".

   In all of these cases, you initially read the data from the drag and drop object, and you specify the following kinds of clone transactions:

   - If a move is specified, the drag attribute kODDropIsMove is set, use a clone transaction of kODCloneDropMove. Use kODCloneDropMove unless the user has specified that a link be created. Creating a link takes precedence over specifying a drag-move; therefore, use a clone transaction of kODCloneDropCopy in this case.

   - If a copy is specified (the drag attribute kODDropIsCopy is set), use a clone transaction of kODCloneDropCopy.

   Note:

   > The destination part can override a drag that is a move and force it to be a copy. It cannot, however, override a copy and force it to be a move.

6. Add a single action to the action history (see "Adding an Action to the Undo History") so that the user can undo the drop. "Adding Multistage Actions" describes why this action must be a single-stage action.

   If the drop involves an embedded frame, follow the instructions listed in Table 5. Save the current value of the frame's in-limbo flag and then set the flag to false.

7. If it is not already active, activate the frame in which the drop occurred, and select the dropped content.

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

When it completes, your Drop method should return an appropriate result (of type ODDropResult), such as kODDropCopy, kODDropMove, or kODDropFail. OpenDoc in turn passes that information on to the source part; see "Completion of StartDrag".

#### Accepting Non-OpenDoc Data

When your part's Drop method is called, it is passed a drag-item iterator (class ODDragItemIterator), so that you can access all drag items in the drag and drop object. If OpenDoc data has been dragged, there is only one drag item in the object, but if the data comes from outside of OpenDoc, there may be more than one item. It is the responsibility of the destination part to iterate through all the drag items to find out whether it can accept the drop.

You can examine the contents property of the storage unit of each dragged item to determine the kind of data it consists of. If you can read data of that type, you can incorporate it into your part. Otherwise, you may be able to embed it as a separate part.

- To access a dropped file whose data you intend to incorporate, you can obtain the filename from the kODFileType value in the contents property of the item's storage unit. You can then use that information to make file-system-specific calls to open the file and process its contents into your part.

- To embed dropped non-OpenDoc data as a part, you treat it just as you would OpenDoc data. You follow the procedures described in "Embedding a Single Part", specifying a cloning operation of kODClonePaste or kODCloneDropMove for the content storage unit of the dropped data. OpenDoc locates and binds a part editor to the data. That part editor itself then must obtain the item's filename, access the file, read its data, and incorporate the data into the part's contents.

#### Completion of StartDrag

If your part is the source part of this drag, your call to the drag and drop object's StartDrag method completes after the destination's Drop method completes. You can now, based on the return value of StartDrag, confirm whether the operation was a move or a copy or whether it failed. Take these steps:

1. Whether it was a move or a copy, add an end action to the undo action history (see "Adding Multistage Actions"), so that the user can undo the entire transaction, from initiating the drag to dropping the data. If it was a move, note any applicable special considerations given in "Handling Cut Data".

   If the completed drag was initiated as a drag-move but ended up being a drag-copy, and if it involved an embedded frame, follow the instructions listed in Table 5 (reset the frames's in-limbo flag to false).

2. If it was a move, delete the dragged content from your part and notify OpenDoc and your containing part that there has been a change to your content. See "Making Content Changes Known".

3. Whether it was a move or a copy, call your dragged frames's SetDragging methods once again, this time passing them a value of kODFalse to notify OpenDoc that the frames can once more be drop targets.

4. If the destPart parameter that was returned is not equal to kODNull, call the Release method on the destination part.

   Asynchronous drag and drop: On the AIX platform, OpenDoc provides the ODPart method DropCompleted. This method is called to notify the source part of the completion of a drag that it initiated. The method provides the same drop results that StartDrag returns for a synchronous drag.

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