Simple Drag and Drop, Step by Step: Cosmo

To demonstrate a basic drag-and-drop implementation, we'll add the feature to the Cosmo sample (CHAP13\COSMO). These changes apply to Component Cosmo as well (CHAP13\COCOSMO), but because both share the same code, we'll see only Cosmo's code in this section. Cosmo's idea of a document is a single Polyline figure that never has scroll bars, so there is no reason to worry about scrolling as a target. In addition, there is already an 8-pixel border around the figure that serves as a perfect pick region. This is the only area of Cosmo's document window class that is visible, so we can say that any mouse button down event in that window causes a pick. Because of this, there is no reason to support mouse debouncing. We'll leave these topics to "Advanced Drag and Drop: Patron" later in this chapter.

Even with these simplifications, we'll make a Cosmo document both the source and the target for Cosmo's private Polyline format. (Instances of Cosmo and CoCosmo will also work together.) Most applications will usually be a source and a target for a private format because those applications can usually copy, cut, and paste private formats using the clipboard within a document window, between document windows, or between applications. OLE Drag and Drop is just as flexible, and in Cosmo's implementation, we'll set up the feature in four source steps and three target steps. We'll do the source side first because it's simpler and because the data object includes graphical formats (metafile and bitmap) that can be dropped into a variety of existing targets that you probably have on your machine (such as an OLE-aware word processor).

The source steps are as follows:

Design the user interface for the operation, including the mouse cursors for each effect and any feedback in the source. (OLE provides default cursors.)

Determine the pick event (for example, left mouse button down) and the drop event (left mouse button up).

Implement the object with IDropSource, which is usually trivial.

Call DoDragDrop when the pick event occurs, and handle both copy and move effects after a drop occurs.

Implementation steps for the target are the following:

Design and implement the end-user feedback code to indicate the result of the drop. Do this first so that step 2 can use it.

Implement the object with IDropTarget. Functions such as DragEnter and DragLeave use the end-user feedback code created in step 1.

Call RegisterDragDrop and RevokeDragDrop, along with CoLockObjectExternal, to manage availability and stability of the target's IDropSource object.

The following sections look at each of the steps in detail. The CDropSource class, which singly inherits members from IDropSource in COSMO.H and is implemented in DROPSRC.CPP, implements the source side. The CDropTarget class, defined in COSMO.H and implemented in DROPTGT.CPP, provides the target side. Both objects have trivial constructors, destructors, and IUnknown members. The objects maintain their own reference counts and delete themselves in Release as usual. (There is no need to do anything more.)

A few changes to DOCUMENT.CPP fill out Cosmo's drag-and-drop responsibilities. One special note about the document class (CCosmoDoc): it now has a BOOL member named m_fDragSource that is set to TRUE when the user picks up data in that document. Because the same document might be a target, this flag allows Cosmo to detect a drag and drop within the same document. Cosmo doesn't need to do anything when a drop happens on the same source document, so we can avoid unnecessary code and processing with this simple flag.