A Feedback Rectangle

The version of Patron in Chapter 12 defined a private clipboard format named Patron Object, which contained necessary information about the original position of an object within a page:


typedef struct tagPATRONOBJECT
{
POINTL ptl; //Location of object
POINTL ptlPick; //Pick point from drag-and-drop operation
SIZEL szl; //Extents of object (absolute)
FORMATETC fe; //Actual object format
} PATRONOBJECT, *PPATRONOBJECT;

This structure contains not only the location of the original tenant but also its size (extents). We can use this information to draw the same-size rectangle in the target window to show the space that the data would occupy if a drop happened. In addition, the field ptlPick is used to store the exact location of the mouse in relation to the upper left corner of the tenant, specifically for a drag and drop. This way we can draw a feedback rectangle in relation to the mouse in the same way as shown below. In Chapter 11, Patron always stored (0,0) in this field. Now CPage::DragDrop calculates these offsets and stores them in the PATRONOBJECT structure included with the data object. This is all so that the feedback rectangle will first appear exactly over the object that is picked up.

The offset is picked up in CDropTarget::DragEnter and used to place the rectangle properly. If the PATRONOBJECT structure is unavailable, we punt and show a default rectangle size, although the representation obviously will not be true to the size of the graphic. This approach lets us avoid having the source render data simply to determine the size. In later chapters, we'll add some code to also check for a structure named OBJECTDESCRIPTOR, which is defined in OLE2UI.H for the CFSTR_OBJECTDESCRIPTOR format. This structure has information similar to PATRONOBJECT but lacks a field with the offset point of the cursor from the upper left corner of the object itself. As a result, Patron will continue to look for its own structure later on, using OBJECTDESCRIPTOR as a backup.

CDropTarget::DragEnter then copies the pick point offset (into m_ptPick) and the extents of the object (into m_szl) for later use in DragOver. DragEnter now also draws the first feedback rectangle through CPages::DrawDropTargetRect (DRAGDROP.CPP), which then calls the Windows function DrawFocusRect to draw a dotted rectangle with an XOR operation. The upper left corner of this rectangle is simply the mouse coordinates minus the offset of the pick point:


//In CDropTarget::DragEnter
pt.x-=m_ptPick.x;
pt.y-=m_ptPick.y;

m_ptLast=pt;
m_fFeedback=TRUE;
m_pDoc->m_pPG->DrawDropTargetRect(&pt, &m_szl);

DrawDropTargetRect is a toggle function because DrawFocusRect is a toggle function. Therefore, to remove the rectangle from inside the other CDropTarget functions, we have to remember where it is and whether it's currently visible. That's the purpose of the CDropTarget members m_ptLast and m_fFeedback. Early in DragOver, DragLeave, and Drop, we remove the old rectangle if it's showing:


if (m_fFeedback)
ppg->DrawDropTargetRect(&m_ptLast, &m_szl);

In DragOver, we have to remember that OLE will pulse this function even if the mouse doesn't move. In such a case, we don't want to remove the feedback rectangle only to redraw it (which would cause annoying flicker). So we leave it be if the point passed to DragOver differs from m_ptLast:


if ((pt.x-m_ptPick.x==m_ptLast.x) && (pt.y-m_ptPick.y==m_ptLast.y))
return NOERROR;