599 lines
14 KiB
C++
599 lines
14 KiB
C++
/*
|
|
* @doc INTERNAL
|
|
*
|
|
* @module object.cpp IRichEditOle implementation |
|
|
*
|
|
* Author: alexgo 8/15/95
|
|
*
|
|
* Copyright (c) 1995-2000, Microsoft Corporation. All rights reserved.
|
|
*/
|
|
|
|
#include "_common.h"
|
|
#include "_edit.h"
|
|
#include "_objmgr.h"
|
|
#include "_coleobj.h"
|
|
#include "_rtext.h"
|
|
#include "_select.h"
|
|
#include "_m_undo.h"
|
|
#include "_disp.h"
|
|
|
|
|
|
// IUnknown is implemented elsewhere
|
|
|
|
/*
|
|
* CTxtEdit::GetClientSite (lplpolesite)
|
|
*
|
|
* @mfunc returns the client site
|
|
*/
|
|
STDMETHODIMP CTxtEdit::GetClientSite(
|
|
LPOLECLIENTSITE FAR * lplpolesite) //@parm where to return
|
|
//the client site
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::GetClientSite");
|
|
|
|
if(!lplpolesite)
|
|
return E_INVALIDARG;
|
|
|
|
COleObject *pobj = new COleObject(this);
|
|
// should start with a ref count of 1.
|
|
if(pobj)
|
|
{
|
|
*lplpolesite = (IOleClientSite *)pobj;
|
|
return NOERROR;
|
|
}
|
|
*lplpolesite = NULL;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::GetObjectCount
|
|
*
|
|
* @mfunc return the number of objects in this edit instance
|
|
*/
|
|
STDMETHODIMP_(LONG) CTxtEdit::GetObjectCount()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::GetObjectCount");
|
|
|
|
return _pobjmgr ? _pobjmgr->GetObjectCount() : 0;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::GetLinkCount
|
|
*
|
|
* @mfunc return the number of likns in this edit instance
|
|
*/
|
|
STDMETHODIMP_(LONG) CTxtEdit::GetLinkCount()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::GetLinkCount");
|
|
|
|
CObjectMgr *pobjmgr = GetObjectMgr();
|
|
return pobjmgr ? pobjmgr->GetLinkCount() : 0;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::GetObject(iob, preobj, dwFlags)
|
|
*
|
|
* @mfunc returns an object structure for the indicated object
|
|
*/
|
|
STDMETHODIMP CTxtEdit::GetObject(
|
|
LONG iob, //@parm index of the object
|
|
REOBJECT * preobj, //@parm where to put object info
|
|
DWORD dwFlags) //@parm flags
|
|
{
|
|
COleObject *pobj = NULL;
|
|
CCallMgr callmgr(this);
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::GetObject");
|
|
if(!preobj || preobj->cbStruct != sizeof(REOBJECT))
|
|
return E_INVALIDARG;
|
|
|
|
CObjectMgr *pobjmgr = GetObjectMgr();
|
|
if(!pobjmgr)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// There are three cases of intestest; get the object at
|
|
// an index, at a given cp, or at the selection.
|
|
|
|
if(iob == REO_IOB_USE_CP || iob == REO_IOB_SELECTION)
|
|
{
|
|
if((Get10Mode() && preobj->cp == REO_CP_SELECTION) || iob == REO_IOB_SELECTION)
|
|
{
|
|
// Use selection cp
|
|
CTxtSelection *psel = GetSel();
|
|
if(psel)
|
|
pobj = pobjmgr->GetObjectFromCp(psel->GetCpMin());
|
|
}
|
|
else
|
|
pobj = pobjmgr->GetObjectFromCp(Get10Mode() ? GetCpFromAcp(preobj->cp): preobj->cp);
|
|
}
|
|
else if (iob >= 0)
|
|
pobj = pobjmgr->GetObjectFromIndex(iob);
|
|
|
|
if(pobj)
|
|
{
|
|
HRESULT hResult = pobj->GetObjectData(preobj, dwFlags);
|
|
|
|
if (Get10Mode())
|
|
preobj->cp = GetAcpFromCp(preobj->cp);
|
|
|
|
return hResult;
|
|
}
|
|
|
|
// This return code is a bit of stretch, but basially
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::InsertObject (preobj)
|
|
*
|
|
* @mfunc inserts a new object
|
|
*
|
|
* @rdesc
|
|
* HRESULT
|
|
*/
|
|
STDMETHODIMP CTxtEdit::InsertObject(
|
|
REOBJECT * preobj) //@parm object info
|
|
{
|
|
CCallMgr callmgr(this);
|
|
CTxtRange rg(this, 0);
|
|
IUndoBuilder * publdr;
|
|
CGenUndoBuilder undobldr(this, UB_AUTOCOMMIT, &publdr);
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::InsertObject");
|
|
|
|
// Do some boundary case checking
|
|
|
|
if(!preobj)
|
|
return E_INVALIDARG;
|
|
|
|
CTxtSelection *psel = GetSel();
|
|
if(!psel)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// The following code gives Outlook major fits if you click on a name in
|
|
// the To:, Cc:, or Bcc: fields, so I've commented it out. In principle,
|
|
// Outlook's EN_PROTECTION handler should say it's OK to insert in this case.
|
|
//
|
|
// if(!IsntProtectedOrReadOnly(0, 0, 0))
|
|
// return E_ACCESSDENIED;
|
|
|
|
// If the insertion of this character would cause
|
|
// us to exceed the text limit, fail
|
|
if((DWORD)(GetAdjustedTextLength() + 1) > TxGetMaxLength())
|
|
{
|
|
// If we're not replacing a selection (or the
|
|
// selection is degenerate, then we will have exceeded
|
|
// our limit
|
|
if(preobj->cp != REO_CP_SELECTION || psel->GetCch() == 0)
|
|
{
|
|
GetCallMgr()->SetMaxText();
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
CObjectMgr *pobjmgr = GetObjectMgr();
|
|
if(pobjmgr)
|
|
{
|
|
LONG cch = 0;
|
|
LONG cp;
|
|
|
|
undobldr.StopGroupTyping();
|
|
|
|
if(preobj->cp == REO_CP_SELECTION)
|
|
{
|
|
LONG cpFormat;
|
|
LONG cpMost;
|
|
psel->AdjustEndEOP(NEWCHARS);
|
|
cch = psel->GetRange(cp, cpMost);
|
|
|
|
// Get cp of active end of selection from which we
|
|
// will obtain CF for object.
|
|
cpFormat = psel->GetCp();
|
|
if(publdr)
|
|
HandleSelectionAEInfo(this, publdr, cpFormat, cch,
|
|
cp + 1, 0, SELAE_FORCEREPLACE);
|
|
|
|
// Get format for ReplaceRange: for cp semantics, use format
|
|
// at the cp; for selection semantics, use the format at the
|
|
// active end of the selection.
|
|
rg.SetCp(cpFormat, FALSE);
|
|
LONG iFormat = rg.Get_iCF();
|
|
rg.Set(cp, -cch);
|
|
rg.Set_iCF(iFormat); // Use _iFormat at sel active end
|
|
rg.SetUseiFormat(TRUE);
|
|
ReleaseFormats(iFormat, -1);
|
|
}
|
|
else
|
|
{
|
|
cp = Get10Mode() ? GetCpFromAcp(preobj->cp): preobj->cp;
|
|
rg.SetCp(cp, FALSE); // Updates rg._iFormat
|
|
}
|
|
|
|
if(preobj->dwFlags & REO_USEASBACKGROUND)
|
|
{
|
|
CDocInfo *pDocInfo = GetDocInfo();
|
|
if(pDocInfo)
|
|
pDocInfo->InitBackground();
|
|
}
|
|
HRESULT hr = pobjmgr->InsertObject(&rg, preobj, publdr);
|
|
CNotifyMgr *pnm = GetNotifyMgr(); // Get notification mgr
|
|
if(pnm) // Notify interested parties
|
|
pnm->NotifyPostReplaceRange(NULL, CP_INFINITE, 0, 0, cp, cp + 1);
|
|
|
|
// Don't want object selected
|
|
psel->SetSelection(cp + 1, cp + 1);
|
|
if(preobj->dwFlags & REO_USEASBACKGROUND)
|
|
_pdp->UpdateView();
|
|
|
|
TxUpdateWindow();
|
|
return hr;
|
|
}
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::ConvertObject(iob, rclsidNew, lpstrUserTypeNew)
|
|
*
|
|
* @mfunc Converts the specified object to the specified class. Does reload
|
|
* the object but does NOT force an update (caller must do this).
|
|
*
|
|
* @rdesc
|
|
* HRESULT Success code.
|
|
*/
|
|
STDMETHODIMP CTxtEdit::ConvertObject(
|
|
LONG iob, //@parm index of the object
|
|
REFCLSID rclsidNew, //@parm the destination clsid
|
|
LPCSTR lpstrUserTypeNew) //@parm the new user type name
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::ConvertObject");
|
|
CCallMgr callmgr(this);
|
|
|
|
// If iob was invalid return
|
|
COleObject * pobj = ObjectFromIOB(iob);
|
|
if(!pobj)
|
|
return E_INVALIDARG;
|
|
|
|
//Delegate to the object.
|
|
return pobj->Convert(rclsidNew, lpstrUserTypeNew);
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::ActivateAs(rclsid, rclsidAs)
|
|
*
|
|
* @mfunc Handles a request by the user to activate all objects of a
|
|
* particular class as objects of another class.
|
|
*
|
|
* @rdesc
|
|
* HRESULT Success code.
|
|
*/
|
|
STDMETHODIMP CTxtEdit::ActivateAs(
|
|
REFCLSID rclsid, //@parm clsid which we're going to change
|
|
REFCLSID rclsidAs) //@parm clsid to activate as
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::ActivateAs");
|
|
CCallMgr callmgr(this);
|
|
|
|
CObjectMgr * pobjmgr = GetObjectMgr();
|
|
if(!pobjmgr)
|
|
return E_OUTOFMEMORY;
|
|
|
|
return pobjmgr->ActivateObjectsAs(rclsid, rclsidAs);
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::SetHostNames(lpstrContainerApp, lpstrContainerDoc)
|
|
*
|
|
* @mfunc Sets the host names for this instance
|
|
*/
|
|
STDMETHODIMP CTxtEdit::SetHostNames(
|
|
LPCSTR lpstrContainerApp, //@parm App name
|
|
LPCSTR lpstrContainerDoc) //@parm Container Object (doc) name
|
|
{
|
|
CCallMgr callmgr(this);
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::SetHostNames");
|
|
|
|
WCHAR *pwsContainerApp = W32->ConvertToWideChar(lpstrContainerApp);
|
|
WCHAR *pwsContainerDoc = W32->ConvertToWideChar(lpstrContainerDoc);
|
|
|
|
CObjectMgr *pobjmgr = GetObjectMgr();
|
|
if(pobjmgr && pwsContainerApp && pwsContainerDoc)
|
|
{
|
|
HRESULT hr = pobjmgr->SetHostNames(pwsContainerApp, pwsContainerDoc);
|
|
delete pwsContainerApp;
|
|
delete pwsContainerDoc;
|
|
return hr;
|
|
}
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::SetLinkAvailable(iob, fAvailable)
|
|
*
|
|
* @mfunc
|
|
* Allows client to tell us whether the link is available or not.
|
|
*/
|
|
STDMETHODIMP CTxtEdit::SetLinkAvailable(
|
|
LONG iob, //@parm index of the object
|
|
BOOL fAvailable) //@parm if TRUE, make object linkable
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::SetLinkAvailable");
|
|
|
|
COleObject * pobj = ObjectFromIOB(iob);
|
|
|
|
// If iob was invalid, return
|
|
if (!pobj)
|
|
return E_INVALIDARG;
|
|
|
|
// Delegate this to the object.
|
|
return pobj->SetLinkAvailable(fAvailable);
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::SetDvaspect(iob, dvaspect)
|
|
*
|
|
* @mfunc Allows client to tell us which aspect to use and force us
|
|
* to recompute positioning and redraw.
|
|
*
|
|
* @rdesc
|
|
* HRESULT Success code.
|
|
*/
|
|
STDMETHODIMP CTxtEdit::SetDvaspect(
|
|
LONG iob, //@parm index of the object
|
|
DWORD dvaspect) //@parm the aspect to use
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::SetDvaspect");
|
|
CCallMgr callmgr(this);
|
|
COleObject * pobj = ObjectFromIOB(iob);
|
|
|
|
// If iob was invalid, return
|
|
if (!pobj)
|
|
return E_INVALIDARG;
|
|
|
|
// Delegate this to the object.
|
|
pobj->SetDvaspect(dvaspect);
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::HandsOffStorage(iob)
|
|
*
|
|
* @mfunc see IPersistStorage::HandsOffStorage
|
|
*
|
|
* @rdesc
|
|
* HRESULT Success code.
|
|
*/
|
|
STDMETHODIMP CTxtEdit::HandsOffStorage(
|
|
LONG iob) //@parm index of the object
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::HandsOffStorage");
|
|
CCallMgr callmgr(this);
|
|
|
|
COleObject * pobj = ObjectFromIOB(iob);
|
|
|
|
// If iob was invalid, return
|
|
if (!pobj)
|
|
return E_INVALIDARG;
|
|
|
|
// Delegate this to the object.
|
|
pobj->HandsOffStorage();
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::SaveCompleted(iob, lpstg)
|
|
*
|
|
* @mfunc see IPersistStorage::SaveCompleted
|
|
*
|
|
* @rdesc
|
|
* HRESULT Success code.
|
|
*/
|
|
STDMETHODIMP CTxtEdit::SaveCompleted(
|
|
LONG iob, //@parm index of the object
|
|
LPSTORAGE lpstg) //@parm new storage
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::SaveCompleted");
|
|
CCallMgr callmgr(this);
|
|
|
|
COleObject * pobj = ObjectFromIOB(iob);
|
|
|
|
// If iob was invalid, return
|
|
if (!pobj)
|
|
return E_INVALIDARG;
|
|
|
|
// Delegate this to the object.
|
|
pobj->SaveCompleted(lpstg);
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::InPlaceDeactivate()
|
|
*
|
|
* @mfunc Deactivate
|
|
*/
|
|
STDMETHODIMP CTxtEdit::InPlaceDeactivate()
|
|
{
|
|
COleObject *pobj;
|
|
HRESULT hr = NOERROR;
|
|
CCallMgr callmgr(this);
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::InPlaceDeactivate");
|
|
|
|
CObjectMgr *pobjmgr = GetObjectMgr();
|
|
if(pobjmgr)
|
|
{
|
|
pobj = pobjmgr->GetInPlaceActiveObject();
|
|
if(pobj)
|
|
hr = pobj->DeActivateObj();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::ContextSensitiveHelp(fEnterMode)
|
|
*
|
|
* @mfunc enter/leave ContextSensitiveHelp mode
|
|
*
|
|
* @rdesc
|
|
* HRESULT Success code.
|
|
*/
|
|
STDMETHODIMP CTxtEdit::ContextSensitiveHelp(
|
|
BOOL fEnterMode) //@parm enter/exit mode
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::ContextSensitiveHelp");
|
|
|
|
HRESULT hr = NOERROR;
|
|
CCallMgr callmgr(this);
|
|
|
|
CObjectMgr * pobjmgr = GetObjectMgr();
|
|
if(!pobjmgr)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// If the mode changes
|
|
if(pobjmgr->GetHelpMode() != fEnterMode)
|
|
{
|
|
pobjmgr->SetHelpMode(fEnterMode);
|
|
COleObject * pobj = pobjmgr->GetInPlaceActiveObject();
|
|
if(pobj)
|
|
{
|
|
IOleWindow *pow;
|
|
hr = pobj->GetIUnknown()->QueryInterface(IID_IOleWindow,
|
|
(void **)&pow);
|
|
if(hr == NOERROR)
|
|
{
|
|
hr = pow->ContextSensitiveHelp(fEnterMode);
|
|
pow->Release();
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::GetClipboardData(lpchrg, reco, lplpdataobj)
|
|
*
|
|
* @mfunc return an data transfer object for the indicated
|
|
* range
|
|
*
|
|
* @rdesc
|
|
* HRESULT Success code.
|
|
*/
|
|
STDMETHODIMP CTxtEdit::GetClipboardData(
|
|
CHARRANGE *lpchrg, //@parm the range of text to use
|
|
DWORD reco, //@parm operation the data is for
|
|
LPDATAOBJECT *lplpdataobj) //@parm where to put the data object
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::GetClipboardData");
|
|
|
|
CCallMgr callmgr(this);
|
|
HRESULT hr;
|
|
LONG cpMin, cpMost;
|
|
CLightDTEngine * pldte = GetDTE();
|
|
|
|
//Make sure cpMin and cpMost are within the current text limits.
|
|
//Interpret neg. value for cpMin as the beginning of the text,
|
|
//and neg. value for cpMax as the end of the text. If a char range
|
|
//is not given use the current selection.
|
|
if(lpchrg)
|
|
{
|
|
LONG cchText = GetTextLength();
|
|
cpMin = max(0, lpchrg->cpMin);
|
|
cpMin = min(cchText, lpchrg->cpMin);
|
|
cpMost = lpchrg->cpMost;
|
|
if(lpchrg->cpMost < 0 || lpchrg->cpMost > cchText)
|
|
cpMost = cchText;
|
|
}
|
|
else
|
|
{
|
|
CTxtSelection * psel = GetSel();
|
|
psel->GetRange(cpMin, cpMost);
|
|
}
|
|
|
|
//Make sure this is a valid range.
|
|
if(cpMin >= cpMost)
|
|
{
|
|
*lplpdataobj = NULL;
|
|
return cpMin == cpMost
|
|
? NOERROR
|
|
: ResultFromScode(E_INVALIDARG);
|
|
}
|
|
|
|
CTxtRange rg(this, cpMin, cpMin-cpMost);
|
|
|
|
//We don't use reco for anything.
|
|
hr = pldte->RangeToDataObject(&rg, SF_RTF, lplpdataobj);
|
|
|
|
#if !defined(NOFULLDEBUG) && defined(DEBUG)
|
|
if(hr != NOERROR)
|
|
TRACEERRSZSC("GetClipboardData", E_OUTOFMEMORY);
|
|
#endif
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::ImportDataObject(lpdataobj, cf, hMetaPict)
|
|
*
|
|
* @mfunc morally equivalent to paste, but with a data object
|
|
*
|
|
* @rdesc
|
|
* HRESULT Success code.
|
|
*/
|
|
STDMETHODIMP CTxtEdit::ImportDataObject(
|
|
LPDATAOBJECT lpdataobj, //@parm Data object to use
|
|
CLIPFORMAT cf, //@parm Clibpoard format to use
|
|
HGLOBAL hMetaPict) //@parm Metafile to use
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "CTxtEdit::ImportDataObject");
|
|
|
|
CCallMgr callmgr(this);
|
|
IUndoBuilder * publdr;
|
|
REPASTESPECIAL rps = {DVASPECT_CONTENT, NULL};
|
|
CGenUndoBuilder undobldr(this, UB_AUTOCOMMIT, &publdr);
|
|
|
|
if(hMetaPict)
|
|
{
|
|
rps.dwAspect = DVASPECT_ICON;
|
|
rps.dwParam = (DWORD_PTR) hMetaPict;
|
|
}
|
|
|
|
return PasteDataObjectToRange(lpdataobj, GetSel(), cf,
|
|
&rps, publdr, PDOR_NOQUERY);
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::ObjectFromIOB(iob)
|
|
*
|
|
* @mfunc Gets an object based on an IOB type index.
|
|
*
|
|
* @rdesc:
|
|
* pointer to COleObject or NULL if none.
|
|
*/
|
|
COleObject * CTxtEdit::ObjectFromIOB(
|
|
LONG iob)
|
|
{
|
|
CObjectMgr * pobjmgr = GetObjectMgr();
|
|
if(!pobjmgr)
|
|
return NULL;
|
|
|
|
COleObject * pobj = NULL;
|
|
|
|
// Figure out the index of the selection
|
|
if (iob == REO_IOB_SELECTION)
|
|
{
|
|
CTxtSelection * psel = GetSel();
|
|
|
|
pobj = pobjmgr->GetFirstObjectInRange(psel->GetCpMin(),
|
|
psel->GetCpMost());
|
|
}
|
|
else
|
|
{
|
|
// Make sure the IOB is in range
|
|
if(0 <= iob && iob < GetObjectCount())
|
|
pobj = pobjmgr->GetObjectFromIndex(iob);
|
|
}
|
|
return pobj;
|
|
}
|
|
|