home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 1998 May
/
Pcwk5b98.iso
/
Borland
/
Cplus45
/
BC45
/
BOCOLE.PAK
/
BOLECLIP.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1995-08-29
|
26KB
|
662 lines
//
//**************************************************************************
//
// BOleClip.cpp -- Some boilerplate clipboard and drag/drop code
//
// Copyright (c) 1993,94 by Borland International, Inc. All rights reserved
//
//**************************************************************************
#include "BOle.h"
#include "BOleSite.h"
#include "OLE2.h"
#include "BOleSvc.h"
#include "BOleDoc.h"
extern "C" {
#include "OLE2UI.h"
}
#ifdef WIN32
//
// Take a wide char string & return an ANSI string in a new'd char[] buffer
//
char* ConvertWtoA(const wchar_t* src, size_t len)
{
size_t size = WideCharToMultiByte(CP_ACP, 0, src, len, 0, 0, 0, 0);
char* dst = new char[size + (len != -1)]; // room for null if fixed size
size = WideCharToMultiByte(CP_ACP, 0, src, len, dst, size, 0, 0);
if (len != -1)
dst[size] = 0;
return dst;
}
#endif
// GetObjDescData -- Fabricate the "Object Descriptor" clipboard format
// structure for the server-side helper.
//
HGLOBAL _IFUNC BOleSite::GetObjDescData ()
{
HGLOBAL hMem = NULL;
CLSID cid;
HRESULT hr = CLSIDFromProgID (pszProgID, &cid);
if (!SUCCEEDED(hr))
return NULL;
DWORD miscStatus = 0;
hr = ::OleRegGetMiscStatus (cid, DVASPECT_CONTENT, &miscStatus);
if (!SUCCEEDED(hr))
return NULL;
miscStatus |= OLEMISC_CANLINKBYOLE1;
SIZEL sizel;
SIZE size;
pProvider->GetPartSize (&size);
sizel.cx = MAP_PIX_TO_LOGHIM (size.cx, BOleService::pixPerIn.x);
sizel.cy = MAP_PIX_TO_LOGHIM (size.cy, BOleService::pixPerIn.y);
POINTL pointl;
pointl.x = pointl.y = 0;
// If we have a moniker we can use that for the instance name
LPOLESTR pszDisplay = NULL; // must be freed later
{
LPMONIKER pMoniker;
hr = GetMoniker(OLEGETMONIKER_ONLYIFTHERE,
OLEWHICHMK_OBJFULL, &pMoniker);
if (SUCCEEDED(hr)) {
LPBC pbc;
CreateBindCtx(0, &pbc);
pMoniker->GetDisplayName(pbc, NULL, &pszDisplay);
pbc->Release();
pMoniker->Release();
}
}
LPOLESTR pszType = NULL; // must be freed later
hr = ::OleRegGetUserType (cid, USERCLASSTYPE_FULL, &pszType);
if (SUCCEEDED(hr)) {
DWORD dwObjectDescSize, dwFullUserTypeNameLen, dwSrcOfCopyLen;
LPOLESTR pszInstance = pszDisplay;
// Get the length of Full User Type Name; Add 1 for the null terminator
dwFullUserTypeNameLen = (pszType ? lstrlen(pszType)+1 : 0) * sizeof(OLECHAR);
// Get the Source of Copy string and it's length; Add 1 for the null terminator
if (pszInstance)
dwSrcOfCopyLen = (lstrlen(pszInstance)+1) * sizeof(OLECHAR);
else {
// No src moniker so use user type name as source string.
pszInstance = pszType;
dwSrcOfCopyLen = dwFullUserTypeNameLen;
}
// Allocate space for OBJECTDESCRIPTOR and the additional string data
//
dwObjectDescSize = sizeof(OBJECTDESCRIPTOR);
hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
dwObjectDescSize + dwFullUserTypeNameLen + dwSrcOfCopyLen);
if (hMem) {
// Fill in the object descriptor
//
LPOBJECTDESCRIPTOR lpOD = (LPOBJECTDESCRIPTOR)GlobalLock(hMem);
// Set the FullUserTypeName offset and copy the string
if (pszType) {
lpOD->dwFullUserTypeName = dwObjectDescSize;
LPSTR pDest = (LPSTR)lpOD + dwObjectDescSize; // dwObjectDescSize is BYTE offset
lstrcpy((LPOLESTR)pDest, pszType);
}
else lpOD->dwFullUserTypeName = 0; // zero offset indicates that string is not present
// Set the SrcOfCopy offset and copy the string
if (pszInstance) {
LPSTR pDest = (LPSTR)lpOD + dwObjectDescSize + dwFullUserTypeNameLen; // dwObjectDescSize is BYTE offset
lpOD->dwSrcOfCopy = dwObjectDescSize + dwFullUserTypeNameLen;
lstrcpy((LPOLESTR)pDest, pszInstance);
}
else lpOD->dwSrcOfCopy = 0; // zero offset indicates that string is not present
// Initialize the rest of the OBJECTDESCRIPTOR
lpOD->cbSize = dwObjectDescSize + dwFullUserTypeNameLen + dwSrcOfCopyLen;
lpOD->clsid = cid;
lpOD->dwDrawAspect = DVASPECT_CONTENT;
lpOD->sizel = sizel;
lpOD->pointl = pointl;
lpOD->dwStatus = miscStatus;
GlobalUnlock(hMem);
}
}
// Free pszType and pszDisplay
//
LPMALLOC pMalloc;
hr = ::CoGetMalloc (MEMCTX_TASK, &pMalloc);
if (SUCCEEDED(hr)) {
if (pszType && pMalloc->DidAlloc(pszType)) {
pMalloc->Free (pszType);
pszType = NULLP;
}
if (pszDisplay && pMalloc->DidAlloc(pszDisplay)) {
pMalloc->Free (pszDisplay);
pszDisplay = NULLP;
}
pMalloc->Release ();
}
return hMem;
}
// GetPasteIcon -- Retrieve the iconic representation of the data object
// from the clipboard if necessary. OLE2UI does this for us
// for Paste Special, but we need to do it ourselves for
// Paste.
//
HMETAFILE _IFUNC BOleService::GetPasteIcon (LPDATAOBJECT pData)
{
HMETAFILE hIconMetafile = NULL;
HGLOBAL hObjDesc = NULL;
STGMEDIUM medObjDesc;
STGMEDIUM medIconMetafile;
hObjDesc = GetDataFromDataObject (pData, BOleDocument::oleObjectDescFmt,
NULL, DVASPECT_CONTENT, &medObjDesc);
if (hObjDesc) {
LPOBJECTDESCRIPTOR pObjDesc = (LPOBJECTDESCRIPTOR) WIN::GlobalLock (hObjDesc);
if (pObjDesc && pObjDesc->dwDrawAspect == DVASPECT_ICON)
hIconMetafile = (HMETAFILE) GetDataFromDataObject (pData,
CF_METAFILEPICT, NULL, DVASPECT_ICON, &medIconMetafile);
WIN::GlobalUnlock (hObjDesc);
OLE::ReleaseStgMedium (&medObjDesc);
}
return hIconMetafile; // should be freed in IBPart::Init
}
// PasteHelper -- Does both paste and paste special.
//
// The Paste Special dialog is borrowed from OLE2UI, but
// it isn't mandatory to use it. You can always fill in
// the BOleInitInfo yourself and send it to IPart::Init
//
// Similarly, you can also get the available clipboard
// formats using the normal Windows API, and only if you
// decide to create an OLE2 object (link or embed), call
// IPart::Init
//
BOOL _IFUNC BOleService::PasteHelper (BOleInitInfo FAR *pInfo, BOOL fPasteSpecial)
{
UINT ret = OLEUI_FALSE;
// Initialize hIcon to NULL so as to not free the icon in BOlePart::Init
pInfo->hIcon = NULL;
// Find the container we should paste into
//
PIBContainer pCont = NULL;
if (pInfo->pContainer) {
pCont = pInfo->pContainer;
}
else {
if (!pActiveDoc || !pActiveDoc->GetContainer())
return FALSE;
pCont = pActiveDoc->GetContainer();
}
// From the container, get the data consumer
//
PIBDataConsumer pConsumer = NULL;
HRESULT hr = pCont->QueryInterface (IID_IBDataConsumer, &(LPVOID) pConsumer);
if (!SUCCEEDED(hr))
return FALSE;
else
pConsumer->Release();
OLEUIPASTESPECIAL ops;
_fmemset (&ops, 0, sizeof (ops));
LPDATAOBJECT pDataObj = NULL;
OLEUIPASTEENTRY *pOle2UIEntries = NULL;
BOleFormat *pBOleEntries = NULL;
UINT *pBOleLinkEntries = NULL;
UINT nAcceptableFormats = 0, nLinkableFormats = 0;
UINT i;
CLIPFORMAT cf;
// If this fails, the clipboard may be empty or otherwise unavailable
//
hr = OLE::OleGetClipboard (&pDataObj);
if (S_OK != GetScode (hr))
return FALSE;
// The idea here is to insulate Bolero users from Ole2UI. BOleFormat
// is only marginally simpler than OLEUIPASTEENTRY, so I'm not sure
// there's a big complexity win.
//
nAcceptableFormats = pConsumer->CountFormats ();
if (!nAcceptableFormats)
goto cleanup;
pOle2UIEntries = new OLEUIPASTEENTRY [nAcceptableFormats];
if (!pOle2UIEntries)
goto cleanup;
pBOleEntries = new BOleFormat[nAcceptableFormats];
if (!pBOleEntries)
goto cleanup;
pBOleLinkEntries = new UINT[nAcceptableFormats];
if (!pBOleLinkEntries)
goto cleanup;
for (i = 0; i < nAcceptableFormats; i++) {
//_fmemset (&pOle2UIEntries[i], 0, sizeof (OLEUIPASTEENTRY));
pConsumer->GetFormat (i, &pBOleEntries[i]);
pOle2UIEntries[i].fmtetc.cfFormat = pBOleEntries[i].fmtId;
pOle2UIEntries[i].fmtetc.ptd = NULL;
pOle2UIEntries[i].fmtetc.dwAspect = DVASPECT_CONTENT;
pOle2UIEntries[i].fmtetc.tymed = pBOleEntries[i].fmtMedium & ~BOLE_MED_STATIC;
pOle2UIEntries[i].fmtetc.lindex = -1;
// The paste special dialog accepts names in Ascii only
//
#ifdef UNICODE
pOle2UIEntries[i].lpstrFormatName =
(LPCOLESTR)( pBOleEntries[i].fmtName[0] ? ConvertWtoA(pBOleEntries[i].fmtName, -1)
: "%s" );
pOle2UIEntries[i].lpstrResultText =
(LPCOLESTR)( pBOleEntries[i].fmtResultName[0] ?
ConvertWtoA(pBOleEntries[i].fmtResultName, -1) : "%s" );
#else
pOle2UIEntries[i].lpstrFormatName =
( pBOleEntries[i].fmtName[0] ?
pBOleEntries[i].fmtName : "%s" );
pOle2UIEntries[i].lpstrResultText =
( pBOleEntries[i].fmtResultName[0] ?
pBOleEntries[i].fmtResultName : "%s" );
#endif
pOle2UIEntries[i].dwFlags =
pBOleEntries[i].fmtIsLinkable ? OLEUIPASTE_PASTE : OLEUIPASTE_PASTEONLY;
if (pBOleEntries[i].fmtId == BOleDocument::oleEmbdObjClipFmt ||
pBOleEntries[i].fmtId == BOleDocument::oleLinkSrcClipFmt ||
pBOleEntries[i].fmtId == BOleDocument::oleEmbSrcClipFmt)
pOle2UIEntries[i].dwFlags |= (OLEUIPASTE_PASTE | OLEUIPASTE_ENABLEICON);
//
//
if (pBOleEntries[i].fmtIsLinkable) {
pBOleLinkEntries[nLinkableFormats] = pBOleEntries[i].fmtId;
DWORD f = 0;
switch (nLinkableFormats) {
case 0 : f = OLEUIPASTE_LINKTYPE1; break;
case 1 : f = OLEUIPASTE_LINKTYPE2; break;
case 2 : f = OLEUIPASTE_LINKTYPE3; break;
case 3 : f = OLEUIPASTE_LINKTYPE4; break;
case 4 : f = OLEUIPASTE_LINKTYPE5; break;
case 5 : f = OLEUIPASTE_LINKTYPE6; break;
case 6 : f = OLEUIPASTE_LINKTYPE7; break;
case 7 : f = OLEUIPASTE_LINKTYPE8; break;
}
pOle2UIEntries[i].dwFlags |= f;
nLinkableFormats++;
}
}
if (fPasteSpecial) {
// Fill in the parameter block to the OLE2UI Paste Special dialog box
//
ops.cbStruct = sizeof (ops);
ops.dwFlags = PSF_SELECTPASTE;
if (ShowHelpButton (BOLE_HELP_BROWSECLIPBOARD))
ops.dwFlags |= PSF_SHOWHELP;
ops.hWndOwner = ::GetActiveWindow(); // pApp->GetWindow ();
ops.lpszCaption = TEXT("Paste Special"); //
ops.lpfnHook = NULL;
ops.lCustData = NULL;
ops.hInstance = NULL;
ops.lpszTemplate = NULL;
ops.hResource = NULL;
ops.arrPasteEntries = pOle2UIEntries;
ops.cPasteEntries = nAcceptableFormats;
ops.lpSrcDataObj = pDataObj;
ops.arrLinkTypes = pBOleLinkEntries;
ops.cLinkTypes = nLinkableFormats;
ops.cClsidExclude = 0;
ops.dwIBApplication = (DWORD) pApp;
EnterBOleDialog (TRUE, ops.hHook, ops.hTask);
ret = OleUIPasteSpecial (&ops);
EnterBOleDialog (FALSE, ops.hHook, ops.hTask);
if (ret == OLEUI_OK) {
if (ops.dwFlags & PSF_CHECKDISPLAYASICON)
pInfo->hIcon = (HICON) ops.hMetaPict;
i = ops.nSelectedIndex;
}
else
goto cleanup;
}
else {
i = MatchPriorityClipFormat (pDataObj, pOle2UIEntries, nAcceptableFormats);
if (i == 0xFFFF)
goto cleanup;
(HMETAFILE) pInfo->hIcon = GetPasteIcon (pDataObj);
ret = OLEUI_OK;
}
cf = pBOleEntries[i].fmtId;
if (cf == BOleDocument::oleEmbdObjClipFmt) {
if (ops.fLink) {
pInfo->How = BOLE_LINK;
pInfo->Where = BOLE_DATAOBJECT;
pInfo->whereData.pData = pDataObj;
}
else {
pInfo->How = BOLE_EMBED;
pInfo->Where = BOLE_DATAOBJECT;
pInfo->whereData.pData = pDataObj;
}
}
else if (cf == BOleDocument::oleEmbSrcClipFmt) {
if (ops.fLink) {
pInfo->How = BOLE_LINK;
pInfo->Where = BOLE_DATAOBJECT;
pInfo->whereData.pData = pDataObj;
}
else {
pInfo->How = BOLE_EMBED;
pInfo->Where = BOLE_DATAOBJECT;
pInfo->whereData.pData = pDataObj;
pInfo->pStorage = NULL;
}
}
else if (cf == BOleDocument::oleLinkSrcClipFmt) {
pInfo->How = BOLE_LINK;
pInfo->Where = BOLE_DATAOBJECT;
pInfo->whereData.pData = pDataObj;
}
else if (pBOleEntries[i].fmtMedium & BOLE_MED_STATIC) {
pInfo->How = (cf == CF_METAFILEPICT) ?
BOLE_EMBED_ASMETAFILE : BOLE_EMBED_ASBITMAP;
pInfo->Where = BOLE_DATAOBJECT;
pInfo->whereData.pData = pDataObj;
}
else {
STGMEDIUM medium;
pInfo->How = ops.fLink ? BOLE_LINK : BOLE_EMBED;
pInfo->Where = BOLE_HANDLE;
pInfo->whereHandle.dataFormat = cf;
if (!ops.fLink)
pInfo->whereHandle.data = GetDataFromDataObject (pDataObj, cf, NULL,
DVASPECT_CONTENT, &medium);
// Normally, the user is expected to call ReleaseDataObject on the
// BOleInitInfo, but in this case we aren't passing back the data
// object, only a handle to real data, so we have to release the
// data object here
//
pDataObj->Release();
// We're not going to draw as an icon if we're not creating
// an OLE object (i.e. can't draw CF_TEXT as an icon)
//
OleUIMetafilePictIconFree (pInfo->hIcon); // clean up metafile
pInfo->hIcon = NULL;
}
cleanup:
// Clean up our various scratch buffers etc
//
if (pBOleEntries)
delete [] pBOleEntries;
if (pOle2UIEntries)
delete [] pOle2UIEntries;
if (pBOleLinkEntries)
delete [] pBOleLinkEntries;
// Since the BOleInitInfo is considered invalid if we fail out of
// IBService::Paste (e.g. user cancels the dialog), they can't call
// ReleaseDataObject so we should do it instead.
//
if (ret != OLEUI_OK && pDataObj)
pDataObj->Release();
//
//
return ((ret == OLEUI_OK) ? TRUE : FALSE);
}
// Get data in a specified format from a data object. This doesn't really
// have to be on the BOleService, but I couldn't think of anywhere more
// appropriate.
//
HGLOBAL _IFUNC BOleService::GetDataFromDataObject (
LPDATAOBJECT lpDataObj,
CLIPFORMAT cfFormat,
DVTARGETDEVICE FAR* lpTargetDevice,
DWORD dwAspect,
LPSTGMEDIUM lpMedium
)
{
HRESULT hrErr;
FORMATETC formatetc;
HGLOBAL hGlobal = NULL;
HGLOBAL hCopy;
LPVOID lp;
formatetc.cfFormat = cfFormat;
formatetc.ptd = lpTargetDevice;
formatetc.dwAspect = dwAspect;
formatetc.lindex = -1;
switch (cfFormat) {
case CF_METAFILEPICT:
formatetc.tymed = TYMED_MFPICT;
break;
case CF_BITMAP:
formatetc.tymed = TYMED_GDI;
break;
default:
formatetc.tymed = TYMED_HGLOBAL;
break;
}
hrErr = lpDataObj->GetData(
(LPFORMATETC)&formatetc,
lpMedium
);
if (hrErr != NOERROR)
return NULL;
if ((hGlobal = lpMedium->hGlobal) == NULL)
return NULL;
// Check if hGlobal really points to valid memory
if ((lp = GlobalLock(hGlobal)) != NULL) {
if (IsBadReadPtr(lp, 1)) {
GlobalUnlock(hGlobal);
return NULL; // ERROR: memory is NOT valid
}
GlobalUnlock(hGlobal);
}
if (hGlobal != NULL && lpMedium->pUnkForRelease != NULL) {
/* OLE2NOTE: the callee wants to retain ownership of the data.
** this is indicated by passing a non-NULL pUnkForRelease.
** thus, we will make a copy of the data and release the
** callee's copy.
*/
hCopy = OleDuplicateData(hGlobal, cfFormat, GHND|GMEM_SHARE);
ReleaseStgMedium(lpMedium); // release callee's copy of data
hGlobal = hCopy;
lpMedium->hGlobal = hCopy;
lpMedium->pUnkForRelease = NULL;
}
return hGlobal;
}
// Mark each entry in the PasteEntryList if its format is available from
// the source IDataObject*. the dwScratchSpace field of each PasteEntry
// is set to TRUE if available, else FALSE.
//
void _IFUNC BOleService::MarkPasteEntries(LPDATAOBJECT dataObj,
LPOLEUIPASTEENTRY priorityList, int cEntries)
{
LPENUMFORMATETC pEnumFmtEtc = NULL;
#define FORMATETC_MAX 64
FORMATETC *rgfmtetc = new FORMATETC[FORMATETC_MAX];
int i;
HRESULT hrErr;
DWORD cFetched;
// Clear all marks
//
for (i = 0; i < cEntries; i++) {
priorityList[i].dwScratchSpace = FALSE;
if (!priorityList[i].fmtetc.cfFormat) {
// Caller wants this item always considered available
// (by specifying a NULL format)
//
priorityList[i].dwScratchSpace = TRUE;
}
else if (priorityList[i].fmtetc.cfFormat == BOleDocument::oleEmbdObjClipFmt
|| priorityList[i].fmtetc.cfFormat == BOleDocument::oleEmbSrcClipFmt) {
// If there is an OLE object format, then handle it
// specially by calling OleQueryCreateFromData. the caller
// need only specify one object type format.
//
#if 0
// This returns NOERROR when QPW drags a graph object which doesn't
// offer any embedding formats. It also DOESN"T CALL BOLESITE!!
// Maybe an OLE2 bug? If we use this function, we'll try to drop
// anyway because we think embedding is available. It's not, so
// the user gets an error.
//
// Workaround: use IDataObject::QueryGetData directly which
// seems to call BOleSite as expected
//
hrErr = OleQueryCreateFromData(dataObj);
#else
FORMATETC fe;
fe.cfFormat = BOleDocument::oleEmbdObjClipFmt;
fe.lindex = -1;
fe.ptd = NULL;
fe.tymed = TYMED_ISTORAGE;
fe.dwAspect = DVASPECT_CONTENT;
hrErr = dataObj->QueryGetData (&fe);
if (hrErr != NOERROR) {
fe.cfFormat = BOleDocument::oleEmbSrcClipFmt;
fe.lindex = -1;
fe.ptd = NULL;
fe.tymed = TYMED_ISTORAGE;
fe.dwAspect = DVASPECT_CONTENT;
hrErr = dataObj->QueryGetData (&fe);
}
#endif
if (NOERROR == hrErr)
priorityList[i].dwScratchSpace = TRUE;
}
else if (priorityList[i].fmtetc.cfFormat == BOleDocument::oleLinkSrcClipFmt) {
// If there is OLE 2.0 LinkSource format, then handle it
// specially by calling OleQueryLinkFromData.
//
hrErr = OleQueryLinkFromData(dataObj);
if(NOERROR == hrErr)
priorityList[i].dwScratchSpace = TRUE;
}
}
hrErr = dataObj->EnumFormatEtc(DATADIR_GET, &pEnumFmtEtc);
if (hrErr != NOERROR)
return; // unable to get format enumerator
// Enumerate the formats offered by the source
//
cFetched = 0;
_fmemset (rgfmtetc,0,sizeof(FORMATETC)*FORMATETC_MAX);
if (pEnumFmtEtc->Next (FORMATETC_MAX, rgfmtetc, &cFetched) == NOERROR ||
(cFetched > 0 && cFetched <= FORMATETC_MAX))
{
for (DWORD j = 0; j < cFetched; j++)
for (i = 0; i < cEntries; i++)
if (!priorityList[i].dwScratchSpace &&
IsEqualFORMATETC(rgfmtetc[j], priorityList[i].fmtetc))
priorityList[i].dwScratchSpace = TRUE;
}
// Clean up
//
if (pEnumFmtEtc)
pEnumFmtEtc->Release();
delete [] rgfmtetc;
}
// Retrieve the first clipboard format in a list for which data
// exists in the source IDataObject*.
//
// Returns -1 if no acceptable match is found.
// index of first acceptable match in the priority list.
//
//
CLIPFORMAT _IFUNC BOleService::MatchPriorityClipFormat(
LPDATAOBJECT dataObj, LPOLEUIPASTEENTRY priorityList, int cEntries)
{
int i;
CLIPFORMAT nFmtEtc = 0xFFFF;
// Mark all entries that the Source provides
//
MarkPasteEntries (dataObj, priorityList, cEntries);
// Loop over the target's priority list of formats
//
for (i = 0; i < cEntries; i++)
{
if ((priorityList[i].dwFlags != OLEUIPASTE_PASTEONLY) &&
!(priorityList[i].dwFlags & OLEUIPASTE_PASTE))
continue;
// get first marked entry
//
if (priorityList[i].dwScratchSpace) {
nFmtEtc = i;
break; // Found priority format; DONE
}
}
return nFmtEtc;
}