As you know, creating an object using the Insert Object dialog box is only one of the ways to get an object into a container. It is also possible to paste an object by using data from the clipboard or to accept an embedded object dropped on the container. In either case, we receive a data object's IDataObject pointer and pass it to OleCreateFromData, as occurs in CTenant::Create. To work with embedded object data, we need to register the OLE-specific clipboard formats with the Windows API RegisterClipboardFormat, which we do in the CPatronDoc constructor (DOCUMENT.CPP) and other places around Patron.
The three formats that concern us here are described in this table:
Symbol | String | Format |
CFSTR_EMBEDDEDOBJECT | "Embedded Object" | An IStorage containing the object's native data |
CFSTR_EMBEDSOURCE | "Embed Source" | Same as CFSTR_EMBEDDEDOBJECT |
CFSTR_OBJECTDESCRIPTOR | "Object Descriptor" | An OBJECTDESCRIPTOR structure in global memory that contains the object's size, aspect (iconic, content, and so on), class, and other information in which a potential consumer might be interested* |
* OBJECTDESCRIPTOR is meant to solve problems similar to those we solved with the PATRONOBJECT structure in Patron in Chapter 13, in which we wanted a graphic on the clipboard to be accompanied by data that indicates the size and placement of the object. In addition, we wanted to store the pick point of an object in drag and drop relative to its upper corner to show the feed-back rectangle in the right place. PATRONOBJECT contains all this data, whereas OBJECTDESCRIPTOR contains only extents (because placement data is generally meaningless between different applications). Patron still looks for its own format first using OBJECTDESCRIPTOR as a backup.
Now, given any data object pointer, you can determine whether an embedded object is available by calling OleQueryCreateFromData, passing the IDataObject pointer. This function basically checks for CFSTR_EMBEDDEDOBJECT and CFSTR_EMBEDSOURCE, returning S_OK if either is available or if OLE 1 embedded object data is available. Patron calls this function in CPatronDoc::FQueryPasteFromData, which we use not only to enable the Edit Paste menu item but also to determine whether we can accept an embedded object from a drag-and-drop operation. If we can paste such data, FQueryPasteFromData returns the exact information that we need in CTenant::Create to create an object from the data. When we create the object, we do not activate it initially as we do for new, uninitialized objects. This is because the object already has meaningful data in it, so the user might be fully satisfied with its contents.
You'll make very few changes to the rest of your clipboard and drag-and-drop code if you centralize format checks and object creation as I've done in Patron. With drag and drop, the only change I made was to use CFSTR_OBJECTDESCRIPTOR as a backup format with placement data; otherwise, the rest of the code stayed the same. This really shows the flexibility of drag and drop and how capable it is of working with any data formats that come along.
If you are using the Paste Special dialog box, you will need to add a new entry to the list of acceptable formats so that the dialog box shows an embedded object if one is available. This new entry should have the format CFSTR_EMBEDDEDOBJECT and the flag OLEUIPASTE_PASTE, which we include in CPatronDoc::PasteSpecial:
SETDefFormatEtc(rgPaste[1].fmtetc, m_cfEmbeddedObject
, TYMED_ISTORAGE);
rgPaste[1].lpstrFormatName="%s Object";
rgPaste[1].lpstrResultText="%s Object";
rgPaste[1].dwFlags=OLEUIPASTE_PASTE œ OLEUIPASTE_ENABLEICON;
Be careful to use OLEUIPASTE_PASTE for this entry and not OLEUIPASTE_PASTEONLY. If you use the latter (as we did for other static formats), you will not see this entry in the dialog box at all. This can increase your job stress by a few orders of magnitude. Believe me. I took six hours to figure this out. Also, no matter where you put this entry in your array of pasteable formats, the Paste Special dialog box will always list embedded objects first. This is the user interface standard.
When a user chooses to paste with data already selected in a document, the usual behavior is to replace that selection with the new data. If the selection is an embedded object marked with OLEMISC_INSERTNOTREPLACE, the container should call IOleObject::InitFromData to perform the paste instead of replacing the selected object. This allows the object to incorporate that data into itself. Patron does not support this feature.
An alternative use of this function, differentiated by a flag named fCreation, is to use a data object to initialize a newly created embedding. After calling OleCreate, you can send initial data to the object with InitFromData. This data might be a selection of data in the container itself or some other data from the clipboard or from a drag-and-drop operation.