windows-nt/Source/XPSP1/NT/windows/richedit/re30/util.cpp
2020-09-26 16:20:57 +08:00

1411 lines
32 KiB
C++

/*
* UTIL.C
*
* Purpose:
* Implementation of various useful utility functions
*
* Author:
* alexgo (4/25/95)
*/
#include "_common.h"
#include "_rtfconv.h"
ASSERTDATA
// Author revision color table
const COLORREF rgcrRevisions[] =
{
RGB(0, 0, 255),
RGB(0, 128, 0),
RGB(255, 0, 0),
RGB(0, 128, 128),
RGB(128, 0, 128),
RGB(0, 0, 128),
RGB(128, 0, 0),
RGB(255, 0, 255)
};
#if REVMASK != 7
#pragma message ("WARNING, Revision mask not equal to table!");
#endif
/*
* DuplicateHGlobal
*
* Purpose:
* duplicates the passed in hglobal
*/
HGLOBAL DuplicateHGlobal( HGLOBAL hglobal )
{
TRACEBEGIN(TRCSUBSYSUTIL, TRCSCOPEINTERN, "DuplicateHGlobal");
UINT flags;
DWORD size;
HGLOBAL hNew;
BYTE * pSrc;
BYTE * pDest;
if( hglobal == NULL )
{
return NULL;
}
flags = GlobalFlags(hglobal);
size = GlobalSize(hglobal);
hNew = GlobalAlloc(flags, size);
if( hNew )
{
pDest = (BYTE *)GlobalLock(hNew);
pSrc = (BYTE *)GlobalLock(hglobal);
if( pDest == NULL || pSrc == NULL )
{
GlobalUnlock(hNew);
GlobalUnlock(hglobal);
GlobalFree(hNew);
return NULL;
}
memcpy(pDest, pSrc, size);
GlobalUnlock(hNew);
GlobalUnlock(hglobal);
}
return hNew;
}
/*
* CountMatchingBits (*pA, *pB, n)
*
* @mfunc
* Count matching bit fields
*
* @comm
* This is used to help decide how good the match is between
* code page bit fields. Mainly for KB/font switching support.
*
* Author:
* Jon Matousek
*/
INT CountMatchingBits(
const DWORD *pA, //@parm Array A to be matched
const DWORD *pB, //@parm Array B to be matched
INT n) //@parm # DWORDs to be matched
{
TRACEBEGIN(TRCSUBSYSUTIL, TRCSCOPEINTERN, "CountMatchingBits");
//0 1 2 3 4 5 6 7 8 9 A B C D E F
static INT bitCount[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
INT c = 0; // Bit count to return
DWORD matchBits; // Next DWORD match
while(n--)
{
//matchBits = ~(*pA++ ^ *pB++); // 1 and 0's
matchBits = *pA++ & *pB++; // 1 only
for( ; matchBits; matchBits >>= 4) // Early out
c += bitCount[matchBits & 15];
}
return c;
}
//
// Object Stabilization classes
//
//+-------------------------------------------------------------------------
//
// Member: CSafeRefCount::CSafeRefCount
//
// Synopsis: constructor for the safe ref count class
//
// Effects:
//
// Arguments: none
//
// Requires:
//
// Returns: none
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 28-Jul-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
CSafeRefCount::CSafeRefCount()
{
m_cRefs = 0;
m_cNest = 0;
m_fInDelete = FALSE;
m_fForceZombie = FALSE;
}
//+-------------------------------------------------------------------------
//
// Member: CSafeRefCount::CSafeRefCount (virtual)
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 28-Jul-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
CSafeRefCount::~CSafeRefCount()
{
Assert(m_cRefs == 0 && m_cNest == 0 && m_fInDelete == TRUE);
}
//+-------------------------------------------------------------------------
//
// Member: CSafeRefCount::SafeAddRef
//
// Synopsis: increments the reference count on the object
//
// Effects:
//
// Arguments: none
//
// Requires:
//
// Returns: ULONG -- the reference count after the increment
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm: increments the reference count.
//
// History: dd-mmm-yy Author Comment
// 28-Jul-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
ULONG CSafeRefCount::SafeAddRef()
{
m_cRefs++;
//AssertSz(m_fInDelete == FALSE, "AddRef called on deleted object!");
// this *could* be really bad. If we are deleting the object,
// it means that during the destructor, somebody made an outgoing
// call eventually ended up with another addref to ourselves
// (even though all pointers to us had been 'Released').
//
// this is usually caused by code like the following:
// m_pFoo->Release();
// m_pFoo = NULL;
//
// If the the Release may cause Foo to be deleted, which may cause
// the object to get re-entered during Foo's destructor. However,
// 'this' object has not yet set m_pFoo to NULL, so it may
// try to continue to use m_pFoo.
//
// However, the May '94 aggregation rules REQUIRE this behaviour
// In your destructor, you have to addref the outer unknown before
// releasing cached interface pointers on your aggregatee. We
// can't put an assert here because we do this all the time now.
//
return m_cRefs;
}
//+-------------------------------------------------------------------------
//
// Member: CSafeRefCount::SafeRelease
//
// Synopsis: decrements the reference count on the object
//
// Effects: May delete the object!
//
// Arguments:
//
// Requires:
//
// Returns: ULONG -- the reference count after decrement
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm: decrements the reference count. If the reference count
// is zero AND the nest count is zero AND we are not currently
// trying to delete our object, then it is safe to delete.
//
// History: dd-mmm-yy Author Comment
// 28-Jul-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
ULONG CSafeRefCount::SafeRelease()
{
ULONG cRefs;
if( m_cRefs > 0 )
{
cRefs = --m_cRefs;
if( m_cRefs == 0 && m_cNest == 0 && m_fInDelete == FALSE )
{
m_fInDelete = TRUE;
delete this;
}
}
else
{
// somebody is releasing a non-addrefed pointer!!
AssertSz(0, "Release called on a non-addref'ed pointer!\n");
cRefs = 0;
}
return cRefs;
}
//+-------------------------------------------------------------------------
//
// Member: CSafeRefCount::IncrementNestCount
//
// Synopsis: increments the nesting count of the object
//
// Effects:
//
// Arguments: none
//
// Requires:
//
// Returns: ULONG; the nesting count after increment
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 28-Jul-94 alexgo author
//
// Notes: The nesting count is the count of how many times an
// an object has been re-entered. For example, suppose
// somebody calls pFoo->Bar1(), which makes some calls that
// eventually call pFoo->Bar2();. On entrace to Bar2, the
// nest count of the object should be 2 (since the invocation
// of Bar1 is still on the stack above us).
//
// It is important to keep track of the nest count so we do
// not accidentally delete ourselves during a nested invocation.
// If we did, then when the stack unwinds to the original
// top level call, it could try to access a non-existent member
// variable and crash.
//
//--------------------------------------------------------------------------
ULONG CSafeRefCount::IncrementNestCount()
{
#ifdef DEBUG
if( m_fInDelete )
{
TRACEWARNSZ("WARNING: CSafeRefCount, object "
"re-entered during delete!\n");
}
#endif
m_cNest++;
return m_cNest;
}
//+-------------------------------------------------------------------------
//
// Member: CSafeRefCount::DecrementNestCount
//
// Synopsis: decrements the nesting count and deletes the object
// (if necessary)
//
// Effects: may delete 'this' object!
//
// Arguments: none
//
// Requires:
//
// Returns: ULONG, the nesting count after decrement
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm: decrements the nesting count. If the nesting count is zero
// AND the reference count is zero AND we are not currently
// trying to delete ourselves, then delete 'this' object
//
// History: dd-mmm-yy Author Comment
// 28-Jul-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
ULONG CSafeRefCount::DecrementNestCount()
{
ULONG cNest;
if( m_cNest > 0 )
{
cNest = --m_cNest;
if( m_cRefs == 0 && m_cNest == 0 && m_fInDelete == FALSE )
{
m_fInDelete = TRUE;
delete this;
}
}
else
{
// somebody forget to increment the nest count!!
AssertSz(0, "Unbalanced nest count!!");
cNest = 0;
}
return cNest;
}
//+-------------------------------------------------------------------------
//
// Member: CSafeRefCount::IsZombie
//
// Synopsis: determines whether or not the object is in a zombie state
// (i.e. all references gone, but we are still on the stack
// somewhere).
//
// Effects:
//
// Arguments: none
//
// Requires:
//
// Returns: TRUE if in a zombie state
// FALSE otherwise
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm: If we are in the middle of a delete, or if the ref count
// is zero and the nest count is greater than zero, then we
// are a zombie
//
// History: dd-mmm-yy Author Comment
// 28-Jul-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL CSafeRefCount::IsZombie()
{
BOOL fIsZombie;
if( (m_cRefs == 0 && m_cNest > 0) || m_fInDelete == TRUE
|| m_fForceZombie == TRUE)
{
fIsZombie = TRUE;
}
else
{
fIsZombie = FALSE;
}
return fIsZombie;
}
//+-------------------------------------------------------------------------
//
// Member: CSafeRefCount::Zombie
//
// Synopsis: Forces the object into a zombie state. This is called
// when the object is still around but shouldn't be. It
// flags us so we behave safely while we are in this state.
//
// Effects:
//
// Arguments: none
//
// Requires:
//
// Returns: none
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History:
//
// Notes:
//
//--------------------------------------------------------------------------
VOID CSafeRefCount::Zombie()
{
m_fForceZombie = TRUE;
}
/* OleStdSwitchDisplayAspect
**
** @mfunc
** Switch the currently cached display aspect between DVASPECT_ICON
** and DVASPECT_CONTENT.
**
** NOTE: when setting up icon aspect, any currently cached content
** cache is discarded and any advise connections for content aspect
** are broken.
**
** @rdesc
** S_OK -- new display aspect setup successfully
** E_INVALIDARG -- IOleCache interface is NOT supported (this is
** required).
** <other SCODE> -- any SCODE that can be returned by
** IOleCache::Cache method.
** NOTE: if an error occurs then the current display aspect and
** cache contents unchanged.
*/
HRESULT OleStdSwitchDisplayAspect(
LPOLEOBJECT lpOleObj,
LPDWORD lpdwCurAspect,
DWORD dwNewAspect,
HGLOBAL hMetaPict,
BOOL fDeleteOldAspect,
BOOL fSetupViewAdvise,
LPADVISESINK lpAdviseSink,
BOOL FAR* lpfMustUpdate)
{
#ifndef PEGASUS
LPOLECACHE lpOleCache = NULL;
LPVIEWOBJECT lpViewObj = NULL;
LPENUMSTATDATA lpEnumStatData = NULL;
STATDATA StatData;
FORMATETC FmtEtc;
STGMEDIUM Medium;
DWORD dwAdvf;
DWORD dwNewConnection;
DWORD dwOldAspect = *lpdwCurAspect;
HRESULT hrErr;
if (lpfMustUpdate)
*lpfMustUpdate = FALSE;
if (hrErr =
lpOleObj->QueryInterface(IID_IOleCache, (void**)&lpOleCache))
{
return hrErr;
}
// Setup new cache with the new aspect
FmtEtc.cfFormat = 0; // whatever is needed to draw
FmtEtc.ptd = NULL;
FmtEtc.dwAspect = dwNewAspect;
FmtEtc.lindex = -1;
FmtEtc.tymed = TYMED_NULL;
/* NOTE: if we are setting up Icon aspect with a custom icon
** then we do not want DataAdvise notifications to ever change
** the contents of the data cache. thus we set up a NODATA
** advise connection. otherwise we set up a standard DataAdvise
** connection.
*/
if (dwNewAspect == DVASPECT_ICON && hMetaPict)
dwAdvf = ADVF_NODATA;
else
dwAdvf = ADVF_PRIMEFIRST;
hrErr = lpOleCache->Cache(
(LPFORMATETC)&FmtEtc,
dwAdvf,
(LPDWORD)&dwNewConnection
);
if (! SUCCEEDED(hrErr)) {
lpOleCache->Release();
return hrErr;
}
*lpdwCurAspect = dwNewAspect;
/* NOTE: if we are setting up Icon aspect with a custom icon,
** then stuff the icon into the cache. otherwise the cache must
** be forced to be updated. set the *lpfMustUpdate flag to tell
** caller to force the object to Run so that the cache will be
** updated.
*/
if (dwNewAspect == DVASPECT_ICON && hMetaPict) {
FmtEtc.cfFormat = CF_METAFILEPICT;
FmtEtc.ptd = NULL;
FmtEtc.dwAspect = DVASPECT_ICON;
FmtEtc.lindex = -1;
FmtEtc.tymed = TYMED_MFPICT;
Medium.tymed = TYMED_MFPICT;
Medium.hGlobal = hMetaPict;
Medium.pUnkForRelease = NULL;
hrErr = lpOleCache->SetData(
(LPFORMATETC)&FmtEtc,
(LPSTGMEDIUM)&Medium,
FALSE /* fRelease */
);
} else {
if (lpfMustUpdate)
*lpfMustUpdate = TRUE;
}
if (fSetupViewAdvise && lpAdviseSink) {
/* NOTE: re-establish the ViewAdvise connection */
lpOleObj->QueryInterface(IID_IViewObject, (void**)&lpViewObj);
if (lpViewObj) {
lpViewObj->SetAdvise(
dwNewAspect,
0,
lpAdviseSink
);
lpViewObj->Release();
}
}
/* NOTE: remove any existing caches that are set up for the old
** display aspect. It WOULD be possible to retain the caches set
** up for the old aspect, but this would increase the storage
** space required for the object and possibly require additional
** overhead to maintain the unused cachaes. For these reasons the
** strategy to delete the previous caches is prefered. if it is a
** requirement to quickly switch between Icon and Content
** display, then it would be better to keep both aspect caches.
*/
if (fDeleteOldAspect) {
hrErr = lpOleCache->EnumCache(
(LPENUMSTATDATA FAR*)&lpEnumStatData
);
while(hrErr == NOERROR) {
hrErr = lpEnumStatData->Next(
1,
(LPSTATDATA)&StatData,
NULL
);
if (hrErr != NOERROR)
break; // DONE! no more caches.
if (StatData.formatetc.dwAspect == dwOldAspect) {
// Remove previous cache with old aspect
lpOleCache->Uncache(StatData.dwConnection);
}
}
if (lpEnumStatData)
lpEnumStatData->Release();
}
if (lpOleCache)
lpOleCache->Release();
#endif
return NOERROR;
}
/*
* ObjectReadSiteFlags
*
* @mfunc
* Read the dwFlags, dwUser, & dvaspect bytes from a container
* specific stream.
*
* Arguments:
* preobj The REOBJ in which to copy the flags.
*
* @rdesc
* HRESULT
*/
HRESULT ObjectReadSiteFlags(REOBJECT * preobj)
{
HRESULT hr = NOERROR;
#ifndef PEGASUS
LPSTREAM pstm = NULL;
OLECHAR StreamName[] = OLESTR("RichEditFlags");
// Make sure we have a storage to read from
if (!preobj->pstg)
return E_INVALIDARG;
// Open the stream
if (hr = preobj->pstg->OpenStream(StreamName, 0, STGM_READ |
STGM_SHARE_EXCLUSIVE, 0, &pstm))
{
goto Cleanup;
}
if ((hr = pstm->Read(&preobj->dwFlags,
sizeof(preobj->dwFlags), NULL)) ||
(hr = pstm->Read(&preobj->dwUser,
sizeof(preobj->dwUser), NULL)) ||
(hr = pstm->Read(&preobj->dvaspect,
sizeof(preobj->dvaspect), NULL)))
{
goto Cleanup;
}
Cleanup:
if (pstm)
pstm->Release();
#endif
return hr;
}
//Used for EnumMetafileCheckIcon & FIsIconMetafilePict
typedef struct _walkmetafile
{
BOOL fAND;
BOOL fPastIcon;
BOOL fHasIcon;
} WALKMETAFILE;
static CHAR szIconOnly[] = "IconOnly";
/*
* EnumMetafileCheckIcon
*
* @mfunc
* Stripped down version of EnumMetafileExtractIcon and
* EnumMetafileExtractIconSource from the OLE2UI library.
*
* EnumMetaFile callback function that walks a metafile looking for
* StretchBlt (3.1) and BitBlt (3.0) records. We expect to see two
* of them, the first being the AND mask and the second being the XOR
* data.
*
* Once we find the icon, we confirm this find by looking for the "IconOnly"
* comment block found in standard OLE iconic metafiles.
*
* Arguments:
* hDC HDC into which the metafile should be played.
* phTable HANDLETABLE FAR * providing handles selected into the DC.
* pMFR METARECORD FAR * giving the enumerated record.
* pIE LPICONEXTRACT providing the destination buffer and length.
*
* @rdesc
* int 0 to stop enumeration, 1 to continue.
*/
int CALLBACK EnumMetafileCheckIcon(HDC hdc, HANDLETABLE *phTable,
METARECORD *pMFR, int cObj,
LPARAM lparam)
{
#ifndef PEGASUS
WALKMETAFILE * pwmf = (WALKMETAFILE *) lparam;
switch (pMFR->rdFunction)
{
case META_DIBBITBLT: // Win30
case META_DIBSTRETCHBLT: // Win31
// If this is the first pass (pIE->fAND==TRUE) then save the memory
// of the AND bits for the next pass.
if (pwmf->fAND)
pwmf->fAND = FALSE;
else
pwmf->fPastIcon = TRUE;
break;
case META_ESCAPE:
if (pwmf->fPastIcon &&
pMFR->rdParm[0] == MFCOMMENT &&
!lstrcmpiA(szIconOnly, (LPSTR)&pMFR->rdParm[2]))
{
pwmf->fHasIcon = TRUE;
return 0;
}
break;
}
#endif
return 1;
}
/*
* FIsIconMetafilePict
*
* @mfunc
* Detect whether the metafile contains an iconic presentation. We do this
* by getting a screen DC and walking the metafile records until we find
* the landmarks denoting an icon.
*
* Arguments:
* hmfp The metafile to test
*
* @rdesc
* BOOL TRUE if the metafile contains an iconic view
*/
BOOL FIsIconMetafilePict(HGLOBAL hmfp)
{
#ifndef PEGASUS
LPMETAFILEPICT pmfp;
WALKMETAFILE wmf = { 0 };
HDC hdc;
wmf.fAND = TRUE;
if (!hmfp || !(pmfp = (LPMETAFILEPICT)GlobalLock(hmfp)))
goto CleanUp;
// We get information back in the ICONEXTRACT structure.
hdc = GetDC(NULL);
EnumMetaFile(hdc, pmfp->hMF, EnumMetafileCheckIcon, (LPARAM) &wmf);
ReleaseDC(NULL, hdc);
GlobalUnlock(hmfp);
CleanUp:
return wmf.fHasIcon;
#else
return TRUE;
#endif
}
/*
* AllocObjectDescriptor
*
* Purpose:
* Allocated and fills an OBJECTDESCRIPTOR structure.
*
* Parameters:
* clsID CLSID to store.
* dwAspect DWORD with the display aspect
* pszl LPSIZEL (optional) if the object is being scaled in
* its container, then the container should pass the
* extents that it is using to display the object.
* ptl POINTL from upper-left corner of object where
* mouse went down for use with Drag & Drop.
* dwMisc DWORD containing MiscStatus flags
* pszName LPTSTR naming the object to copy
* pszSrc LPTSTR identifying the source of the object.
*
* Return Value:
* HBGLOBAL Handle to OBJECTDESCRIPTOR structure.
*/
/*
* AllocObjectDescriptor
*
* Purpose:
* Allocated and fills an OBJECTDESCRIPTOR structure.
*
* Parameters:
* clsID CLSID to store.
* dwAspect DWORD with the display aspect
* pszl LPSIZEL (optional) if the object is being scaled in
* its container, then the container should pass the
* extents that it is using to display the object.
* ptl POINTL from upper-left corner of object where
* mouse went down for use with Drag & Drop.
* dwMisc DWORD containing MiscStatus flags
* pszName LPTSTR naming the object to copy
* pszSrc LPTSTR identifying the source of the object.
*
* Return Value:
* HBGLOBAL Handle to OBJECTDESCRIPTOR structure.
*/
static HGLOBAL AllocObjectDescriptor(
CLSID clsID,
DWORD dwAspect,
SIZEL szl,
POINTL ptl,
DWORD dwMisc,
LPTSTR pszName,
LPTSTR pszSrc)
{
#ifndef PEGASUS
HGLOBAL hMem=NULL;
LPOBJECTDESCRIPTOR pOD;
DWORD cb, cbStruct;
DWORD cchName, cchSrc;
cchName=wcslen(pszName)+1;
if (NULL!=pszSrc)
cchSrc=wcslen(pszSrc)+1;
else
{
cchSrc=cchName;
pszSrc=pszName;
}
/*
* Note: CFSTR_OBJECTDESCRIPTOR is an ANSI structure.
* That means strings in it must be ANSI. OLE will do
* internal conversions back to Unicode as necessary,
* but we have to put ANSI strings in it ourselves.
*/
cbStruct=sizeof(OBJECTDESCRIPTOR);
cb=cbStruct+(sizeof(WCHAR)*(cchName+cchSrc)); //HACK
hMem=GlobalAlloc(GHND, cb);
if (NULL==hMem)
return NULL;
pOD=(LPOBJECTDESCRIPTOR)GlobalLock(hMem);
pOD->cbSize=cb;
pOD->clsid=clsID;
pOD->dwDrawAspect=dwAspect;
pOD->sizel=szl;
pOD->pointl=ptl;
pOD->dwStatus=dwMisc;
if (pszName)
{
pOD->dwFullUserTypeName=cbStruct;
wcscpy((LPTSTR)((LPBYTE)pOD+pOD->dwFullUserTypeName)
, pszName);
}
else
pOD->dwFullUserTypeName=0; //No string
if (pszSrc)
{
pOD->dwSrcOfCopy=cbStruct+(cchName*sizeof(WCHAR));
wcscpy((LPTSTR)((LPBYTE)pOD+pOD->dwSrcOfCopy), pszSrc);
}
else
pOD->dwSrcOfCopy=0; //No string
GlobalUnlock(hMem);
return hMem;
#else
return NULL;
#endif
}
HGLOBAL OleGetObjectDescriptorDataFromOleObject(
LPOLEOBJECT pObj,
DWORD dwAspect,
POINTL ptl,
LPSIZEL pszl
)
{
#ifndef PEGASUS
CLSID clsID;
LPTSTR pszName=NULL;
LPTSTR pszSrc=NULL;
BOOL fLink=FALSE;
IOleLink *pLink;
TCHAR szName[512];
DWORD dwMisc=0;
SIZEL szl = {0,0};
HGLOBAL hMem;
HRESULT hr;
if (SUCCEEDED(pObj->QueryInterface(IID_IOleLink
, (void **)&pLink)))
fLink=TRUE;
if (FAILED(pObj->GetUserClassID(&clsID)))
ZeroMemory(&clsID, sizeof(CLSID));
//Get user string, expand to "Linked %s" if this is link
pObj->GetUserType(USERCLASSTYPE_FULL, &pszName);
if (fLink && NULL!=pszName)
{
// NB!! we do these two lines of code below instead
// wcscat because we don't use wcscat anywhere else
// in the product at the moment. The string "Linked "
// should never change either.
wcscpy(szName, TEXT("Linked "));
wcscpy(&(szName[7]), pszName);
}
else if (pszName)
wcscpy(szName, pszName);
else
szName[0] = 0;
CoTaskMemFree(pszName);
/*
* Get the source name of this object using either the
* link display name (for link) or a moniker display
* name.
*/
if (fLink)
{
hr=pLink->GetSourceDisplayName(&pszSrc);
}
else
{
IMoniker *pmk;
hr=pObj->GetMoniker(OLEGETMONIKER_TEMPFORUSER
, OLEWHICHMK_OBJFULL, &pmk);
if (SUCCEEDED(hr))
{
IBindCtx *pbc;
CreateBindCtx(0, &pbc);
pmk->GetDisplayName(pbc, NULL, &pszSrc);
pbc->Release();
pmk->Release();
}
}
if (fLink)
pLink->Release();
//Get MiscStatus bits
hr=pObj->GetMiscStatus(dwAspect, &dwMisc);
if (pszl)
{
szl.cx = pszl->cx;
szl.cy = pszl->cy;
}
//Get OBJECTDESCRIPTOR
hMem=AllocObjectDescriptor(clsID, dwAspect, szl, ptl, dwMisc, szName, pszSrc);
CoTaskMemFree(pszSrc);
return hMem;
#else
return NULL;
#endif
}
/*
* OleStdGetMetafilePictFromOleObject()
*
* @mfunc:
* Generate a MetafilePict from the OLE object.
* Parameters:
* lpOleObj LPOLEOBJECT pointer to OLE Object
* dwDrawAspect DWORD Display Aspect of object
* lpSizelHim SIZEL (optional) If the object is being scaled in its
* container, then the container should pass the extents
* that it is using to display the object.
* May be NULL if the object is NOT being scaled. in this
* case, IViewObject2::GetExtent will be called to get the
* extents from the object.
* ptd TARGETDEVICE FAR* (optional) target device to render
* metafile for. May be NULL.
*
* @rdesc
* HANDLE -- handle of allocated METAFILEPICT
*/
HANDLE OleStdGetMetafilePictFromOleObject(
LPOLEOBJECT lpOleObj,
DWORD dwDrawAspect,
LPSIZEL lpSizelHim,
DVTARGETDEVICE FAR* ptd
)
{
#ifndef PEGASUS
LPVIEWOBJECT2 lpViewObj2 = NULL;
HDC hDC;
HMETAFILE hmf;
HANDLE hMetaPict;
LPMETAFILEPICT lpPict;
RECT rcHim;
RECTL rclHim;
SIZEL sizelHim;
HRESULT hrErr;
SIZE size;
POINT point;
LPOLECACHE polecache = NULL;
LPDATAOBJECT pdataobj = NULL;
FORMATETC fetc;
STGMEDIUM med;
// First try the easy way,
// pull out the cache's version of things.
ZeroMemory(&fetc, sizeof(FORMATETC));
fetc.dwAspect = dwDrawAspect;
fetc.cfFormat = CF_METAFILEPICT;
fetc.lindex = -1;
fetc.tymed = TYMED_MFPICT;
ZeroMemory(&med, sizeof(STGMEDIUM));
hMetaPict = NULL;
if (!lpOleObj->QueryInterface(IID_IOleCache, (void **)&polecache) &&
!polecache->QueryInterface(IID_IDataObject, (void **)&pdataobj) &&
!pdataobj->GetData(&fetc, &med))
{
hMetaPict = OleDuplicateData(med.hGlobal, CF_METAFILEPICT, 0);
ReleaseStgMedium(&med);
}
if (pdataobj)
{
pdataobj->Release();
}
if (polecache)
{
polecache->Release();
}
// If all this failed, fall back to the hard way and draw the object
// into a metafile.
if (hMetaPict)
return hMetaPict;
if (lpOleObj->QueryInterface(IID_IViewObject2, (void **)&lpViewObj2))
return NULL;
// Get SIZEL
if (lpSizelHim) {
// Use extents passed by the caller
sizelHim = *lpSizelHim;
} else {
// Get the current extents from the object
hrErr = lpViewObj2->GetExtent(
dwDrawAspect,
-1, /*lindex*/
ptd, /*ptd*/
(LPSIZEL)&sizelHim);
if (hrErr != NOERROR)
sizelHim.cx = sizelHim.cy = 0;
}
hDC = CreateMetaFileA(NULL);
rclHim.left = 0;
rclHim.top = 0;
rclHim.right = sizelHim.cx;
rclHim.bottom = sizelHim.cy;
rcHim.left = (int)rclHim.left;
rcHim.top = (int)rclHim.top;
rcHim.right = (int)rclHim.right;
rcHim.bottom = (int)rclHim.bottom;
SetWindowOrgEx(hDC, rcHim.left, rcHim.top, &point);
SetWindowExtEx(hDC, rcHim.right-rcHim.left, rcHim.bottom-rcHim.top,&size);
hrErr = lpViewObj2->Draw(
dwDrawAspect,
-1,
NULL,
ptd,
NULL,
hDC,
(LPRECTL)&rclHim,
(LPRECTL)&rclHim,
NULL,
0
);
lpViewObj2->Release();
hmf = CloseMetaFile(hDC);
if (hrErr != NOERROR) {
TRACEERRORHR(hrErr);
hMetaPict = NULL;
}
else
{
hMetaPict = GlobalAlloc(GHND|GMEM_SHARE, sizeof(METAFILEPICT));
if (hMetaPict && (lpPict = (LPMETAFILEPICT)GlobalLock(hMetaPict))){
lpPict->hMF = hmf;
lpPict->xExt = (int)sizelHim.cx ;
lpPict->yExt = (int)sizelHim.cy ;
lpPict->mm = MM_ANISOTROPIC;
GlobalUnlock(hMetaPict);
}
}
if (!hMetaPict)
DeleteMetaFile(hmf);
return hMetaPict;
#else
return NULL;
#endif
}
/*
* OleUIDrawShading
*
* Purpose:
* Shade the object when it is in in-place editing. Borders are drawn
* on the Object rectangle. The right and bottom edge of the rectangle
* are excluded in the drawing.
*
* Parameters:
* lpRect Dimensions of Container Object
* hdc HDC for drawing
*
* Return Value: null
*
*/
void OleUIDrawShading(LPRECT lpRect, HDC hdc)
{
#ifndef PEGASUS
HBRUSH hbr;
HBRUSH hbrOld;
HBITMAP hbm;
RECT rc;
WORD wHatchBmp[] = {0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88};
COLORREF cvText;
COLORREF cvBk;
hbm = CreateBitmap(8, 8, 1, 1, wHatchBmp);
hbr = CreatePatternBrush(hbm);
hbrOld = (HBRUSH)SelectObject(hdc, hbr);
rc = *lpRect;
cvText = SetTextColor(hdc, RGB(255, 255, 255));
cvBk = SetBkColor(hdc, RGB(0, 0, 0));
PatBlt(hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top,
0x00A000C9L /* DPa */ );
SetTextColor(hdc, cvText);
SetBkColor(hdc, cvBk);
SelectObject(hdc, hbrOld);
DeleteObject(hbr);
DeleteObject(hbm);
#endif
}
/*
* OleSaveSiteFlags
*
* Purpose:
* Save the dwFlags and dwUser bytes into a container specific stream
*
* Arguments:
* pstg The storage to save to
* pobsite The site from where to copy the flags
*
* Returns:
* None.
*/
VOID OleSaveSiteFlags(LPSTORAGE pstg, DWORD dwFlags, DWORD dwUser, DWORD dvAspect)
{
#ifndef PEGASUS
HRESULT hr;
LPSTREAM pstm = NULL;
static const OLECHAR szSiteFlagsStm[] = OLESTR("RichEditFlags");
TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "OleSaveSiteFlags");
// Create/overwrite the stream
AssertSz(pstg, "Invalid storage");
if (hr = pstg->CreateStream(szSiteFlagsStm, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
0, 0, &pstm))
{
TraceError("OleSaveSiteFlags", GetScode(hr));
goto Cleanup;
}
//$ FUTURE: Put a version stamp
// Write out the values
//$ BUG: byte order
if ((hr = pstm->Write(&dwFlags, sizeof(dwFlags), NULL)) ||
(hr = pstm->Write(&dwUser, sizeof(dwUser), NULL)) ||
(hr = pstm->Write(&dvAspect, sizeof(dvAspect), NULL)))
{
TraceError("OleSaveSiteFlags", GetScode(hr));
//$ FUTURE: Wipe the data to make this operation all or nothing
goto Cleanup;
}
Cleanup:
if (pstm)
pstm->Release();
#endif
}
/*
* AppendString ( szInput, szAppendStr, dBuffSize, dByteUsed )
*
* Purpose:
* Append new string to original string. Check for size of buffer
* and re-allocate a large buffer is necessary
*
* Arguments:
* szInput Original String
* szAppendStr String to be appended to szInput
* dBuffSize Byte size of the buffer for szInput
* dByteUsed Byte used in the buffer for szInput
*
* Returns:
* INT The error code
*/
INT AppendString(
BYTE ** szInput,
BYTE * szAppendStr,
int * dBuffSize,
int * dByteUsed)
{
BYTE *pch;
int cchAppendStr;
pch = *szInput;
// check if we have enough space to append the new string
cchAppendStr = strlen( (char *)szAppendStr );
if ( cchAppendStr + *dByteUsed >= *dBuffSize )
{
// re-alloc a bigger buffer
int cchNewSize = *dBuffSize + cchAppendStr + 32;
pch = (BYTE *)PvReAlloc( *szInput, cchNewSize );
if ( !pch )
{
return ( ecNoMemory );
}
*dBuffSize = cchNewSize;
*szInput = pch;
}
pch += *dByteUsed;
*dByteUsed += cchAppendStr;
while (*pch++ = *szAppendStr++);
return ecNoError;
}
/*
* CTempBuf::Init
*
* @mfunc Set object to its initial state using the stack buffer
*
*/
void CTempBuf::Init()
{
_pv = (void *) &_chBuf[0];
_cb = MAX_STACK_BUF;
}
/*
* CTempBuf::FreeBuf
*
* @mfunc Free an allocated buffer if there is one
*
*/
void CTempBuf::FreeBuf()
{
if (_pv != &_chBuf[0])
{
delete _pv;
}
}
/*
* CTempBuf::GetBuf
*
* @mfunc Get a buffer for temporary use
*
* @rdesc Pointer to buffer if one could be allocated otherwise NULL.
*
*
*/
void *CTempBuf::GetBuf(
LONG cb) //@parm Size of buffer needed in bytes
{
if (_cb >= cb)
{
// Currently allocated buffer is big enough so use it
return _pv;
}
// Free our current buffer
FreeBuf();
// Allocate a new buffer if we can
_pv = new BYTE[cb];
if (NULL == _pv)
{
// Could not allocate a buffer so reset to our initial state and
// return NULL.
Init();
return NULL;
}
// Store the size of our new buffer.
_cb = cb;
// Returnt he pointer to the buffer.
return _pv;
}