3250 lines
88 KiB
C++
3250 lines
88 KiB
C++
/*
|
|
* @doc TOM
|
|
*
|
|
* @module TOMRANGE.CPP - Implement the CTxtRange Class |
|
|
*
|
|
* This module contains the implementation of the TOM ITextRange
|
|
* interface on the CTxtRange object
|
|
*
|
|
* History: <nl>
|
|
* 5/24/95 - Alex Gounares: stubs created <nl>
|
|
* 8/95 - MurrayS: main implementation <nl>
|
|
* 11/95 - MurrayS: upgrade to TOM spec of 12/10/95 <nl>
|
|
* 5/96 - MurrayS: added zombie protection
|
|
*
|
|
* @comm
|
|
* All ITextRange methods return HRESULTs. If the method can move a
|
|
* range cp, the HRESULT is NOERROR if movement occurs and S_FALSE if
|
|
* no movement occurs. These methods usually take a <p pDelta> argument
|
|
* that returns the count of characters or Units actually moved. If this
|
|
* parameter is NULL, E_INVALIDARG is returned. Other return values
|
|
* include E_NOTIMPL, e.g., for Unit values not implemented,
|
|
* E_OUTOFMEMORY, e.g., when allocations fail, and CO_E_RELEASED, when
|
|
* the CTxtEdit (_ped) to which the range is attached has been deleted.
|
|
*
|
|
* For more complete documentation, please see tom.doc
|
|
*
|
|
* @devnote
|
|
* All ptr parameters must be validated before use and all entry points
|
|
* need to check whether this range is a zombie. These checks are
|
|
* done in one of three places: 1) immediately on entry to a function,
|
|
* 2) immediately on entry to a helper function (e.g., private Mover()
|
|
* for the move methods), or 3) before storing the out value.
|
|
* Alternative 3) is used for optional return values, such as pDelta
|
|
* and pB.
|
|
*
|
|
* To achieve a simple, efficient inheritance model, CTxtSelection
|
|
* inherits ITextSelection through CTxtRange. Otherwise we'd have a
|
|
* diamond inheritance, since ITextSelection itself inherits from
|
|
* ITextRange. Diamond inheritance creates two copies of the multiply
|
|
* inherited class unless that class is inherited virtually. Virtual
|
|
* inheritance uses run-time base-offset tables and is slower and
|
|
* bigger. To avoid such a mess, we include the extra ITextSelection
|
|
* methods in CTxtRange, with the intention that they'll never be called
|
|
* and therefore they return E_NOTIMPL. This is overridden for
|
|
* ITextSelection objects
|
|
*
|
|
* @future
|
|
* 1) Finder match ^p, etc.
|
|
* 2) Fast GetEffects() method. Would speed up the myriad IsProtected()
|
|
* calls and be useful for getting other effects as well.
|
|
* 3) Fast copies/pastes of RichEdit binary format. This can be done by
|
|
* creating a method to copy a range to a new CTxtStory and a method
|
|
* to insert a CTxtStory.
|
|
* 4) Delayed rendering
|
|
*
|
|
* Copyright (c) 1995-1998, Microsoft Corporation. All rights reserved.
|
|
*/
|
|
|
|
#include "_common.h"
|
|
#include "_select.h"
|
|
#include "_edit.h"
|
|
#include "_line.h"
|
|
#include "_frunptr.h"
|
|
#include "_tomfmt.h"
|
|
#include "_disp.h"
|
|
#include "_objmgr.h"
|
|
#include "_callmgr.h"
|
|
#include "_measure.h"
|
|
|
|
ASSERTDATA
|
|
|
|
#define DEBUG_CLASSNAME CTxtRange
|
|
#include "_invar.h"
|
|
|
|
HRESULT QueryInterface (REFIID riid, REFIID riid1, IUnknown *punk,
|
|
void **ppv, BOOL fZombie);
|
|
|
|
|
|
//----------------- CTxtRange (ITextRange) PUBLIC methods ----------------------------------
|
|
|
|
//----------------------- CTxtRange IUnknown Methods -------------------------------------
|
|
|
|
/*
|
|
* CTxtRange::QueryInterface (riid, ppv)
|
|
*
|
|
* @mfunc
|
|
* IUnknown method
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (!ppv) ? E_INVALIDARG :
|
|
* (interface found) ? NOERROR : E_NOINTERFACE
|
|
*/
|
|
STDMETHODIMP CTxtRange::QueryInterface (
|
|
REFIID riid, //@parm Reference to requested interface ID
|
|
void ** ppv) //@parm Out parm to receive interface ptr
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::QueryInterface");
|
|
|
|
REFIID riid1 = _fSel && IsEqualIID(riid, IID_ITextSelection)
|
|
? IID_ITextSelection : IID_ITextRange;
|
|
return ::QueryInterface(riid, riid1, this, ppv, IsZombie());
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::AddRef()
|
|
*
|
|
* @mfunc
|
|
* IUnknown method
|
|
*
|
|
* @rdesc
|
|
* ULONG - incremented reference count
|
|
*/
|
|
STDMETHODIMP_(ULONG) CTxtRange::AddRef()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::AddRef");
|
|
|
|
return ++_cRefs;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Release()
|
|
*
|
|
* @mfunc
|
|
* IUnknown method
|
|
*
|
|
* @rdesc
|
|
* ULONG - decremented reference count
|
|
*/
|
|
STDMETHODIMP_(ULONG) CTxtRange::Release()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Release");
|
|
|
|
_cRefs--;
|
|
|
|
if(!_cRefs)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
Assert(_cRefs > 0);
|
|
return _cRefs;
|
|
}
|
|
|
|
|
|
//------------------------ CTxtRange IDispatch Methods -------------------------------------
|
|
|
|
/*
|
|
* CTxtRange::GetTypeInfoCount(pcTypeInfo)
|
|
*
|
|
* @mfunc
|
|
* Get the number of TYPEINFO elements (1)
|
|
*
|
|
* @rdesc
|
|
* HRESULT
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetTypeInfoCount (
|
|
UINT * pcTypeInfo) //@parm Out parm to receive type-info count
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetTypeInfoCount");
|
|
|
|
if(!pcTypeInfo)
|
|
return E_INVALIDARG;
|
|
|
|
*pcTypeInfo = 1;
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::GetTypeInfo(iTypeInfo, lcid, ppTypeInfo)
|
|
*
|
|
* @mfunc
|
|
* Return ptr to type information object for ITextSelection interface
|
|
*
|
|
* @rdesc
|
|
* HRESULT
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetTypeInfo (
|
|
UINT iTypeInfo, //@parm Index of type info to return
|
|
LCID lcid, //@parm Local ID of type info
|
|
ITypeInfo **ppTypeInfo) //@parm Out parm to receive type info
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetTypeInfo");
|
|
|
|
return ::GetTypeInfo(iTypeInfo, g_pTypeInfoSel, ppTypeInfo);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid)
|
|
*
|
|
* @mfunc
|
|
* Get DISPIDs for methods in the ITextSelection, ITextRange, ITextFont,
|
|
* and ITextPara interfaces
|
|
*
|
|
* @rdesc
|
|
* HRESULT
|
|
*
|
|
* @devnote
|
|
* If the ITextFont and ITextPara ever offer more methods than exposed
|
|
* in their type libraries, the code should delegate to the corresponding
|
|
* GetIDsOfNames. The current code only gets DISPIDs for the methods in
|
|
* type libraries, thereby not having to instantiate the objects.
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetIDsOfNames (
|
|
REFIID riid, //@parm Interface ID to interpret names for
|
|
OLECHAR ** rgszNames, //@parm Array of names to be mapped
|
|
UINT cNames, //@parm Count of names to be mapped
|
|
LCID lcid, //@parm Local ID to use for interpretation
|
|
DISPID * rgdispid) //@parm Out parm to receive name mappings
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetIDsOfNames");
|
|
|
|
HRESULT hr = GetTypeInfoPtrs(); // Ensure TypeInfo ptrs are OK
|
|
if(hr != NOERROR)
|
|
return hr;
|
|
|
|
if(g_pTypeInfoSel->GetIDsOfNames(rgszNames, cNames, rgdispid) == NOERROR)
|
|
return NOERROR;
|
|
|
|
if(g_pTypeInfoFont->GetIDsOfNames(rgszNames, cNames, rgdispid) == NOERROR)
|
|
return NOERROR;
|
|
|
|
return g_pTypeInfoPara->GetIDsOfNames(rgszNames, cNames, rgdispid);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Invoke(dispidMember, riid, lcid, wFlags, pdispparams,
|
|
* pvarResult, pexcepinfo, puArgError)
|
|
* @mfunc
|
|
* Invoke methods for the ITextRange and ITextSelection objects, as
|
|
* well as for ITextFont and ITextPara interfaces on those objects.
|
|
*
|
|
* @rdesc
|
|
* HRESULT
|
|
*/
|
|
STDMETHODIMP CTxtRange::Invoke (
|
|
DISPID dispidMember, //@parm Identifies member function
|
|
REFIID riid, //@parm Pointer to interface ID
|
|
LCID lcid, //@parm Locale ID for interpretation
|
|
USHORT wFlags, //@parm Flags describing context of call
|
|
DISPPARAMS *pdispparams, //@parm Ptr to method arguments
|
|
VARIANT * pvarResult, //@parm Out parm for result (if not NULL)
|
|
EXCEPINFO * pexcepinfo, //@parm Out parm for exception info
|
|
UINT * puArgError) //@parm Out parm for error
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Invoke");
|
|
|
|
HRESULT hr = GetTypeInfoPtrs(); // Ensure TypeInfo ptrs are OK
|
|
if(hr != NOERROR)
|
|
return hr;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
IDispatch * pDispatch;
|
|
ITypeInfo * pTypeInfo;
|
|
|
|
if((DWORD)dispidMember <= 0x2ff) // Include default (0), selection,
|
|
{ // and range DISPIDs
|
|
pTypeInfo = g_pTypeInfoSel;
|
|
pDispatch = this;
|
|
AddRef(); // Compensate for Release() below
|
|
}
|
|
else if((DWORD)dispidMember <= 0x3ff) // 0x300 to 0x3ff: DISPIDs
|
|
{ // reserved for ITextFont
|
|
pTypeInfo = g_pTypeInfoFont;
|
|
hr = GetFont((ITextFont**)&pDispatch);
|
|
}
|
|
else if((DWORD)dispidMember <= 0x4ff) // 0x400 to 0x4ff: DISPIDs
|
|
{ // reserved for ITextPara
|
|
pTypeInfo = g_pTypeInfoPara;
|
|
hr = GetPara((ITextPara **)&pDispatch);
|
|
}
|
|
else // dispidMember is negative or
|
|
return DISP_E_MEMBERNOTFOUND; // > 0x4ff, i.e., not TOM
|
|
|
|
if(hr != NOERROR) // Couldn't instantiate ITextFont
|
|
return hr; // or ITextPara
|
|
|
|
hr = pTypeInfo->Invoke(pDispatch, dispidMember, wFlags,
|
|
pdispparams, pvarResult, pexcepinfo, puArgError);
|
|
pDispatch->Release();
|
|
return hr;
|
|
}
|
|
|
|
|
|
//----------------------- ITextRange Methods/Properties ------------------------
|
|
|
|
/*
|
|
* CTxtRange::CanEdit (pB)
|
|
*
|
|
* @mfunc
|
|
* Set *<p pB> = tomTrue iff this range can be edited and
|
|
* pB isn't NULL
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (can edit) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::CanEdit (
|
|
long * pB) //@parm Out parm to receive boolean value
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::CanEdit");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
return IsTrue(!WriteAccessDenied(), pB);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::CanPaste (pVar, long Format, pB)
|
|
*
|
|
* @mfunc
|
|
* Set *<p pB> = tomTrue iff the data object <p pVar>->punkVal can be
|
|
* pasted into this range and pB isn't NULL. If <p pVar> is NULL,
|
|
* use the clipboard instead.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (can paste) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::CanPaste (
|
|
VARIANT * pVar, //@parm Data object to paste
|
|
long Format, //@parm Desired clipboard format
|
|
long * pB) //@parm Out parm to receive boolean value
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::CanPaste");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
HRESULT hr;
|
|
IDataObject * pdo = NULL; // Default clipboard
|
|
|
|
if(pVar && pVar->vt == VT_UNKNOWN)
|
|
pVar->punkVal->QueryInterface(IID_IDataObject, (void **)&pdo);
|
|
|
|
hr = IsTrue(!WriteAccessDenied() &&
|
|
(GetPed()->GetDTE()->CanPaste(pdo, (CLIPFORMAT)Format,
|
|
RECO_PASTE)), pB);
|
|
if(pdo)
|
|
pdo->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* ITextRange::ChangeCase (long Type)
|
|
*
|
|
* @mfunc
|
|
* Change the case of letters in this range according to Type:
|
|
*
|
|
* tomSentenceCase = 0: capitalize first letter of each sentence
|
|
* tomLowerCase = 1: change all letters to lower case
|
|
* tomUpperCase = 2: change all letters to upper case
|
|
* tomTitleCase = 3: capitalize the first letter of each word
|
|
* tomToggleCase = 4: toggle the case of each letter
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (WriteAccessDenied) ? E_ACCESSDENIED :
|
|
* (if change) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::ChangeCase (
|
|
long Type) //@parm Type of case change. Default value: tomLower
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::ChangeCase");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
|
|
if(WriteAccessDenied())
|
|
return E_ACCESSDENIED;
|
|
|
|
IUndoBuilder * publdr;
|
|
CGenUndoBuilder undobldr(GetPed(), UB_AUTOCOMMIT, &publdr);
|
|
LONG cpMin, cpMax;
|
|
LONG cch = GetRange(cpMin, cpMax);
|
|
CRchTxtPtr rtp(*this);
|
|
|
|
undobldr.StopGroupTyping();
|
|
|
|
rtp.SetCp(cpMin);
|
|
return (rtp.ChangeCase(cch, Type, publdr)) ? NOERROR : S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Collapse (bStart)
|
|
*
|
|
* @mfunc
|
|
* Collapse this range into a degenerate point either at the
|
|
* the start (<p bStart> is nonzero or the end (<p bStart> = 0)
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::Collapse (
|
|
long bStart) //@parm Flag specifying end to collapse at
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Collapse");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
|
|
if(!_cch) // Already collapsed
|
|
return S_FALSE; // Signal that no change occurred
|
|
|
|
Collapser(bStart);
|
|
Update(TRUE); // Update selection
|
|
return NOERROR; // Signal that change occurred
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Copy (pVar)
|
|
*
|
|
* @mfunc
|
|
* Copy the plain and/or rich text to a data object and return the
|
|
* object ptr in <p pVar>. If <p pVar> is null, copy to the clipboard.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if success) ? NOERROR : E_OUTOFMEMORY
|
|
*/
|
|
STDMETHODIMP CTxtRange::Copy (
|
|
VARIANT * pVar) //@parm Out parm for data object
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Copy");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
LONG lStreamFormat = CheckTableSelection(FALSE, TRUE, NULL, 0)
|
|
? SFF_WRITEXTRAPAR : 0;
|
|
|
|
CLightDTEngine * pldte = &GetPed()->_ldte;
|
|
|
|
if(pVar && pVar->vt == (VT_UNKNOWN | VT_BYREF))
|
|
{
|
|
return pldte->RangeToDataObject(this, SF_TEXT | SF_RTF | lStreamFormat,
|
|
(IDataObject **)pVar->ppunkVal);
|
|
}
|
|
return pldte->CopyRangeToClipboard(this, lStreamFormat);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Cut (pVar)
|
|
*
|
|
* @mfunc
|
|
* Cut the plain and/or rich text to a data object and return the
|
|
* object ptr in <p pVar>. If <p pVar> is null,
|
|
* cut to the clipboard.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if success) ? NOERROR : E_OUTOFMEMORY
|
|
*/
|
|
STDMETHODIMP CTxtRange::Cut (
|
|
VARIANT * pVar) //@parm Out parm for data object
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Cut");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
|
|
if(WriteAccessDenied())
|
|
return E_ACCESSDENIED;
|
|
|
|
HRESULT hr = Copy(pVar);
|
|
|
|
Replacer(0, NULL);
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Delete (Unit, Count, pDelta)
|
|
*
|
|
* @mfunc
|
|
* If this range is nondegenerate, delete it along with |Count| - 1 Units
|
|
* in the direction specified by the sign of Count. If this range is
|
|
* degenerate, delete Count Units.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (WriteAccessDenied) ? E_ACCESSDENIED :
|
|
* (all requested Units deleted) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::Delete (
|
|
long Unit, //@parm Unit to use
|
|
long Count, //@parm Number of chars to delete
|
|
long * pDelta) //@parm Out parm to receive count of units deleted
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Delete");
|
|
|
|
if(pDelta)
|
|
*pDelta = 0;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
|
|
if(WriteAccessDenied())
|
|
return E_ACCESSDENIED;
|
|
|
|
LONG cchSave = _cch; // Remember initial count
|
|
LONG cchText = GetAdjustedTextLength();
|
|
LONG CountOrg = Count;
|
|
LONG cpMin, cpMost;
|
|
LONG cUnit = 0; // Default no Units
|
|
MOVES Mode = (Count >= 0) ? MOVE_END : MOVE_START;
|
|
|
|
GetRange(cpMin, cpMost);
|
|
if(cpMost > cchText) // Can't delete final CR. To get
|
|
{ // *pDelta right, handle here
|
|
Set(cpMin, cpMin - cchText); // Backup before CR & set active
|
|
if(Count > 0) // end at cpMin since there's
|
|
{ // nothing to delete forward
|
|
Count = 0;
|
|
if(!_cch) // Only collapsed selection of
|
|
Mode = MOVE_IP; // final CR: set up nothing
|
|
} // deleted (MOVE_IP = 0)
|
|
}
|
|
if(Count)
|
|
{
|
|
if((_cch ^ Mode) < 0) // Be sure active end is in
|
|
FlipRange(); // deletion direction
|
|
if(cchSave) // Deleting nondegenerate range
|
|
Count -= Mode; // counts as one unit
|
|
if(Mover(Unit, Count, &cUnit, Mode) // Try to expand range for
|
|
== E_INVALIDARG) // remaining Count Units
|
|
{
|
|
if(pDelta)
|
|
*pDelta = 0;
|
|
return E_INVALIDARG;
|
|
}
|
|
if(GetCp() > cchText && cUnit > 0) // Range includes final CR, which
|
|
{ // cannot be deleted. Reduce
|
|
if(Unit == tomCharacter) // counts for some Units
|
|
cUnit -= GetTextLength() - cchText;
|
|
else if(Unit == tomWord)
|
|
cUnit--; // An EOP qualifies as a tomWord
|
|
}
|
|
}
|
|
|
|
if(cchSave) // Deleting nondegenerate range
|
|
cUnit += Mode; // counts as a Unit
|
|
|
|
if(pDelta)
|
|
*pDelta = cUnit;
|
|
|
|
if(_cch) // Mover() may have changed _cch
|
|
{
|
|
IUndoBuilder * publdr;
|
|
CGenUndoBuilder undobldr(GetPed(), UB_AUTOCOMMIT, &publdr);
|
|
|
|
if (publdr)
|
|
{
|
|
publdr->StopGroupTyping();
|
|
publdr->SetNameID(UID_DELETE);
|
|
}
|
|
|
|
// FUTURE (murrays): the cchSave case should set up undo to
|
|
// restore the original range, not the extended range resulting
|
|
// when |CountOrg| > 1. This could be done using two calls to
|
|
// ReplaceRange(), one to delete the original range and one to
|
|
// delete the rest
|
|
SELRR selrr = !_fSel || cchSave ? SELRR_REMEMBERRANGE :
|
|
CountOrg > 0 ? SELRR_REMEMBERCPMIN :
|
|
SELRR_REMEMBERENDIP;
|
|
|
|
CheckTableSelection(TRUE, TRUE, NULL, 0);
|
|
ReplaceRange(0, NULL, publdr, selrr);
|
|
|
|
if (cUnit == CountOrg || // Delete(Unit,0,0)
|
|
cUnit == 1 && !CountOrg) // deletes one "Unit", namely
|
|
{ // what's selected
|
|
return NOERROR; // Signal everything deleted as
|
|
} // requested
|
|
}
|
|
else if(cchSave) // Collapsed selection of final CR
|
|
{ // but didn't delete anything
|
|
Update(TRUE); // Selection highlighting changed
|
|
}
|
|
return S_FALSE; // Less deleted than requested
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::EndOf (Unit, Extend, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Move this range end(s) to end of the first overlapping Unit in
|
|
* the range.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if Unit supported) ? S_FALSE : E_NOTIMPL
|
|
*/
|
|
STDMETHODIMP CTxtRange::EndOf (
|
|
long Unit, //@parm Unit to use
|
|
long Extend, //@parm If true, leave other end alone
|
|
long * pDelta) //@parm Count of chars that End is moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::EndOf");
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
LONG cpMost;
|
|
|
|
HRESULT hr = Expander (Unit, Extend, pDelta, NULL, &cpMost);
|
|
if(hr == NOERROR)
|
|
Update(TRUE); // Update selection
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Expand (Unit, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Expand this range so that any partial Units it contains are
|
|
* completely contained. If this range consists of one or more full
|
|
* Units, no change is made. If this range is an insertion point at
|
|
* the beginning or within a Unit, Expand() expands this range to include
|
|
* that Unit. If this range is an insertion point at the end of the
|
|
* story, Expand() tries to set this range to include the last Unit in
|
|
* the story. The active end is always cpMost except for the last case.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if Unit supported) ? S_FALSE : E_NOTIMPL
|
|
*/
|
|
STDMETHODIMP CTxtRange::Expand (
|
|
long Unit, //@parm Unit to expand range to
|
|
long * pDelta) //@parm Out parm to receive count of chars added
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Expand");
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
LONG cpMin, cpMost;
|
|
|
|
HRESULT hr = Expander (Unit, TRUE, pDelta, &cpMin, &cpMost);
|
|
|
|
if(SUCCEEDED(hr))
|
|
Update(TRUE); // Update selection
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::FindText (bstr, cch, Flags, pLength)
|
|
*
|
|
* @mfunc
|
|
* If this range isn't an insertion point already, convert it into an
|
|
* insertion point at its End if <p cch> <gt> 0 and at its Start if
|
|
* <p cch> <lt> 0. Then search up to <p cch> characters of the range
|
|
* looking for the string <p bstr> subject to the compare flags
|
|
* <p Flags>. If <p cch> <gt> 0, the search is forward and if <p cch>
|
|
* <lt> 0 the search is backward. If the string is found, the range
|
|
* limits are changed to be those of the matched string and *<p pLength>
|
|
* is set equal to the length of the string. If the string isn't found,
|
|
* the range remains unchanged and *<p pLength> is set equal to 0.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if <p bstr> found) ? NOERROR : S_FALSE
|
|
*
|
|
* @devnote
|
|
* Argument validation of the three Find methods is done by the helper
|
|
* function CTxtRange::Finder(bstr, cch, dwFlags, pDelta, fExtend, fFlip)
|
|
*/
|
|
STDMETHODIMP CTxtRange::FindText (
|
|
BSTR bstr, //@parm String to find
|
|
long Count, //@parm Max count of chars to search
|
|
long Flags, //@parm Flags governing compares
|
|
long * pDelta) //@parm Out parm to receive count of chars moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::FindText");
|
|
|
|
return Finder(bstr, Count, Flags, pDelta, MOVE_IP);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::FindTextEnd (bstr, cch, Flags, pLength)
|
|
*
|
|
* @mfunc
|
|
* Starting from this range's End, search up to <p cch> characters
|
|
* looking for the string <p bstr> subject to the compare flags
|
|
* <p Flags>. If <p cch> <gt> 0, the search is forward and if <p cch>
|
|
* <lt> 0 the search is backward. If the string is found, the range
|
|
* limits are changed to be those of the matched string and *<p pLength>
|
|
* is set equal to the length of the string. If the string isn't found,
|
|
* the range remains unchanged and *<p pLength> is set equal to 0.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if <p bstr> found) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::FindTextEnd (
|
|
BSTR bstr, //@parm String to find
|
|
long Count, //@parm Max count of chars to search
|
|
long Flags, //@parm Flags governing compares
|
|
long * pDelta) //@parm Out parm to receive count of chars moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::FindTextEnd");
|
|
|
|
return Finder(bstr, Count, Flags, pDelta, MOVE_END);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::FindTextStart (bstr, cch, Flags, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Starting from this range's Start, search up to <p cch> characters
|
|
* looking for the string <p bstr> subject to the compare flags
|
|
* <p Flags>. If <p cch> <gt> 0, the search is forward and if <p cch>
|
|
* <lt> 0 the search is backward. If the string is found, the range
|
|
* limits are changed to be those of the matched string and *<p pLength>
|
|
* is set equal to the length of the string. If the string isn't found,
|
|
* the range remains unchanged and *<p pLength> is set equal to 0.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if <p bstr> found) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::FindTextStart (
|
|
BSTR bstr, //@parm String to find
|
|
long Count, //@parm Max count of chars to search
|
|
long Flags, //@parm Flags governing compares
|
|
long * pDelta) //@parm Out parm to receive count of chars moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::FindTextStart");
|
|
|
|
return Finder(bstr, Count, Flags, pDelta, MOVE_START);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::GetChar (pChar)
|
|
*
|
|
* @mfunc
|
|
* Set *<p pChar> equal to the character at cpFirst
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (<p pChar>) NOERROR ? E_INVALIDARG
|
|
*
|
|
* @devnote
|
|
* This method is very handy for walking a range character by character
|
|
* from the Start. Accordingly, it's desirable that the active end
|
|
* is at the Start. We set this up for a range, since the API doesn't
|
|
* care which range end is active. But we can't do this for a selection,
|
|
* since the selection API depends on the active end.
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetChar (
|
|
long * pChar) //@parm Out parm for char
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetChar");
|
|
|
|
HRESULT hr = GetLong(0, pChar);
|
|
if(hr != NOERROR)
|
|
return hr;
|
|
|
|
if(_cch > 0) // Active end at cpMost (End)
|
|
{
|
|
if(_fSel)
|
|
{
|
|
CTxtPtr tp(_rpTX); // For selection, can't change
|
|
tp.Move(-_cch); // active end
|
|
*pChar = (long)(tp.GetChar());
|
|
return NOERROR;
|
|
}
|
|
FlipRange(); // For range, it's more efficient
|
|
} // to work from cpFirst and API
|
|
*(DWORD *)pChar = _rpTX.GetChar(); // doesn't expose RichEdit active
|
|
// end
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::GetDuplicate (ppRange)
|
|
*
|
|
* @mfunc
|
|
* Get a clone of this range object. For example, you may want to
|
|
* create an insertion point to traverse a range, so you clone the
|
|
* range and then set the clone's cpLim equal to its cpFirst. A range
|
|
* is characterized by its cpFirst, cpLim, and the story it belongs to.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if success) ? NOERROR :
|
|
* (<p ppRange>) ? E_OUTOFMEMORY : E_INVALIDARG
|
|
*
|
|
* @comm
|
|
* Even if this range is a selection, the clone returned is still only
|
|
* a range.
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetDuplicate (
|
|
ITextRange ** ppRange) //@parm Out parm to receive duplicate of range
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetDuplicate");
|
|
|
|
HRESULT hr = GetLong(NULL, (LONG *)ppRange);
|
|
if(hr != NOERROR)
|
|
return hr;
|
|
|
|
#ifdef _WIN64
|
|
*ppRange = NULL;
|
|
#endif
|
|
|
|
ITextRange *prg = new CTxtRange(*this);
|
|
if(prg)
|
|
{
|
|
*ppRange = prg;
|
|
prg->AddRef();
|
|
return NOERROR;
|
|
}
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
/*
|
|
* ITextRange::GetEmbeddedObject (ppV)
|
|
*
|
|
* @mfunc
|
|
* Property get method that gets a ptr to the object at cpFirst
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (!ppV) ? E_INVALIDARG :
|
|
* (object found) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetEmbeddedObject (
|
|
IUnknown **ppV) //@parm Out parm to receive embedded object
|
|
{
|
|
HRESULT hr = GetLong(NULL, (LONG *)ppV);
|
|
if(hr != NOERROR)
|
|
return hr;
|
|
|
|
#ifdef _WIN64
|
|
*ppV = NULL;
|
|
#endif
|
|
|
|
if(GetObjectCount())
|
|
{
|
|
COleObject *pobj = GetPed()->_pobjmgr->GetObjectFromCp(GetCpMin());
|
|
|
|
if(pobj && (*ppV = pobj->GetIUnknown()) != NULL)
|
|
{
|
|
(*ppV)->AddRef();
|
|
return NOERROR;
|
|
}
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::GetEnd (pcpLim)
|
|
*
|
|
* @mfunc
|
|
* Get this range's End (cpMost) cp
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (<p pcpLim>) ? NOERROR : E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetEnd (
|
|
long * pcpLim) //@parm Out parm to receive End cp value
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetEnd");
|
|
|
|
return GetLong(GetCpMost(), pcpLim);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::GetFont (ppFont)
|
|
*
|
|
* @mfunc
|
|
* Get an ITextFont object with the character attributes of this range
|
|
*
|
|
* @rdesc
|
|
* HRESULT = <p ppFont> ? NOERROR : E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetFont (
|
|
ITextFont ** ppFont) //@parm Out parm to receive ITextFont object
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetFont");
|
|
|
|
HRESULT hr = GetLong(NULL, (LONG *)ppFont);
|
|
if(hr != NOERROR)
|
|
return hr;
|
|
|
|
#ifdef _WIN64
|
|
*ppFont = NULL;
|
|
#endif
|
|
|
|
*ppFont = (ITextFont *) new CTxtFont(this);
|
|
return *ppFont ? NOERROR : E_OUTOFMEMORY;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::GetFormattedText (ppRange)
|
|
*
|
|
* @mfunc
|
|
* Retrieves an ITextRange with this range's formatted text.
|
|
* If <p ppRange> is NULL, the clipboard is the target.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if success) ? NOERROR :
|
|
* (<p ppRange>) ? E_OUTOFMEMORY : E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetFormattedText (
|
|
ITextRange ** ppRange) //@parm Out parm to receive formatted text
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetFormattedText");
|
|
|
|
return GetDuplicate(ppRange);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::GetIndex (Unit, pIndex)
|
|
*
|
|
* @mfunc
|
|
* Set *<p pIndex> equal to the Unit number at this range's cpFirst
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (!<p pIndex>) ? E_INVALIDARG :
|
|
* (Unit not implemented) ? E_NOTIMPL :
|
|
* (Unit available) ? NOERROR : S_FALSE
|
|
* @future
|
|
* implement tomWindow?
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetIndex (
|
|
long Unit, //@parm Unit to index
|
|
long * pIndex) //@parm Out parm to receive index value
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetIndex");
|
|
|
|
HRESULT hr = GetLong(0, pIndex);
|
|
if(hr != NOERROR)
|
|
return hr;
|
|
|
|
LONG cp;
|
|
LONG cUnit = tomBackward;
|
|
CTxtRange rg(*this);
|
|
|
|
hr = rg.Expander(Unit, FALSE, NULL, // Go to Start of Unit; else
|
|
&cp, NULL); // UnitCounter gives 1 extra
|
|
if(FAILED(hr))
|
|
return hr; // Unit not recognized
|
|
|
|
LONG cch = rg.UnitCounter(Unit, cUnit, 0,
|
|
_fSel && cp == GetCp() && ((CTxtSelection *)this)->IsCaretNotAtBOL());
|
|
|
|
if(cch == tomForward) // UnitCounter() doesn't know
|
|
return E_NOTIMPL; // Unit
|
|
|
|
if(cch == tomBackward) // Unit not in story
|
|
return S_FALSE;
|
|
|
|
*pIndex = -cUnit + 1; // Make count positive and
|
|
// 1-based
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::GetPara (ppPara)
|
|
*
|
|
* @mfunc
|
|
* Get an ITextPara object with the paragraph attributes of this range
|
|
*
|
|
* @rdesc
|
|
* HRESULT = <p ppPara> ? NOERROR : E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetPara (
|
|
ITextPara ** ppPara) //@parm Out parm to receive ITextPara object
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetPara");
|
|
|
|
HRESULT hr = GetLong(NULL, (LONG *)ppPara);
|
|
if(hr != NOERROR)
|
|
return hr;
|
|
|
|
#ifdef _WIN64
|
|
*ppPara = NULL;
|
|
#endif
|
|
|
|
*ppPara = (ITextPara *) new CTxtPara(this);
|
|
return *ppPara ? NOERROR : E_OUTOFMEMORY;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::GetPoint (px, py, Type)
|
|
*
|
|
* @mfunc
|
|
* Get point for selection Start or End and intraline position
|
|
* as determined by <p Type>.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (!<p px> or !<p py>) ? E_INVALIDARG :
|
|
* (if success) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetPoint (
|
|
long Type, //@parm Type of point
|
|
long * px, //@parm Out parm for x coordinate
|
|
long * py) //@parm Out parm for y coordinate
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::GetPoint");
|
|
|
|
if(!px || !py)
|
|
return E_INVALIDARG;
|
|
|
|
*px = *py = 0;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
LONG ili;
|
|
BOOL fEnd = !(Type & tomStart); // TRUE if get pt at end of range
|
|
BOOL fAtEnd = _cch && fEnd; // TRUE if get ambiguous pt at end of line
|
|
POINTUV pt;
|
|
CRchTxtPtr rtp(*this); // Default active end
|
|
CTxtEdit * ped = GetPed();
|
|
CDisplay * pdp = ped->_pdp; // Save indirections
|
|
|
|
if(!pdp || !ped->fInplaceActive()) // No display or not active
|
|
return E_FAIL; // then we can do nothing
|
|
|
|
if((_cch > 0) ^ fEnd) // Move tp to active end
|
|
rtp.Move(-_cch);
|
|
|
|
ili = pdp->PointFromTp(rtp, NULL, fAtEnd, pt, NULL, Type & 0x1f);
|
|
POINT ptxy;
|
|
pdp->PointFromPointuv(ptxy, pt);
|
|
|
|
RECTUV rcView; // Verify return value makes
|
|
// sense since PointFromTp
|
|
pdp->GetViewRect(rcView, NULL); // may return values outside
|
|
// client rect
|
|
rcView.bottom++; // Enlarge Rect to include
|
|
rcView.right++; // bottom and right edges
|
|
if(ili >= 0 && (PtInRect(&rcView, pt) || // Function succeeded or
|
|
(Type & tomAllowOffClient))) // allow off client rect pts
|
|
{
|
|
// Caller wants screen coordinates?
|
|
if ( !(Type & tomClientCoord) )
|
|
ped->TxClientToScreen(&ptxy);
|
|
|
|
*px = ptxy.x;
|
|
*py = ptxy.y;
|
|
return NOERROR;
|
|
}
|
|
return S_FALSE; // Function failed
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::GetStart (pcpFirst)
|
|
*
|
|
* @mfunc
|
|
* Get this range's Start (cpMin) cp
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (<p pcpFirst>) ? NOERROR : E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetStart (
|
|
long * pcpFirst) //@parm Out parm to receive Start cp value
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetStart");
|
|
|
|
return GetLong(GetCpMin(), pcpFirst);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::GetStoryLength (pcch)
|
|
*
|
|
* @mfunc
|
|
* Set *<p pcch> = count of chars in this range's story
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (<p pcch>) ? NOERROR : E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetStoryLength (
|
|
long * pcch) //@parm Out parm to get length of this range's story
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetStoryLength");
|
|
|
|
return GetLong(GetTextLength(), pcch);
|
|
}
|
|
|
|
/*
|
|
* ITextRange::GetStoryType (pValue)
|
|
*
|
|
* @mfunc
|
|
* Property get method that gets the type of this range's
|
|
* story.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (pValue) NOERROR ? E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetStoryType (
|
|
long *pValue) //@parm Out parm to get type of this range's story
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetStoryType");
|
|
|
|
return GetLong(tomUnknownStory, pValue);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::GetText (pbstr)
|
|
*
|
|
* @mfunc
|
|
* Get plain text in this range. The Text property is the default
|
|
* property for ITextRange.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if success) ? NOERROR :
|
|
* (!<p pbstr>) ? E_INVALIDARG : E_OUTOFMEMORY
|
|
*/
|
|
STDMETHODIMP CTxtRange::GetText (
|
|
BSTR * pbstr) //@parm Out parm to receive bstr
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetText");
|
|
|
|
HRESULT hr = GetLong(NULL, (LONG *)pbstr);
|
|
if(hr != NOERROR)
|
|
return hr;
|
|
|
|
#ifdef _WIN64
|
|
*pbstr = NULL;
|
|
#endif
|
|
|
|
if(!GetCch())
|
|
return NOERROR;
|
|
|
|
LONG cpMin, cpMost;
|
|
LONG cch = GetRange(cpMin, cpMost);
|
|
|
|
*pbstr = SysAllocStringLen(NULL, cch);
|
|
if(!*pbstr)
|
|
return E_OUTOFMEMORY;
|
|
|
|
CTxtPtr tp(_rpTX);
|
|
tp.SetCp(cpMin);
|
|
tp.GetText( cch, (WCHAR*) * pbstr );
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::InRange (pRange, pB)
|
|
*
|
|
* @mfunc
|
|
* Returns *<p pB> = tomTrue iff this range points within or at the same
|
|
* text as <p pRange> does
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (within range) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::InRange (
|
|
ITextRange * pRange, //@parm ITextRange to compare with
|
|
long * pB) //@parm Out parm for comparison result
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::InRange");
|
|
|
|
return IsTrue(Comparer(pRange), pB);
|
|
}
|
|
|
|
|
|
/*
|
|
* CTxtRange::InStory (pRange, pB)
|
|
*
|
|
* @mfunc
|
|
* Return *pB = tomTrue iff this range's story is the same as
|
|
* <p pRange>'s
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (in story) ? NOERROR : S_FALSE
|
|
*
|
|
* @future
|
|
* If RichEdit acquires the ability to have multiple stories and
|
|
* therefore ranges get a _story member, then compare that member
|
|
* instead of calling _rpTX.SameRuns().
|
|
*/
|
|
STDMETHODIMP CTxtRange::InStory (
|
|
ITextRange *pRange, //@parm ITextRange to query for private interface
|
|
long * pB) //@parm Out parm to receive tomBool result
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::InStory");
|
|
|
|
return IsTrue(IsSameVtables(this, pRange) && // Same vtables,
|
|
_rpTX.SameRuns(&((CTxtRange *)pRange)->_rpTX), // same Runs
|
|
pB);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::IsEqual (pRange, pB)
|
|
*
|
|
* @mfunc
|
|
* Returns *<p pB> = tomTrue iff this range points at the same text (cp's
|
|
* and story) as <p pRange> does and pB isn't NULL.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (equal objects) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::IsEqual (
|
|
ITextRange * pRange, //@parm ITextRange to compare with
|
|
long * pB) //@parm Out parm for comparison result
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::IsEqual");
|
|
|
|
return IsTrue(Comparer(pRange) < 0, pB);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Move (Unit, Count, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Move end(s) <p Count> <p Unit>'s, returning *<p pDelta> = # units
|
|
* actually moved. In general, this method converts a range into an
|
|
* insertion point if it isn't already, and moves the insertion point.
|
|
* If <p Count> <gt> 0, motion is forward toward the end of the story;
|
|
* if <p Count> <lt> 0, motion is backward toward the beginning.
|
|
* <p Count> = 0 moves cpFirst to the beginning of the <p Unit>
|
|
* containing cpFirst.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if Unit supported) ? S_FALSE : E_NOTIMPL
|
|
* @devnote
|
|
* Argument validation of the three Move methods is done by the helper
|
|
* function CTxtRange::Mover(Unit, Count, pDelta, Mode)
|
|
*/
|
|
STDMETHODIMP CTxtRange::Move (
|
|
long Unit, //@parm Unit to use
|
|
long Count, //@parm Number of Units to move
|
|
long * pDelta) //@parm Out parm to receive actual count of
|
|
// Units end is moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Move");
|
|
CCallMgr callmgr(GetPed());
|
|
|
|
return Mover(Unit, Count, pDelta, MOVE_IP);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::MoveEnd (Unit, Count, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Move End end <p Count> <p Unit>'s, returning *<p pDelta> = # units
|
|
* actually moved
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if Unit supported) ? S_FALSE : E_NOTIMPL
|
|
*/
|
|
STDMETHODIMP CTxtRange::MoveEnd (
|
|
long Unit, //@parm Unit to use
|
|
long Count, //@parm Number of Units to move
|
|
long * pDelta) //@parm Out parm to receive actual count of
|
|
// Units end is moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveEnd");
|
|
CCallMgr callmgr(GetPed());
|
|
|
|
return Mover(Unit, Count, pDelta, MOVE_END);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::MoveEndUntil (Cset, Count, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Move the End just past all contiguous characters that are not found
|
|
* in the set of characters specified by the VARIANT <p Cset> parameter.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if <p Cset> valid) ? S_FALSE : E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtRange::MoveEndUntil (
|
|
VARIANT * Cset, //@parm Character match set to use
|
|
long Count, //@parm Max number of characters to move past
|
|
long * pDelta) //@parm Out parm to receive actual count of
|
|
// characters end is moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveEndUntil");
|
|
|
|
return Matcher(Cset, Count, pDelta, MOVE_END, MATCH_UNTIL);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::MoveEndWhile (Cset, Count, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Move the End just past all contiguous characters that are found in
|
|
* the set of characters specified by the VARIANT <p Cset> parameter.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if <p Cset> valid) ? S_FALSE : E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtRange::MoveEndWhile (
|
|
VARIANT * Cset, //@parm Character match set to use
|
|
long Count, //@parm Max number of characters to move past
|
|
long * pDelta) //@parm Out parm to receive actual count of
|
|
// characters end is moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveEndWhile");
|
|
|
|
return Matcher(Cset, Count, pDelta, MOVE_END, MATCH_WHILE);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::MoveStart (Unit, Count, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Move Start end <p Count> <p Unit>'s, returning *<p pDelta> = # units
|
|
* actually moved
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if Unit supported) ? S_FALSE : E_NOTIMPL
|
|
*/
|
|
STDMETHODIMP CTxtRange::MoveStart (
|
|
long Unit, //@parm Unit to use
|
|
long Count, //@parm Number of Units to move
|
|
long * pDelta) //@parm Out parm to receive actual count of
|
|
// Units end is moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveStart");
|
|
CCallMgr callmgr(GetPed());
|
|
|
|
return Mover(Unit, Count, pDelta, MOVE_START);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::MoveStartUntil (Cset, Count, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Move the Start just past all contiguous characters that are not found
|
|
* in the set of characters specified by the VARIANT <p Cset> parameter.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if <p Cset> valid) ? S_FALSE : E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtRange::MoveStartUntil (
|
|
VARIANT * Cset, //@parm Character match set to use
|
|
long Count, //@parm Max number of characters to move past
|
|
long * pDelta) //@parm Out parm to receive actual count of
|
|
// characters end is moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveStartUntil");
|
|
|
|
return Matcher(Cset, Count, pDelta, MOVE_START, MATCH_UNTIL);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::MoveStartWhile (Cset, Count, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Move the Start just past all contiguous characters that are found in
|
|
* the set of characters specified by the VARIANT <p Cset> parameter.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if <p Cset> valid) ? S_FALSE : E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtRange::MoveStartWhile (
|
|
VARIANT * Cset, //@parm Character match set to use
|
|
long Count, //@parm Max number of characters to move past
|
|
long * pDelta) //@parm Out parm to receive actual count of
|
|
// characters end is moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveStartWhile");
|
|
|
|
return Matcher(Cset, Count, pDelta, MOVE_START, MATCH_WHILE);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::MoveUntil (Cset,Count, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Convert this range into an insertion point if it isn't already,
|
|
* and keep moving the insertion point until encountering any
|
|
* character in the set of characters specified by the VARIANT cset
|
|
* parameter.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if <p Cset> valid) ? S_FALSE : E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtRange::MoveUntil (
|
|
VARIANT * Cset, //@parm Character match set to use
|
|
long Count, //@parm Max number of characters to move past
|
|
long * pDelta) //@parm Out parm to receive actual count of
|
|
// characters end is moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveUntil");
|
|
|
|
return Matcher(Cset, Count, pDelta, MOVE_IP, MATCH_UNTIL);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::MoveWhile (Cset, Count, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Convert this range into an insertion point if it isn't already,
|
|
* and keep moving the insertion point so long as (while) the
|
|
* characters past by are found in set of characters specified by
|
|
* the VARIANT cset parameter. Such a contiguous set of characters
|
|
* is known as a span of characters. The magnitude of the <p Count>
|
|
* parameter gives the maximum number of characters to move past and
|
|
* the sign of <p Count> specifies the direction to move in.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if <p Cset> valid) ? S_FALSE : E_INVALIDARG
|
|
*
|
|
* @devnote
|
|
* Argument validation of the MoveWhile and MoveUntil methods is done by
|
|
* the helper CTxtRange::Matcher (Cset, Count, pDelta, fExtend, fSpan)
|
|
*/
|
|
STDMETHODIMP CTxtRange::MoveWhile (
|
|
VARIANT * Cset, //@parm Character match set to use
|
|
long Count, //@parm Max number of characters to move past
|
|
long * pDelta) //@parm Out parm to receive actual count of
|
|
// characters end is moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::MoveWhile");
|
|
|
|
return Matcher(Cset, Count, pDelta, MOVE_IP, MATCH_WHILE);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Paste (pVar, ClipboardFormat)
|
|
*
|
|
* @mfunc
|
|
* Paste the data object <p pVar> into this range. If
|
|
* <p pVar> is null, paste from the clipboard.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (WriteAccessDenied) ? E_ACCESSDENIED :
|
|
* (if success) ? NOERROR : E_OUTOFMEMORY
|
|
*/
|
|
STDMETHODIMP CTxtRange::Paste (
|
|
VARIANT *pVar, //@parm Data object to paste
|
|
long ClipboardFormat) //@parm Desired clipboard format
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Paste");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
HRESULT hr;
|
|
IDataObject * pdo = NULL; // Default clipboard
|
|
IUndoBuilder * publdr;
|
|
CGenUndoBuilder undobldr(GetPed(), UB_AUTOCOMMIT, &publdr);
|
|
|
|
if(WriteAccessDenied())
|
|
return E_ACCESSDENIED;
|
|
|
|
if(pVar)
|
|
if (pVar->vt == VT_UNKNOWN)
|
|
pVar->punkVal->QueryInterface(IID_IDataObject, (void **)&pdo);
|
|
else if (pVar->vt == (VT_UNKNOWN | VT_BYREF))
|
|
pdo = (IDataObject *)(*pVar->ppunkVal);
|
|
|
|
hr = GetPed()->PasteDataObjectToRange (pdo, this,
|
|
(WORD)ClipboardFormat, NULL, publdr, PDOR_NONE);
|
|
|
|
if(pdo && pVar->vt == VT_UNKNOWN)
|
|
pdo->Release();
|
|
Update(TRUE); // Update selection
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* ITextRange::ScrollIntoView(long Code)
|
|
*
|
|
* @mfunc
|
|
* Method that scrolls this range into view according to the
|
|
* code Code defined below.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if success) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::ScrollIntoView (
|
|
long Code) //@parm Scroll code
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::ScrollIntoView");
|
|
|
|
// Check for invalid bits
|
|
if (Code & ~(tomStart + tomEnd + TA_LEFT + TA_TOP + TA_BOTTOM +
|
|
TA_CENTER + TA_STARTOFLINE + TA_ENDOFLINE + TA_LOGICAL))
|
|
return E_INVALIDARG;
|
|
|
|
// Validate parameter
|
|
long lCode = tomEnd;
|
|
if (Code & tomStart)
|
|
lCode = tomStart;
|
|
|
|
Code &= ~tomStart;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
// Get local copy of ped to save some indirections.
|
|
CTxtEdit *ped = GetPed();
|
|
|
|
if (!ped->fInplaceActive())
|
|
{
|
|
// If the control is not active, we can't get the information
|
|
// because no one knows what our client rect is.
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Get a local copy of display to save some indirections.
|
|
CDisplay *pdp = ped->_pdp;
|
|
|
|
if (pdp->IsFrozen())
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
LONG cpStart;
|
|
LONG cpForEnd;
|
|
|
|
GetRange(cpStart, cpForEnd);
|
|
|
|
// Get the view rectangle so we can compute the absolute x/y
|
|
RECTUV rcView;
|
|
pdp->GetViewRect(rcView, NULL);
|
|
|
|
// Set up tp for PointFromTp call
|
|
CRchTxtPtr rtp(*this);
|
|
|
|
if(_cch > 0)
|
|
rtp.Move(-_cch);
|
|
|
|
// Values used for making returned point locations absolute since
|
|
// PointFromTp adjusts the point returned to be relative to the
|
|
// display.
|
|
const LONG upScrollAdj = pdp->GetUpScroll() - rcView.left;
|
|
const LONG vpScrollAdj = pdp->GetVpScroll() - rcView.top;
|
|
|
|
// Get the left/top for the start
|
|
BOOL taMask = Code & TA_STARTOFLINE; //set beginning of line flag
|
|
BOOL fAtEnd = _cch ? TRUE : !rtp._rpTX.IsAfterEOP();
|
|
POINTUV ptStart;
|
|
CLinePtr rpStart(pdp);
|
|
LONG iliStart = pdp->PointFromTp(rtp, NULL, _cch ? FALSE : fAtEnd,
|
|
ptStart, &rpStart, (lCode == tomStart && Code) ? Code :
|
|
TA_TOP + TA_LEFT);
|
|
ptStart.u += upScrollAdj;
|
|
ptStart.v += vpScrollAdj;
|
|
|
|
// Get the right/bottom for the end
|
|
rtp.SetCp(cpForEnd);
|
|
|
|
POINTUV ptEnd;
|
|
CLinePtr rpEnd(pdp);
|
|
LONG iliEnd = pdp->PointFromTp(rtp, NULL, fAtEnd, ptEnd, &rpEnd,
|
|
(lCode == tomEnd && Code) ? Code :
|
|
TA_BOTTOM + TA_RIGHT);
|
|
ptEnd.u += upScrollAdj;
|
|
ptEnd.v += vpScrollAdj;
|
|
|
|
//
|
|
// Calculate the vpScroll
|
|
//
|
|
|
|
// The basic idea is to display both the start and the end if possible. But
|
|
// if it is not possible then display the requested end based on the input
|
|
// parameter.
|
|
|
|
LONG dvpView = pdp->GetDvpView();
|
|
LONG vpScroll;
|
|
|
|
if (tomStart == lCode)
|
|
{
|
|
// Scroll the Start cp to the top of the view
|
|
vpScroll = ptStart.v;
|
|
}
|
|
else
|
|
{
|
|
// Scroll the End cp to the bottom of the view
|
|
vpScroll = ptEnd.v;
|
|
if (!pdp->IsInPageView())
|
|
vpScroll -= dvpView;
|
|
}
|
|
|
|
//
|
|
// Calculate the X Scroll
|
|
//
|
|
|
|
// Default scroll to beginning of the line
|
|
LONG upScroll = 0;
|
|
|
|
// Make view local to save a number of indirections
|
|
LONG dupView = pdp->GetDupView();
|
|
|
|
if (iliStart == iliEnd)
|
|
{
|
|
// Entire selection is on the same line so we want to display as
|
|
// much of it as is possible.
|
|
LONG xWidthSel = ptEnd.u - ptStart.u;
|
|
|
|
if (xWidthSel > dupView)
|
|
{
|
|
// Selection length is greater than display width
|
|
if (tomStart == lCode)
|
|
{
|
|
// Show Start requested - just start from beginning
|
|
// of selection
|
|
upScroll = ptStart.u;
|
|
}
|
|
else
|
|
{
|
|
// Show end requested - show as much of selection as
|
|
// possible, ending with last character in the
|
|
// selection.
|
|
upScroll = ptEnd.u - dupView;
|
|
}
|
|
}
|
|
else if (xWidthSel < 0)
|
|
{
|
|
xWidthSel = -xWidthSel;
|
|
if (xWidthSel > dupView)
|
|
{
|
|
if (tomStart == lCode)
|
|
{
|
|
// Show Requested Start;
|
|
upScroll = max(0, ptStart.u - dupView);
|
|
}
|
|
else
|
|
{
|
|
upScroll = max(0, ptEnd.u - dupView);
|
|
}
|
|
}
|
|
else if (ptEnd.u > dupView || ptStart.u > dupView)
|
|
{
|
|
// Check mask if position is outside the boundaries
|
|
if (taMask)
|
|
upScroll = ptStart.u - dupView;
|
|
else
|
|
upScroll = ptEnd.u - dupView;
|
|
}
|
|
}
|
|
else if (ptEnd.u > dupView || ptStart.u > dupView)
|
|
{
|
|
// Check mask if position is outside the boundaries
|
|
if (taMask)
|
|
upScroll = ptStart.u - dupView;
|
|
else
|
|
upScroll = ptEnd.u - dupView;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Multiline selection. Display as much as possible of the requested
|
|
// end's line.
|
|
|
|
// Calc width of line
|
|
LONG xWidthLine = (tomStart == lCode)
|
|
? rpStart->_dup + rpStart->_upStart
|
|
: rpEnd->_dup + rpEnd->_upStart;
|
|
|
|
|
|
// If line width is less than or equal to view, start at
|
|
// 0 otherwise we need to adjust starting position to
|
|
// show as much of the requested end's selection line
|
|
// as possible.
|
|
if(xWidthLine > dupView)
|
|
{
|
|
if(tomStart == lCode)
|
|
{
|
|
// Start end to be displayed
|
|
|
|
if(xWidthLine - ptStart.u > dupView)
|
|
{
|
|
// Selection is bigger than view, so start at beginning
|
|
// and display as much as possible.
|
|
upScroll = ptStart.u;
|
|
}
|
|
else
|
|
{
|
|
// Remember that this is a multiline selection so the
|
|
// selection on this line goes from ptStart.x to the
|
|
// end of line. Since the selection width is less than
|
|
// the width of the view, we just back up the width
|
|
// of view to show the entire selection.
|
|
upScroll = xWidthLine - dupView;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Show the end of the selection. In the multiline case,
|
|
// this goes from the beginning of the line to End. So
|
|
// we only have to adjust if the End is beyond the view.
|
|
if(ptEnd.u > dupView)
|
|
{
|
|
// End beyond the view. Show as much as possible
|
|
// of the selection.
|
|
upScroll = ptEnd.u - dupView;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Do the scroll
|
|
pdp->ScrollView(upScroll, vpScroll, FALSE, FALSE);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Select ()
|
|
*
|
|
* @mfunc
|
|
* Copy this range's cp's and story ptr to the active selection.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if selection exists) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::Select ()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Select");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
CTxtSelection *pSel = GetPed()->GetSel();
|
|
|
|
if(pSel)
|
|
{
|
|
LONG cpMin, cpMost;
|
|
GetRange(cpMin, cpMost);
|
|
if (pSel->SetRange(cpMin, cpMost) == S_FALSE)
|
|
pSel->Update(TRUE); // Force a update selection
|
|
return NOERROR;
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::SetChar (Char)
|
|
*
|
|
* @mfunc
|
|
* Set char at cpFirst = <p Char>
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (WriteAccessDenied) ? E_ACCESSDENIED :
|
|
* (char stored) ? NOERROR : S_FALSE
|
|
*
|
|
* @devnote
|
|
* Special cases could be much faster, e.g., just overtype the plain-
|
|
* text backing store unless at EOD or EOR. Code below uses a cloned
|
|
* range to handle all cases easily and preserve undo capability.
|
|
*/
|
|
STDMETHODIMP CTxtRange::SetChar (
|
|
long Char) //@parm New value for char at cpFirst
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetChar");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CTxtEdit * ped = GetPed();
|
|
CCallMgr callmgr(ped);
|
|
WCHAR ch = (WCHAR)Char; // Avoid endian problems
|
|
CTxtRange rg(*this);
|
|
IUndoBuilder * publdr;
|
|
CGenUndoBuilder undobldr(ped, UB_AUTOCOMMIT, &publdr);
|
|
CFreezeDisplay fd(ped->_pdp);
|
|
|
|
if(WriteAccessDenied())
|
|
return E_ACCESSDENIED;
|
|
|
|
if(!ped->_pdp->IsMultiLine() && IsEOP(Char))// EOPs are not allowed in
|
|
return S_FALSE; // single-line edit controls
|
|
|
|
if(Char == CELL || IN_RANGE(STARTFIELD, Char, NOTACHAR))
|
|
return S_FALSE; // Can't insert table structure
|
|
// chars
|
|
undobldr.StopGroupTyping();
|
|
|
|
rg.Collapser(tomStart); // Collapse at cpMin
|
|
|
|
unsigned ch1 = rg._rpTX.GetChar();
|
|
if(ch1 == CELL || IN_RANGE(STARTFIELD, ch1, NOTACHAR))
|
|
return S_FALSE; // Can't replace table structure
|
|
// chars
|
|
rg.Move(1, TRUE); // Try to select char at IP
|
|
ped->OrCharFlags(GetCharFlags(&ch, 1), publdr);
|
|
if(rg.ReplaceRange(1, &ch, publdr, SELRR_REMEMBERRANGE))
|
|
{
|
|
Update(TRUE); // Update selection
|
|
return NOERROR;
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::SetEnd (cp)
|
|
*
|
|
* @mfunc
|
|
* Set this range's End cp
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR : S_FALSE
|
|
*
|
|
* @comm
|
|
* Note that setting this range's cpMost to <p cp> also sets cpMin to
|
|
* <p cp> if <p cp> < cpMin.
|
|
*/
|
|
STDMETHODIMP CTxtRange::SetEnd (
|
|
long cp) //@parm Desired new End cp
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetEnd");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
LONG cpMin = GetCpMin();
|
|
|
|
ValidateCp(cp);
|
|
return SetRange(min(cpMin, cp), cp); // Active end is End
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::SetFont (pFont)
|
|
*
|
|
* @mfunc
|
|
* Set this range's character attributes to those given by <p pFont>.
|
|
* This method is a "character format painter".
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (!pFont) ? E_INVALIDARG :
|
|
* (if success) ? NOERROR :
|
|
* (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
|
|
*/
|
|
STDMETHODIMP CTxtRange::SetFont (
|
|
ITextFont * pFont) //@parm Font object with desired character formatting
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetFont");
|
|
|
|
if(!pFont)
|
|
return E_INVALIDARG;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
ITextFont *pFontApply = (ITextFont *) new CTxtFont(this);
|
|
|
|
if(!pFontApply)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr;
|
|
if(*(LONG *)pFontApply == *(LONG *)pFont) // If same vtable, use
|
|
hr = CharFormatSetter(&((CTxtFont *)pFont)->_CF, // its copy
|
|
((CTxtFont *)pFont)->_dwMask);
|
|
else // Else copy
|
|
hr = pFontApply->SetDuplicate(pFont); // to clone and apply
|
|
|
|
pFontApply->Release();
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::SetFormattedText (pRange)
|
|
*
|
|
* @mfunc
|
|
* Replace this range's text with formatted text given by <p pRange>.
|
|
* If <p pRange> is NULL, paste from the clipboard.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (WriteAccessDenied) ? E_ACCESSDENIED :
|
|
* (if success) ? NOERROR : E_OUTOFMEMORY
|
|
*
|
|
* @FUTURE
|
|
* Do this more efficiently if pRange points at a RichEdit range. This
|
|
* would also help with RichEdit D&D to RichEdit targets
|
|
*/
|
|
STDMETHODIMP CTxtRange::SetFormattedText (
|
|
ITextRange * pRange) //@parm Formatted text to replace this
|
|
// range's text
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetFormattedText");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
LONG cpMin = GetCpMin();
|
|
HRESULT hr;
|
|
IUnknown * pdo = NULL;
|
|
VARIANT vr;
|
|
|
|
if(!pRange)
|
|
return NOERROR; // Nothing to paste
|
|
|
|
if(WriteAccessDenied())
|
|
return E_ACCESSDENIED;
|
|
|
|
VariantInit(&vr);
|
|
vr.vt = VT_UNKNOWN | VT_BYREF;
|
|
vr.ppunkVal = &pdo;
|
|
|
|
hr = pRange->Copy(&vr);
|
|
if(hr == NOERROR)
|
|
{
|
|
hr = Paste(&vr, 0);
|
|
pdo->Release(); // Release the data object
|
|
_cch = GetCp() - cpMin; // Select the new text
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::SetIndex (Unit, Index, Extend)
|
|
*
|
|
* @mfunc
|
|
* If <p Extend> is zero, convert this range into an insertion point
|
|
* at the start of the <p Index>th <p Unit> in the current story. If
|
|
* <p Extend> is nonzero, set this range to consist of this unit. The
|
|
* start of the story corresponds to <p Index> = 0 for all units.
|
|
*
|
|
* Positive indices are 1-based and index relative to the beginning of
|
|
* the story. Negative indices are -1-based and index relative to the
|
|
* end of the story. So an index of 1 refers to the first Unit in the
|
|
* story and an index of -1 refers to the last Unit in the story.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (invalid index) ? E_INVALIDARG :
|
|
* (Unit not supported) ? E_NOTIMPL :
|
|
* (change) ? NOERROR : S_FALSE
|
|
*
|
|
* @devnote
|
|
* Currently moves out <p Index> <p Unit>s from the start of the story.
|
|
* Might be faster to move from current position, but would need to know
|
|
* the current index.
|
|
*/
|
|
STDMETHODIMP CTxtRange::SetIndex (
|
|
long Unit, //@parm Unit to index
|
|
long Index, //@parm Index value to use
|
|
long Extend) //@parm if nonzero, set range to <p Unit>
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetIndex");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
if(!Index)
|
|
return E_INVALIDARG;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
|
|
LONG cchText = GetTextLength();
|
|
CTxtRange rg(GetPed()); // Create IP at cp = 0
|
|
|
|
if(Index > 0) // Index going forward First
|
|
Index--; // Unit is at start of story
|
|
else // Index from end of story
|
|
rg.Set(cchText, cchText); // selecting whole story
|
|
|
|
LONG cUnit;
|
|
HRESULT hr = rg.Mover(Unit, Index, &cUnit, MOVE_END);
|
|
if(FAILED(hr))
|
|
return hr;
|
|
|
|
if(Index != cUnit || rg.GetCp() == cchText) // No such index in story
|
|
return E_INVALIDARG;
|
|
|
|
rg._cch = 0; // Collapse at active end
|
|
// namely at cpMost
|
|
LONG cpMin, cpMost;
|
|
if(Extend) // Select Index'th Unit
|
|
rg.Expander(Unit, TRUE, NULL, &cpMin, &cpMost);
|
|
|
|
if(Set(rg.GetCp(), rg._cch)) // Something changed
|
|
{
|
|
Update(TRUE);
|
|
return NOERROR;
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::SetPara (pPara)
|
|
*
|
|
* @mfunc
|
|
* Set this range's paragraph attributes to those given by <p pPara>
|
|
* This method is a "Paragraph format painter".
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (!pPara) ? E_INVALIDARG :
|
|
* (if success) ? NOERROR :
|
|
* (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
|
|
*/
|
|
STDMETHODIMP CTxtRange::SetPara (
|
|
ITextPara * pPara) //@parm Paragraph object with desired paragraph
|
|
{ // formatting
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetPara");
|
|
|
|
if(!pPara)
|
|
return E_INVALIDARG;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
ITextPara * pParaApply = (ITextPara *) new CTxtPara(this);
|
|
|
|
if(!pParaApply)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr;
|
|
|
|
if(*(LONG *)pParaApply == *(LONG *)pPara) // If same vtable, use
|
|
{ // its _PF
|
|
hr = ParaFormatSetter(&((CTxtPara *)pPara)->_PF,
|
|
((CTxtPara *)pPara)->_dwMask);
|
|
}
|
|
else // Else copy
|
|
hr = pParaApply->SetDuplicate(pPara); // to clone and apply
|
|
|
|
pParaApply->Release();
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::SetPoint (x, y, Type, Extend)
|
|
*
|
|
* @mfunc
|
|
* Select text at or up through (depending on <p Extend>) the point
|
|
* (<p x>, <p y>).
|
|
*
|
|
* @rdesc
|
|
* HRESULT = NOERROR
|
|
*/
|
|
STDMETHODIMP CTxtRange::SetPoint (
|
|
long x, //@parm Horizontal coord of point to select
|
|
long y, //@parm Vertical coord of point to select
|
|
long Type, //@parm Defines the end to extend if Extend != 0.
|
|
long Extend) //@parm Whether to extend selection to point
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetPoint");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
// Copy the ped locally once to save some indirections
|
|
CTxtEdit *ped = GetPed();
|
|
CCallMgr callmgr(ped);
|
|
|
|
if(Type != tomStart && Type != tomEnd)
|
|
return E_INVALIDARG;
|
|
|
|
if(!ped->fInplaceActive())
|
|
{
|
|
// If we aren't inplace active we can't get a DC to
|
|
// calculate the cp.
|
|
return OLE_E_NOT_INPLACEACTIVE;
|
|
}
|
|
|
|
// Convert (x, y) from screen coordinates to client coordinates
|
|
POINT ptxy = {x, y};
|
|
// Caller specifies screen coordinates?
|
|
if ( !(Type & tomClientCoord) )
|
|
if(!ped->TxScreenToClient(&ptxy))
|
|
return E_FAIL; // It is unexpected for this to happen
|
|
|
|
// Get cp for (x, y)
|
|
POINTUV pt;
|
|
ped->_pdp->PointuvFromPoint(pt, ptxy);
|
|
LONG cpSel = ped->_pdp->CpFromPoint(pt, NULL, NULL, NULL, TRUE);
|
|
if(cpSel == -1)
|
|
return E_FAIL; // It is highly unexpected for this to fail
|
|
|
|
// Extend range as requested
|
|
LONG cchForSel = 0;
|
|
if(Extend)
|
|
{
|
|
LONG cpMin, cpMost;
|
|
GetRange(cpMin, cpMost);
|
|
if(Type == tomStart)
|
|
cchForSel = cpSel - cpMin;
|
|
else
|
|
cchForSel = cpSel - cpMost;
|
|
}
|
|
|
|
// Update range
|
|
Set(cpSel, cchForSel);
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::SetRange (cp1, cp2)
|
|
*
|
|
* @mfunc
|
|
* Set this range's ends
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (cp1 > cp2) ? E_INVALIDARG
|
|
* : (if change) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::SetRange (
|
|
long cp1, //@parm Char position for Start end
|
|
long cp2) //@parm Char position for End end
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetRange");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
LONG cpMin, cpMost; // Save starting cp's for
|
|
// change determination
|
|
GetRange(cpMin, cpMost);
|
|
ValidateCp(cp1);
|
|
ValidateCp(cp2);
|
|
|
|
Set(cp2, cp2 - cp1);
|
|
GetRange(cp1, cp2); // See if either range end changed
|
|
if(cp1 != cpMin || cp2 != cpMost) // (independent of active end)
|
|
{
|
|
Update(TRUE); // Update selection
|
|
return NOERROR;
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::SetStart (cp)
|
|
*
|
|
* @mfunc
|
|
* Set this range's Start cp
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR : S_FALSE
|
|
*
|
|
* @comm
|
|
* Note that setting this range's cpMin to <p cp> also sets cpMost to
|
|
* <p cp> if <p cp> > cpMost.
|
|
*/
|
|
STDMETHODIMP CTxtRange::SetStart (
|
|
long cp) //@parm Desired new Start cp
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetStart");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
LONG cpMost = GetCpMost();
|
|
|
|
ValidateCp(cp);
|
|
return SetRange(max(cpMost, cp), cp); // Active end is Start
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::SetText (bstr)
|
|
*
|
|
* @mfunc
|
|
* Replace text in this range by that given by <p bstr>. If <p bstr>
|
|
* is NULL, delete text in range.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (WriteAccessDenied) ? E_ACCESSDENIED :
|
|
* (if success) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtRange::SetText (
|
|
BSTR bstr) //@parm Text to replace text in this range by
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetText");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
|
|
if(WriteAccessDenied())
|
|
return E_ACCESSDENIED;
|
|
|
|
LONG cchNew = bstr ? SysStringLen(bstr) : 0;
|
|
_cch = Replacer(cchNew, (WCHAR *)bstr, RR_ITMZ_UNICODEBIDI); // Select the new text
|
|
|
|
_TEST_INVARIANT_
|
|
|
|
GetPed()->TxSetMaxToMaxText();
|
|
|
|
return _cch == cchNew ? NOERROR : S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::StartOf (Unit, Extend, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Move this range end(s) to start of the first overlapping Unit in
|
|
* the range.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if <p Unit> valid) ? S_FALSE : E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtRange::StartOf (
|
|
long Unit, //@parm Unit to use
|
|
long Extend, //@parm If true, leave other end alone
|
|
long * pDelta) //@parm Out parm to get count of chars that
|
|
// StartOf moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::StartOf");
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
LONG cpMin;
|
|
HRESULT hr = Expander (Unit, Extend, pDelta, &cpMin, NULL);
|
|
|
|
if(hr == NOERROR)
|
|
Update(TRUE); // Update selection
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//---------------------- CTxtRange ITextSelection stubs -----------------------------
|
|
|
|
// Dummy CTxtRange routines to simplify CTxtSelection inheritance hierarchy
|
|
|
|
STDMETHODIMP CTxtRange::GetFlags (long * pFlags)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetFlags");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CTxtRange::SetFlags (long Flags)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::SetFlags");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CTxtRange::GetType (long * pType)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::GetType");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CTxtRange::MoveLeft (long Unit, long Count, long Extend, long * pDelta)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Left");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CTxtRange::MoveRight (long Unit, long Count, long Extend, long * pDelta)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Right");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CTxtRange::MoveUp (long Unit, long Count, long Extend, long * pDelta)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Up");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CTxtRange::MoveDown (long Unit, long Count, long Extend, long * pDelta)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::Down");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CTxtRange::HomeKey (long Unit, long Extend, long * pDelta)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::HomeKey");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CTxtRange::EndKey (long Unit, long Extend, long * pDelta)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::EndKey");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CTxtRange::TypeText (BSTR bstr)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtRange::TypeText");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
//--------------------- ITextRange Private Helper Methods -----------------------------
|
|
|
|
/*
|
|
* @doc INTERNAL
|
|
*
|
|
* CTxtRange::Collapser (bStart)
|
|
*
|
|
* @mfunc
|
|
* Internal routine to collapse this range into a degenerate point
|
|
* either at the the start (<p bStart> is nonzero or the end
|
|
* (<p bStart> = 0)
|
|
*/
|
|
void CTxtRange::Collapser (
|
|
long bStart) //@parm Flag specifying end to collapse at
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEEXTERN, "CTxtRange::Collapser");
|
|
|
|
if(bStart) // Collapse to Start
|
|
{
|
|
if(_cch > 0)
|
|
FlipRange(); // Move active end to range Start
|
|
}
|
|
else // Collapse to End
|
|
{
|
|
if(_cch < 0)
|
|
FlipRange(); // Move active end to range End
|
|
|
|
const LONG cchText = GetAdjustedTextLength();
|
|
|
|
if(GetCp() > cchText) // IP can't follow final CR
|
|
Set(cchText, 0); // so move it before
|
|
}
|
|
if(_cch)
|
|
_fMoveBack = bStart != 0;
|
|
_cch = 0; // Collapse this range
|
|
_fSelHasEOP = FALSE; // Insertion points don't have
|
|
_fSelExpandCell = FALSE; // EOPs, table rows or Cells
|
|
_nSelExpandLevel = 0;
|
|
|
|
if(_fSel) // Notify if selection changed
|
|
GetPed()->GetCallMgr()->SetSelectionChanged();
|
|
|
|
Update_iFormat(-1); // Make sure format is up to date
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Comparer(pRange)
|
|
*
|
|
* @mfunc
|
|
* helper function for CTxtRange::InRange() and IsEqual()
|
|
*
|
|
* @rdesc
|
|
* 0 if not same story or if this range isn't contained by <p pRange>;
|
|
* -1 if ranges are equal; 1 if this range wholely contained in
|
|
* <p pRange>.
|
|
*
|
|
* @comm
|
|
* Note that if this range is degenerate and *pRange is nondegenerate,
|
|
* this range is not included in *pRange if it's located at pRange's
|
|
* End position.
|
|
*/
|
|
LONG CTxtRange::Comparer (
|
|
ITextRange * pRange) //@parm ITextRange to compare with
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::Comparer");
|
|
|
|
LONG cpMin, cpMost;
|
|
LONG Start, End;
|
|
|
|
if(InStory(pRange, NULL) != NOERROR) // If this range doesn't point at
|
|
return 0; // same story as pRange's,
|
|
// return 0
|
|
GetRange(cpMin, cpMost); // Get this range's cp's
|
|
pRange->GetStart(&Start); // Get pRange's cp's
|
|
pRange->GetEnd(&End);
|
|
if(cpMin == Start && cpMost == End) // Exact match
|
|
return -1;
|
|
return cpMin >= Start && cpMost <= End && cpMin < End;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Expander (Unit, fExtend, pDelta, pcpMin, pcpMost)
|
|
*
|
|
* @mfunc
|
|
* Helper function that expands this range so that partial Units it
|
|
* contains are completely contained according to the out parameters
|
|
* pcpMin and pcpMost. If pcpMin is not NULL, the range is expanded to
|
|
* the beginning of the Unit. Similarly, if pcpMost is not NULL, the
|
|
* range is expanded to the end of the Unit. <p pDelta> is an out
|
|
* parameter that receives the number of chars added to the range.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if Unit valid) ? S_FALSE : E_INVALIDARG
|
|
*
|
|
* @devnote
|
|
* Used by ITextRange::Expand(), StartOf(), and EndOf(). Both pcpMin and
|
|
* pcpMost are nonNULL for Expand(). pcpMin is NULL for EndOf() and
|
|
* pcpMost is NULL for StartOf().
|
|
*
|
|
* @future
|
|
* Discontiguous Units. Expander should expand only to end of Unit,
|
|
* rather than to start of next Unit.
|
|
*/
|
|
HRESULT CTxtRange::Expander (
|
|
long Unit, //@parm Unit to expand range to
|
|
BOOL fExtend, //@parm Expand this range if TRUE
|
|
LONG * pDelta, //@parm Out parm that receives chars added
|
|
LONG * pcpMin, //@parm Out parm that receives new cpMin
|
|
LONG * pcpMost) //@parm Out parm that receives new cpMost
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::Expander");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
LONG cch = 0; // Default no chars added
|
|
LONG cchRange;
|
|
LONG cchAdjustedText = GetAdjustedTextLength();
|
|
LONG cchText = GetTextLength();
|
|
LONG cp;
|
|
LONG cpMin, cpMost;
|
|
BOOL fUnitFound = TRUE; // Most Units can be found
|
|
LONG cchCollapse;
|
|
CDisplay *pdp; // but tomObject maybe not
|
|
|
|
GetRange(cpMin, cpMost); // Save starting cp's
|
|
if(pcpMin) // Default no change
|
|
{
|
|
*pcpMin = cpMin;
|
|
AssertSz(!pcpMost || fExtend,
|
|
"CTxtRange::Expander should extend if both pcpMin and pcpMost != 0");
|
|
}
|
|
if(pcpMost)
|
|
*pcpMost = cpMost;
|
|
if(pDelta)
|
|
*pDelta = 0;
|
|
|
|
if(Unit < 0)
|
|
{
|
|
// Valid attribute Units are high bit plus any combo of CFE_xxx.
|
|
// CFE_REVISED is most significant value currently defined.
|
|
if(Unit & ~(2*CFM_REVISED - 1 + 0x80000000))
|
|
return E_NOTIMPL;
|
|
FindAttributes(pcpMin, pcpMost, Unit);
|
|
}
|
|
else
|
|
{
|
|
switch(Unit) // Calculate new cp's
|
|
{
|
|
case tomObject:
|
|
fUnitFound = FindObject(pcpMin, pcpMost);
|
|
break;
|
|
|
|
case tomCharacter:
|
|
if (pcpMost && cpMin == cpMost &&// EndOf/Expand insertion point
|
|
cpMost < cchText && // with at least 1 more char
|
|
(!cpMost || pcpMin)) // at beginning of story or
|
|
{ // Expand(), then
|
|
(*pcpMost)++; // expand by one char
|
|
}
|
|
break;
|
|
|
|
case tomCharFormat:
|
|
_rpCF.FindRun (pcpMin, pcpMost, cpMin, _cch, cchText);
|
|
break;
|
|
|
|
case tomParaFormat:
|
|
_rpPF.FindRun (pcpMin, pcpMost, cpMin, _cch, cchText);
|
|
break;
|
|
|
|
case tomWord:
|
|
FindWord (pcpMin, pcpMost, FW_INCLUDE_TRAILING_WHITESPACE);
|
|
break;
|
|
|
|
case tomSentence:
|
|
FindSentence (pcpMin, pcpMost);
|
|
break;
|
|
|
|
case tomCell:
|
|
FindCell (pcpMin, pcpMost);
|
|
break;
|
|
|
|
case tomRow:
|
|
pdp = GetPed()->_pdp;
|
|
if(!IsRich() || pdp && !pdp->IsMultiLine())
|
|
return E_INVALIDARG;
|
|
FindRow (pcpMin, pcpMost);
|
|
break;
|
|
|
|
case tomScreen: // Could be supported
|
|
if(!GetPed()->IsInPageView()) // in Normal View using
|
|
return E_NOTIMPL; // ITextSelection::Down()
|
|
Unit = tomPage; // In Page View, it's an alias
|
|
// for tomPage
|
|
case tomPage:
|
|
case tomLine:
|
|
pdp = GetPed()->_pdp;
|
|
if(pdp) // If this story has a display
|
|
{ // use line array
|
|
CLinePtr rp(pdp);
|
|
cp = GetCp();
|
|
pdp->WaitForRecalc(cp, -1);
|
|
rp.SetCp(cp, FALSE);
|
|
if(Unit == tomLine || !rp.IsValid() || !pdp->IsInPageView())
|
|
rp.FindRun (pcpMin, pcpMost, cpMin, _cch, cchText);
|
|
else
|
|
rp.FindPage(pcpMin, pcpMost, cpMin, _cch, cchText);
|
|
break;
|
|
}
|
|
if(Unit == tomPage)
|
|
return S_FALSE;
|
|
// Else fall thru to tomPara
|
|
case tomParagraph:
|
|
FindParagraph(pcpMin, pcpMost);
|
|
break;
|
|
|
|
case tomWindow:
|
|
fUnitFound = FindVisibleRange(pcpMin, pcpMost);
|
|
break;
|
|
|
|
case tomStory:
|
|
if(pcpMin)
|
|
*pcpMin = 0;
|
|
if(pcpMost)
|
|
*pcpMost = cchText;
|
|
break;
|
|
|
|
default:
|
|
return E_NOTIMPL;
|
|
}
|
|
}
|
|
if(!fUnitFound)
|
|
return S_FALSE;
|
|
|
|
cchCollapse = !fExtend && _cch; // Collapse counts as a char
|
|
// Note: Expand() has fExtend = 0
|
|
if(pcpMin)
|
|
{
|
|
cch = cpMin - *pcpMin; // Default positive cch for Expand
|
|
cpMin = *pcpMin;
|
|
}
|
|
|
|
if(pcpMost) // EndOf() and Expand()
|
|
{
|
|
if(!fExtend) // Will be IP if not already
|
|
{
|
|
if(cpMost > cchAdjustedText) // If we collapse (EndOf only),
|
|
cchCollapse = -cchCollapse; // it'll be before the final CR
|
|
else
|
|
*pcpMost = min(*pcpMost, cchAdjustedText);
|
|
}
|
|
cch += *pcpMost - cpMost;
|
|
cp = cpMost = *pcpMost;
|
|
}
|
|
else // StartOf()
|
|
{
|
|
cch = -cch; // Invert count
|
|
cp = cpMin; // Active end at cpMin
|
|
cchCollapse = -cchCollapse; // Backward collapses count as -1
|
|
}
|
|
|
|
cch += cchCollapse; // Collapse counts as a char
|
|
if(cch) // One or both ends changed
|
|
{
|
|
cchRange = cpMost - cpMin; // cch for EndOf() and Expand()
|
|
if(!pcpMost) // Make negative for StartOf()
|
|
cchRange = -cchRange;
|
|
if(!fExtend) // We're not expanding (EndOf()
|
|
cchRange = 0; // or StartOf() call)
|
|
if(Set(cp, cchRange)) // Set active end and signed cch
|
|
{ // Something changed
|
|
if(pDelta) // Report cch if caller cares
|
|
*pDelta = cch;
|
|
return NOERROR;
|
|
}
|
|
}
|
|
|
|
return S_FALSE; // Report Unit found but no change
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Finder (bstr, Count, dwFlags, pDelta, Mode)
|
|
*
|
|
* @mfunc
|
|
* Helper find function that moves active end up to <p cch> characters
|
|
* subject to compare flags <p Flags> and the <p Mode>, which has the
|
|
* following possible values:
|
|
*
|
|
* 1: set this range's cpMost = cpMost of matched string
|
|
* 0: set this range's cp's equal to those of matched string
|
|
* -1: set this range's cpMin = cpMin of matched string
|
|
*
|
|
* Return *<p pDelta> = # characters past.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if <p bstr> found) ? NOERROR : S_FALSE
|
|
*
|
|
* @devnote
|
|
* Used by ITextRange::FindText(), FindTextStart() and FindTextEnd()
|
|
*/
|
|
HRESULT CTxtRange::Finder (
|
|
BSTR bstr, //@parm String to find
|
|
long Count, //@parm Max count of chars to search
|
|
long Flags, //@parm Flags governing compares
|
|
LONG * pDelta, //@parm Out parm to receive count of chars moved
|
|
MOVES Mode) //@parm Governs setting of range wrt matched string
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::Finder");
|
|
|
|
if(!bstr)
|
|
return S_FALSE;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
|
|
LONG cpMin, cpMost;
|
|
LONG cch = GetRange(cpMin, cpMost); // Get this range's cp's
|
|
LONG cchBstr = SysStringLen(bstr);
|
|
LONG cchSave = _cch;
|
|
LONG cp, cpMatch, cpSave;
|
|
LONG cpStart = cpMost; // Default Start cp to range
|
|
CRchTxtPtr rtp(*this); // End
|
|
|
|
if(Mode == MOVE_IP) // FindText(): Count = 0 is
|
|
{ // treated specially: if IP,
|
|
if(!Count) // compare string at IP; else
|
|
Count = cch ? cch : cchBstr; // confine search to range
|
|
if(Count > 0) // Forward searches start from
|
|
cpStart = cpMin; // beginning of range
|
|
}
|
|
else // FindTextStart() or
|
|
{ // FindTextEnd()
|
|
if(!Count) // Compare string at IP; else
|
|
Count = cch ? -Mode*cch : cchBstr; // confine search to range
|
|
if(Mode < 0) // Find from Start
|
|
cpStart = cpMin;
|
|
}
|
|
|
|
cpSave = cpStart; // Save starting cp
|
|
cp = cpStart + Count; // cp = limiting cp. Can be on
|
|
cp = max(cp, 0); // either side of cpStart
|
|
Flags &= ~FR_DOWN; // Default search backward
|
|
if(Count >= 0) // It's forward, so set
|
|
Flags |= FR_DOWN; // downward search bit
|
|
|
|
find:
|
|
rtp.SetCp(cpStart); // Move to start of search
|
|
cpMatch = rtp.FindText(cp, Flags, bstr, cchBstr);
|
|
if (Mode == MOVE_IP && cpMatch == cpMin && // Ordinary Find matched
|
|
rtp.GetCp() == cpMost) // current range
|
|
{
|
|
Assert(cpStart == cpSave); // (Can't loop twice)
|
|
cpStart += Count > 0 ? 1 : -1; // Move over one char
|
|
goto find; // and try again
|
|
}
|
|
|
|
if(cpMatch < 0) // Match failed
|
|
{
|
|
if(pDelta) // Return match string length
|
|
*pDelta = 0; // = 0
|
|
return S_FALSE; // Signal no match
|
|
}
|
|
|
|
|
|
// Match succeeded: set new cp and cch for range, update selection (if
|
|
// this range is a selection), send notifications, and return NOERROR
|
|
|
|
cp = rtp.GetCp(); // cp = cpMost of match string
|
|
cch = cp - cpMatch; // Default to select matched
|
|
// string (for MOVE_IP)
|
|
if(pDelta) // Return match string length
|
|
*pDelta = cch; // if caller wants to know
|
|
|
|
if(Mode != MOVE_IP) // MOVE_START or MOVE_END
|
|
{
|
|
if(Mode == MOVE_START) // MOVE_START moves to start
|
|
cp = cpMatch; // of matched string
|
|
cch = cp - cpSave; // Distance end moved
|
|
if(!cchSave && (Mode ^ cch) < 0) // If crossed ends of initial
|
|
cch = 0; // IP, use an IP
|
|
else if(cchSave) // Initially nondegenerate
|
|
{ // range
|
|
if((cchSave ^ Mode) < 0) // If wrong end is active,
|
|
cchSave = -cchSave; // fake a FlipRange to get
|
|
cch += cchSave; // new length
|
|
if((cch ^ cchSave) < 0) // If ends would cross,
|
|
cch = 0; // convert to insertion point
|
|
}
|
|
}
|
|
if ((cp != GetCp() || cch != _cch) // Active end and/or length of
|
|
&& Set(cp, cch)) // range changed
|
|
{ // Use the new values
|
|
Update(TRUE); // Update selection
|
|
}
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Matcher (Cset, Count, pDelta, fExtend, Match)
|
|
*
|
|
* @mfunc
|
|
* Helper function to move active end up to <p cch> characters past
|
|
* all contiguous characters that are (<p Match> ? in : not in) the cset
|
|
* *<p pvar>. If <p fExtend>, extend the range to include the characters
|
|
* past by. Return *<p pDelta> = # characters past by.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if <p Cset> valid) ? S_FALSE : E_INVALIDARG
|
|
*/
|
|
HRESULT CTxtRange::Matcher (
|
|
VARIANT * Cset, //@parm Character match set
|
|
long Count, //@parm Max cch to match
|
|
long * pDelta, //@parm Out parm for cch moved
|
|
MOVES Mode, //@parm MOVE_START (-1), MOVE_IP (0), MOVE_END (1)
|
|
MATCHES Match) //@parm MATCH_WHILE spans Cset; else break on Cset
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::Matcher");
|
|
|
|
// This (and other) code assumes the following conditions:
|
|
Assert(MOVE_START == -1 && MOVE_IP == 0 && MOVE_END == 1);
|
|
Assert(MATCH_UNTIL == 0 && MATCH_WHILE == 1);
|
|
Assert(sizeof(WORD) == 2); // 16-bit WORDs
|
|
|
|
if(!Cset)
|
|
return E_INVALIDARG;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
LONG cch; // For cch moved
|
|
WCHAR ch; // Current char
|
|
LONG count = Count; // Count down variable
|
|
LONG cpSave; // To save initial cp
|
|
WORD ctype; // CT_TYPEx info for ch
|
|
long Delta; // Value for *pDelta
|
|
BOOL fInCset; // TRUE iff ch in Cset
|
|
UINT i, j; // Handy indices
|
|
LONG iDir = (Count > 0) ? 1 : -1; // Count increment
|
|
long lVal = Cset->lVal;
|
|
WCHAR * pch; // Used to walk BSTR Cset
|
|
CTxtPtr tp(_rpTX); // tp to walk text with
|
|
LONG vt = Cset->vt;
|
|
|
|
if(pDelta) // Default neither motion
|
|
*pDelta = 0; // nor match
|
|
|
|
if (Mode == MOVE_IP && (_cch ^ Count) < 0 || // Wrong active end:
|
|
Mode != MOVE_IP && (_cch ^ Mode) < 0)
|
|
{
|
|
tp.Move(-_cch); // go to other end
|
|
}
|
|
cpSave = tp.GetCp(); // Save cp for checks
|
|
|
|
if(Count > 0) // If matching forward,
|
|
{ // get current char
|
|
ch = tp.GetChar();
|
|
count--; // One less char to match
|
|
}
|
|
else // If matching backward,
|
|
ch = tp.NextCharCount(count); // start at previous char
|
|
|
|
if(!ch) // At one end or other, so
|
|
return S_FALSE; // signal no match
|
|
|
|
|
|
// Process built-in and explicit character sets
|
|
if(vt & VT_BYREF) // VB passes VT_BYREF
|
|
{ // unless args are
|
|
lVal = *Cset->plVal; // enclosed in ()'s
|
|
vt &= ~ VT_BYREF;
|
|
}
|
|
|
|
if(vt == VT_I2) // Should be VT_I4, but
|
|
lVal &= 0xffff; // facilitate common cases
|
|
|
|
// Built-in char set: either Unicode range or CT_CTYPEx
|
|
if(vt == VT_I4 || vt == VT_I2)
|
|
{
|
|
i = lVal & 0xffff; // First code or CT mask
|
|
j = lVal >> 16; // Size of range
|
|
if(lVal < 0) // Unicode range Cset
|
|
{ // (sign bit is set)
|
|
j &= 0x7fff; // Kill sign bit
|
|
while (((BOOL)Match ^ (ch - i > j)) && // ch in range or not
|
|
(ch = tp.NextCharCount(count))) // Another char available
|
|
; // Note: count is passed
|
|
} // by reference
|
|
else // CT_CTYPEx Cset
|
|
{ // CT_CTYPEx is given by
|
|
if(!j) // upper WORD of lVal
|
|
j = CT_CTYPE1; // 0 defaults to CT_CTYPE1
|
|
do
|
|
{
|
|
ctype = 0; // For each char, get
|
|
// string type info
|
|
W32->GetStringTypeEx(0, j, &ch, 1, &ctype);
|
|
|
|
// Loop (up to |Count| - 1 times) as long as the characters
|
|
// encountered are in the Cset (Match = MATCH_WHILE (=1)),
|
|
// or as long as they are not (Match = MATCH_UNTIL (=0)).
|
|
|
|
fInCset = (j == CT_CTYPE2) // CT_CTYPE2 values are
|
|
? (ctype == i) // mutually exclusive;
|
|
: (ctype & i) != 0; // others can be combos
|
|
|
|
} while ((Match ^ fInCset) == 0 &&
|
|
(ch = tp.NextCharCount(count)) != 0);
|
|
} // End of built-in Csets
|
|
} // End of Cset VT_I4
|
|
|
|
// Explicit char set given by chars in Cset->bstrVal
|
|
else if (Cset->vt == VT_BSTR)
|
|
{
|
|
//REVIEW (keithcu) What is going on here?
|
|
if((DWORD_PTR)Cset->bstrVal < 0xfffff) // Don't get fooled by
|
|
return E_INVALIDARG; // invalid vt values
|
|
j = SysStringLen(Cset->bstrVal);
|
|
do
|
|
{ // Set i = 0 if ch isn't
|
|
pch = Cset->bstrVal; // in set; this stops
|
|
for(i = j; // movement
|
|
i && (ch != *pch++);
|
|
i--) ;
|
|
|
|
// If we are doing a MATCH_WHILE routine then we only
|
|
// continue while i > 0 becuase this indicates that we
|
|
// found the char at the current cp in the CSet. If
|
|
// we were doing a MATCH_UNTIL then we should quit when
|
|
// i != 0 becuase the current char was in the CSet.
|
|
} while((Match == (i ? MATCH_WHILE : MATCH_UNTIL)) &&
|
|
(ch = tp.NextCharCount(count))); // Break if no more chars
|
|
} // or ch not in set
|
|
else
|
|
return E_INVALIDARG;
|
|
|
|
/* If MoveWhile, leave tp immediately after last matched char going
|
|
* forward and at that char going backward (helps to think of tp
|
|
* pointing in between chars). If MoveUntil, leave tp at the char
|
|
* going forward and just after that char going backward.
|
|
*
|
|
* E.g.: the code
|
|
*
|
|
* r.MoveUntil (C1_DIGIT, tomForward, NULL)
|
|
* r.MoveEndWhile(C1_DIGIT, tomForward, NULL)
|
|
*
|
|
* breaks at the first digit and selects the number going forward.
|
|
* Similarly
|
|
*
|
|
* r.MoveUntil (C1_DIGIT, tomBackward, NULL)
|
|
* r.MoveStartWhile(C1_DIGIT, tomBackward, NULL)
|
|
*
|
|
* selects the number going backward.
|
|
*/
|
|
count = (Match == MATCH_WHILE && !ch) // If MoveWhile, move past
|
|
? iDir : 0; // last matched char
|
|
if(Count < 0)
|
|
count++;
|
|
tp.Move(count);
|
|
|
|
Delta = cch = 0; // Suppress motion unless
|
|
if(Match == MATCH_WHILE || ch) // match occurred
|
|
{
|
|
Delta = cch = tp.GetCp() - cpSave; // Calculate distance moved
|
|
if(Match == MATCH_UNTIL) // For MoveUntil methods,
|
|
Delta += iDir; // match counts as a char
|
|
}
|
|
|
|
if(pDelta) // Report motion to caller
|
|
*pDelta = Delta; // if it wants to know
|
|
|
|
// Handle cases for which range is changed
|
|
if(cch || (Delta && _cch && Mode == MOVE_IP))
|
|
{
|
|
if (Mode == MOVE_IP || // If move IP or asked to
|
|
!_cch && (Mode ^ Count) < 0) // cross ends of initial
|
|
{ // IP, use an IP
|
|
cch = 0;
|
|
}
|
|
else if(_cch) // Initially nondegenerate
|
|
{ // range
|
|
if((_cch ^ Mode) < 0) // If wrong end is active,
|
|
_cch = -_cch; // fake a FlipRange (will
|
|
cch += _cch; // set cp shortly)
|
|
if((cch ^ _cch) < 0) // If ends crossed, convert
|
|
cch = 0; // to insertion point
|
|
}
|
|
if(Set(tp.GetCp(), cch)) // Set new range cp and cch
|
|
{
|
|
Update(TRUE); // Update selection
|
|
return NOERROR; // Signal match occurred
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
// No change in range. Return NOERROR iff match occurred for MOVE_UNTIL
|
|
return Delta ? NOERROR : S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::Mover (Unit, Count, pDelta, Mode)
|
|
*
|
|
* @mfunc
|
|
* Helper function to move end(s) <p Count> <p Unit>s, which end(s)
|
|
* depending on Mode = MOVE_IP, MOVE_START, and MOVE_END. Collapsing
|
|
* the range by using MOVE_IP counts as a Unit.
|
|
*
|
|
* Extends range from End if <p Mode> = MOVE_END and from Start if
|
|
* <p Mode> = MOVE_START; else (MOVE_IP) it collapses range to Start if
|
|
* <p Count> <lt>= 0 and to End if <p Count> <gt> 0.
|
|
*
|
|
* Sets *<p pDelta> = count of Units moved
|
|
*
|
|
* Used by ITextRange::Delete(), Move(), MoveStart(), MoveEnd(),
|
|
* and SetIndex()
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR :
|
|
* (if <p Unit> valid) ? S_FALSE : E_INVALIDARG
|
|
*/
|
|
HRESULT CTxtRange::Mover (
|
|
long Unit, //@parm Unit to use for moving active end
|
|
long Count, //@parm Count of units to move active end
|
|
long * pDelta, //@parm Out parm for count of units moved
|
|
MOVES Mode) //@parm MOVE_START (-1), MOVE_IP (0), MOVE_END (1)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::Mover");
|
|
|
|
if(pDelta)
|
|
*pDelta = 0; // Default no units moved
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
LONG cch;
|
|
LONG cchAdj = GetAdjustedTextLength();
|
|
LONG cchMax = 0; // Default full story limits
|
|
LONG cp;
|
|
LONG cpMost = GetCpMost();
|
|
LONG cUnitCollapse = 0;
|
|
HRESULT hr = NOERROR;
|
|
CTxtRange rg(*this); // Use a copy to look around
|
|
|
|
if(pDelta)
|
|
*pDelta = 0; // Default no units moved
|
|
|
|
if(_cch && Count) // Nondegenerate range
|
|
{
|
|
if(Mode == MOVE_IP) // Insertion point: will
|
|
{ // collapse range if Unit is
|
|
if((Count ^ rg._cch) < 0) // defined. Go to correct end
|
|
rg.FlipRange();
|
|
if(Count > 0)
|
|
{
|
|
if(cpMost > cchAdj)
|
|
{
|
|
cUnitCollapse = -1; // Collapse before final CR
|
|
Count = 0; // No more motion
|
|
}
|
|
else
|
|
{ // Extend pDelta pcpMin pcpMost
|
|
hr = rg.Expander(Unit, FALSE, NULL, NULL, &cp);
|
|
cUnitCollapse = 1; // Collapse counts as a Unit
|
|
Count--; // One less Unit to count
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = rg.Expander(Unit, FALSE, NULL, &cp, NULL);
|
|
cUnitCollapse = -1;
|
|
Count++;
|
|
}
|
|
if(FAILED(hr))
|
|
return hr;
|
|
}
|
|
else if((Mode ^ rg._cch) < 0) // MOVE_START or MOVE_END
|
|
rg.FlipRange(); // Go to Start or End
|
|
}
|
|
|
|
if(Count > 0 && Mode != MOVE_END) // Moving IP or Start forward
|
|
{
|
|
cchMax = cchAdj - rg.GetCp(); // Can't pass final CR
|
|
if(cchMax <= 0) // Already at or past it
|
|
{ // Only count comes from
|
|
Count = cUnitCollapse; // possible collapse
|
|
cp = cchAdj; // Put active end at cchAdj
|
|
cch = (Mode == MOVE_START && cpMost > cchAdj)
|
|
? cp - cpMost : 0;
|
|
goto set;
|
|
}
|
|
}
|
|
|
|
cch = rg.UnitCounter(Unit, Count, cchMax); // Count off Count Units
|
|
|
|
if(cch == tomForward) // Unit not implemented
|
|
return E_NOTIMPL;
|
|
|
|
if(cch == tomBackward) // Unit not available, e.g.,
|
|
return S_FALSE; // tomObject and no objects
|
|
|
|
Count += cUnitCollapse; // Add a Unit if collapse
|
|
if(!Count) // Nothing changed, so quit
|
|
return S_FALSE;
|
|
|
|
if (Mode == MOVE_IP || // MOVE_IP or
|
|
!_cch && (Mode ^ Count) < 0) // initial IP end cross
|
|
{
|
|
cch = 0; // New range is degenerate
|
|
}
|
|
else if(_cch) // MOVE_START or MOVE_END
|
|
{ // with nondegenerate range
|
|
if((_cch ^ Mode) < 0) // Make _cch correspond to end
|
|
_cch = -_cch; // that moved
|
|
cch += _cch; // Possible new range length
|
|
if((cch ^ _cch) < 0) // Nondegenerate end cross
|
|
cch = 0; // Use IP
|
|
}
|
|
cp = rg.GetCp();
|
|
|
|
set:
|
|
if(Set(cp, cch)) // Attempt to set new range
|
|
{ // Something changed
|
|
if(pDelta) // Report count of units
|
|
*pDelta = Count; // moved
|
|
Update(TRUE); // Update selection
|
|
return NOERROR;
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* CTxtRange::Replacer (cchNew, *pch)
|
|
*
|
|
* @mfunc
|
|
* Replace this range's using CHARFORMAT _iFormat and updating other
|
|
* text runs as needed.
|
|
*
|
|
* Same as CTxtRange::CleanseAndReplaceRange(cchNew, *pch, publdr),
|
|
* except creates its own undo builder.
|
|
*
|
|
* @rdesc
|
|
* cch of text actually pasted
|
|
*
|
|
* @devnote
|
|
* moves this text pointer to end of replaced text and
|
|
* may move text block and formatting arrays
|
|
*/
|
|
LONG CTxtRange::Replacer (
|
|
LONG cchNew, //@parm Length of replacement text
|
|
WCHAR const * pch, //@parm Replacement text
|
|
DWORD dwFlags) //@parm ReplaceRange flags
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::Replacer");
|
|
|
|
IUndoBuilder * publdr;
|
|
CGenUndoBuilder undobldr(GetPed(), UB_AUTOCOMMIT, &publdr);
|
|
CFreezeDisplay fd(GetPed()->_pdp);
|
|
|
|
undobldr.StopGroupTyping();
|
|
|
|
// Note: we don't check the limit on text here. Right now, this
|
|
// is only called by Delete and SetText so this is OK. However,
|
|
// we might want to reinvestigate this latter if this is called
|
|
// by anything else.
|
|
LONG cchEOP = 0;
|
|
BOOL fTRDsInvolved;
|
|
CheckTableSelection(TRUE, TRUE, &fTRDsInvolved, 0);
|
|
|
|
if(fTRDsInvolved)
|
|
cchEOP = DeleteWithTRDCheck(publdr, SELRR_REMEMBERRANGE, NULL, dwFlags);
|
|
|
|
LONG cch = CleanseAndReplaceRange(cchNew, pch, FALSE, publdr, NULL, NULL, dwFlags);
|
|
|
|
if(cchEOP && _rpTX.IsAfterEOP())
|
|
{ // Don't need extra EOP anymore
|
|
_cch = -cchEOP; // since text ended with one
|
|
ReplaceRange(0, NULL, publdr, SELRR_REMEMBERRANGE);
|
|
cchEOP = 0;
|
|
}
|
|
return cch + cchEOP;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::CharFormatSetter (pCF, dwMask)
|
|
*
|
|
* @mfunc
|
|
* Helper function that's the same as CTxtRange::SetCharFormat(), but
|
|
* adds undo building, and notification.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if success) ? NOERROR : S_FALSE
|
|
* (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
|
|
*/
|
|
HRESULT CTxtRange::CharFormatSetter (
|
|
const CCharFormat *pCF, //@parm CCharFormat to fill with results
|
|
DWORD dwMask, //@parm CHARFORMAT2 mask
|
|
DWORD dwMask2) //@parm CHARFORMAT2 mask
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::CharFormatSetter");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CTxtEdit *ped = GetPed();
|
|
CCallMgr callmgr(ped);
|
|
IUndoBuilder * publdr;
|
|
CGenUndoBuilder undobldr(ped, UB_AUTOCOMMIT, &publdr);
|
|
|
|
if(WriteAccessDenied())
|
|
return E_ACCESSDENIED;
|
|
|
|
undobldr.StopGroupTyping();
|
|
return SetCharFormat(pCF, FALSE, publdr, dwMask, dwMask2);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::ParaFormatSetter (pPF, dwMask)
|
|
*
|
|
* @mfunc
|
|
* Helper function that's the same as CTxtRange::SetParaFormat(), but
|
|
* adds protection checking, undo building, and notification.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if success) ? NOERROR : S_FALSE
|
|
* (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
|
|
*/
|
|
HRESULT CTxtRange::ParaFormatSetter (
|
|
const CParaFormat *pPF, //@parm CParaFormat to fill with results
|
|
DWORD dwMask) //@parm Mask to use
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::ParaFormatSetter");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CTxtEdit *ped = GetPed();
|
|
CCallMgr callmgr(ped);
|
|
IUndoBuilder * publdr;
|
|
CGenUndoBuilder undobldr(ped, UB_AUTOCOMMIT, &publdr);
|
|
|
|
if(WriteAccessDenied())
|
|
return E_ACCESSDENIED;
|
|
|
|
undobldr.StopGroupTyping();
|
|
|
|
CheckTableSelection(FALSE, FALSE, NULL, 0);
|
|
return SetParaFormat(pPF, publdr, dwMask & ~(PFM_TABLE | PFM_TABLEROWDELIMITER), 0);
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::WriteAccessDenied()
|
|
*
|
|
* @mfunc
|
|
* Returns TRUE iff at least part of the range is protected and the
|
|
* owner chooses to enforce it
|
|
*
|
|
* @rdesc
|
|
* TRUE iff write access to range is denied
|
|
*/
|
|
BOOL CTxtRange::WriteAccessDenied ()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtRange::WriteAccessDenied");
|
|
|
|
PROTECT iProt;
|
|
CTxtEdit *ped = GetPed();
|
|
|
|
if (ped && ped->TxGetReadOnly() ||
|
|
((iProt = IsProtected(_cch ? CHKPROT_BACKWARD : CHKPROT_TOM)) == PROTECTED_YES ||
|
|
(iProt == PROTECTED_ASK && ped->IsProtectionCheckingEnabled() &&
|
|
ped->QueryUseProtection(this, 0, 0, 0))))
|
|
// N.B. the preceding if statement assumes that IsProtected returns a tri-value
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::IsTrue (f, pB)
|
|
*
|
|
* @mfunc
|
|
* Return *<p pB> = tomTrue iff <p f> is nonzero and pB isn't NULL
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (f) ? NOERROR : S_FALSE
|
|
*/
|
|
HRESULT CTxtRange::IsTrue(BOOL f, long *pB)
|
|
{
|
|
if(pB)
|
|
*pB = tomFalse;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
if(f)
|
|
{
|
|
if(pB)
|
|
*pB = tomTrue;
|
|
return NOERROR;
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::GetLong (lValue, pLong)
|
|
*
|
|
* @mfunc
|
|
* Return *pLong = lValue provided pLong isn't NULL and this range
|
|
* isn't a zombie
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (zombie) ? CO_E_RELEASED :
|
|
* (pLong) ? NOERROR : E_INVALIDARG
|
|
*/
|
|
HRESULT CTxtRange::GetLong (
|
|
LONG lValue, //@parm Long value to return
|
|
long *pLong) //@parm Out parm to receive long value
|
|
{
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
_TEST_INVARIANT_
|
|
|
|
if(!pLong)
|
|
return E_INVALIDARG;
|
|
|
|
*pLong = lValue;
|
|
|
|
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* IsSameVtables (punk1, punk2)
|
|
*
|
|
* @mfunc
|
|
* Returns true if punk1 has same vtable as punk2
|
|
*
|
|
* @rdesc
|
|
* TRUE iff punk1 has same vtable as punk2
|
|
*/
|
|
BOOL IsSameVtables(IUnknown *punk1, IUnknown *punk2)
|
|
{
|
|
return punk1 && punk2 && *(long *)punk1 == *(long *)punk2;
|
|
}
|
|
|
|
/*
|
|
* FPPTS_TO_TWIPS (x)
|
|
*
|
|
* @mfunc
|
|
* Returns 20*x, i.e., the number of twips corresponding to
|
|
* x given in floating-point points. The value is rounded.
|
|
*
|
|
* @rdesc
|
|
* x converted to twips
|
|
*/
|
|
long FPPTS_TO_TWIPS(
|
|
float x)
|
|
{
|
|
return 20*x + ((x >= 0) ? 0.5 : -0.5);
|
|
}
|