Tenant Creation, Paste

The difference between Paste Special and the typical Paste operation is that Paste does not allow selection of the specific format. For Paste, Patron conducts a series of checks to find the best format on the clipboard by calling IDataObject::QueryGetData; when it finds a suitable format, it uses the same "paste from data" function that Paste Special uses. Checking for formats occurs in CPatronDoc::FQueryPasteFromData, which returns the FORMATETC of the best format to paste. Actual pasting occurs in CPatronDoc::Paste:


BOOL CPatronDoc::FQueryPasteFromData(LPDATAOBJECT pIDataObject
, LPFORMATETC pFE, LPTENANTTYPE ptType)
{
FORMATETC fe;
HRESULT hr;

if (NULL!=(LPVOID)ptType)
*ptType=TENANTTYPE_STATIC;

//Any of our specific data here?
SETDefFormatEtc(fe, m_cf, TYMED_HGLOBAL);
hr=pIDataObject->QueryGetData(&fe);

if (NOERROR!=hr)
{
//Try metafile, DIB, and then bitmap, setting fe each time.
SETDefFormatEtc(fe, CF_METAFILEPICT, TYMED_MFPICT);
hr=pIDataObject->QueryGetData(&fe);

if (NOERROR!=hr)
{
SETDefFormatEtc(fe, CF_DIB, TYMED_HGLOBAL);
hr=pIDataObject->QueryGetData(&fe);

if (NOERROR!=hr)
{
SETDefFormatEtc(fe, CF_BITMAP, TYMED_GDI);
hr=pIDataObject->QueryGetData(&fe);
}
}
}

if (NOERROR==hr && NULL!=pFE)
*pFE=fe;

return (NOERROR==hr);
}


BOOL CPatronDoc::Paste(HWND hWndFrame)
{
LPDATAOBJECT pIDataObject;
BOOL fRet=FALSE;
FORMATETC fe;
TENANTTYPE tType;

if (NULL==m_pPG)
return FALSE;

if (FAILED(OleGetClipboard(&pIDataObject)))
return FALSE;

//Go get type and format we *can* paste; then paste it.
if (FQueryPasteFromData(pIDataObject, &fe, &tType))
fRet=PasteFromData(pIDataObject, &fe, tType, NULL, 0L);

pIDataObject->Release();
return fRet;
}

BOOL CPatronDoc::PasteFromData(LPDATAOBJECT pIDataObject
, LPFORMATETC pFE, TENANTTYPE tType, LPPATRONOBJECT ppo
, DWORD dwData)
{
BOOL fRet;
HRESULT hr;
PATRONOBJECT po;
STGMEDIUM stm;

if (NULL==pFE)
return FALSE;

//If we're not given any placement data, see if we can retrieve it.
if (pFE->cfFormat==m_cf && NULL==ppo)
{
hr=pIDataObject->GetData(pFE, &stm);

if (SUCCEEDED(hr))
{
ppo=(LPPATRONOBJECT)GlobalLock(stm.hGlobal);

po=*ppo;
ppo=&po;
GlobalUnlock(stm.hGlobal);
ReleaseStgMedium(&stm);
}
}

fRet=m_pPG->TenantCreate(tType, (LPVOID)pIDataObject, pFE
, ppo, dwData);

if (fRet)
{
//Disable Printer Setup once we've created a tenant.
if (m_fPrintSetup)
m_fPrintSetup=FALSE;

FDirtySet(TRUE);
}

return fRet;
}

PasteFromData first checks to see whether it was given explicit placement data for whatever it pastes. This way we can allow an end user to simply move a tenant on a page instead of copying it and pasting it. Placement data consists of a point for the upper left corner of the tenant on the page and the tenant extents. I separated the corner point and the extents so that they can be manipulated independently—changing the size of a tenant doesn't necessarily change the upper left corner; moving a tenant doesn't necessarily change the extents.2

In any case, during pastes from the clipboard the ppo parameter to PasteFromData will always be NULL. This means that if the clipboard holds our private Patron Object data, PasteFromData pastes the best available graphic format at the point specified by any present positioning information. In this chapter, Patron always passes a NULL in ppo to this function—a preparation for Chapter 13's work. In all honesty, I implemented this differently on my first pass through this chapter's code, and after finding it deficient for drag and drop, I came back and modified it. This is a worthy consideration for your own designs.

Now let's look at what actually creates a tenant, which is an object of the C++ class CTenant and has member functions such as Open, Load, Update, Destroy, Select, Activate, Draw, and SizeSet, just to name a few. This tenant class will expand into a compound document site in Chapter 17 and a control site in Chapter 25. In this chapter, a tenant already does much of what it needs in OLE Documents: it holds some object's IUnknown pointer, asks that object to draw through IViewObject2, and uses the OLE functions OleSave and OleLoad to save and load the object and its presentations to and from a piece of storage. (We'll see these APIs in Chapter 17.)

Each tenant's storage is a uniquely named storage element created in the storage element for the tenant's page. This happens in CTenant::Open, if you care to look, which opens the storage of a given name if it exists or, failing

that, creates a new storage of that name. The names are "Tenant n" (n is the value of a persistent counter kept in each page). The page saves this counter with a list of tenants, just as the CPages implementation saves lists of pages to its own storage element, as we saw in Chapter 7.

When Patron creates a tenant (a process that usually begins at the document level and works through CPages and then to CPage), it instantiates the tenant with new CTenant (see CPages::TenantAdd) and then asks that tenant to initialize itself (CTenant::Create) to obtain the IUnknown pointer for whatever object is stored in the tenant. Create is the only piece of code that knows how to take something like an IDataObject pointer and create an object with whatever is there. Exactly what it should try is determined by the TENANTTYPE (see TENANT.H) argument. For this chapter, the type is always TENANTTYPE_STATIC, so Create generally uses the same technique as Chapter 11's Freeloader to create a static picture. In later chapters, we'll add more types, such as compound document objects and controls.

2 The code that disables Printer Setup after creating a tenant is simply to accommodate my own laziness—I didn't want to introduce the complication of reformatting a page to a new paper size after tenants have been placed on that page. This hack has nothing to do with OLE.