540 lines
15 KiB
C++
540 lines
15 KiB
C++
/*
|
|
* @doc TOM
|
|
*
|
|
* @module tomsel.cpp - Implement the CTxtSelection Class |
|
|
*
|
|
* This module contains the TOM ITextSelection implementation for
|
|
* the selection object
|
|
*
|
|
* History: <nl>
|
|
* 5/24/95 - Alex Gounares: stubs <nl>
|
|
* 8/95 - Murray Sargent: core implementation
|
|
*
|
|
* @comm
|
|
* The "cursor-pad" functions (Left, Right, Up, Down, Home, End)
|
|
* are simple generalizations of the corresponding keystrokes and have
|
|
* to express the same UI. Consequently they are typically not as
|
|
* efficient for moving the cursor around as ITextRange methods, which
|
|
* are designed for particular purposes. This is especially true for
|
|
* counts larger than one.
|
|
*
|
|
* @devnote
|
|
* All ITextSelection methods inherited from ITextRange are handled by
|
|
* the ITextRange methods, since they either don't affect the display of
|
|
* the selection (e.g., Get methods), or virtual methods are used that
|
|
* perform the appropriate updating of the selection on screen.
|
|
*
|
|
* Copyright (c) 1995-1998, Microsoft Corporation. All rights reserved.
|
|
*/
|
|
|
|
#include "_common.h"
|
|
#include "_select.h"
|
|
#include "_disp.h"
|
|
#include "_edit.h"
|
|
|
|
#define DEBUG_CLASSNAME CTxtSelection
|
|
#include "_invar.h"
|
|
|
|
|
|
//---------------------- CTxtSelection methods ------------------------------------
|
|
|
|
/*
|
|
* CTxtSelection::EndKey (Unit, Extend, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Act as UI End key, such that <p Extend> is TRUE corresponds to the
|
|
* Shift key being depressed and <p Unit> = start of line/document for
|
|
* Ctrl key not being/being depressed. Returns *<p pDelta> = count of
|
|
* characters active end is moved forward, i.e., a number >= 0.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (invalid Unit) ? E_INVALIDARG :
|
|
* (if change) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtSelection::EndKey (
|
|
long Unit, //@parm Unit to use
|
|
long Extend, //@parm Extend selection or go to IP
|
|
long * pDelta) //@parm Out parm to receive count of chars moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::EndKey");
|
|
|
|
return Homer(Unit, Extend, pDelta, End);
|
|
}
|
|
|
|
/*
|
|
* CTxtSelection::GetFlags (pFlags)
|
|
*
|
|
* @mfunc
|
|
* Set <p pFlags> = this text selection's flags
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (<p pFlags>) ? NOERROR : E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtSelection::GetFlags(
|
|
long * pFlags) //@parm Out parm to receive selection flags
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::GetFlags");
|
|
|
|
if(!pFlags)
|
|
return E_INVALIDARG;
|
|
|
|
if(IsZombie())
|
|
{
|
|
*pFlags = tomSelStartActive | tomSelReplace;
|
|
return CO_E_RELEASED;
|
|
}
|
|
|
|
DWORD dwFlags = _cch <= 0; // Store tomSelStartActive value
|
|
|
|
if(_fCaretNotAtBOL)
|
|
dwFlags |= tomSelAtEOL;
|
|
|
|
if(GetPed()->_fOverstrike)
|
|
dwFlags |= tomSelOvertype;
|
|
|
|
if(GetPed()->_fFocus)
|
|
dwFlags |= tomSelActive;
|
|
|
|
*pFlags = dwFlags | tomSelReplace; // tomSelReplace isn't optional
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtSelection::GetSelectionType (pType)
|
|
*
|
|
* @mfunc
|
|
* Set *pType = type of this text selection
|
|
*
|
|
* @rdesc
|
|
* HRESULT = <p pType> ? NOERROR : E_INVALIDARG
|
|
*/
|
|
STDMETHODIMP CTxtSelection::GetType(
|
|
long * pType) //@parm Out parm to receive selection type
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::GetSelectionType");
|
|
|
|
if(!pType)
|
|
return E_INVALIDARG;
|
|
|
|
*pType = !_cch ? tomSelectionIP
|
|
: (_cch == -1 && _rpTX.GetChar() == WCH_EMBEDDING ||
|
|
_cch == 1 && GetPrevChar() == WCH_EMBEDDING)
|
|
? tomSelectionInlineShape : tomSelectionNormal;
|
|
|
|
return IsZombie() ? CO_E_RELEASED : NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtSelection::HomeKey (Unit, Extend, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Act as UI Home key, such that <p Extend> is TRUE corresponds to the
|
|
* Shift key being depressed and <p Unit> = start of line/document for
|
|
* Ctrl key not being/being depressed. Returns *<p pDelta> = count of
|
|
* characters active end is moved forward, i.e., a number <= 0.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (invalid Unit) ? E_INVALIDARG :
|
|
* (if change) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtSelection::HomeKey (
|
|
long Unit, //@parm Unit to use
|
|
long Extend, //@parm Extend selection or go to IP
|
|
long * pDelta) //@parm Out parm to receive count of chars moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::HomeKey");
|
|
|
|
return Homer(Unit, Extend, pDelta, Home);
|
|
}
|
|
|
|
/*
|
|
* CTxtSelection::MoveDown (Unit, Count, Extend, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Act as UI Down arrow, such that <p Extend> is TRUE corresponds to the
|
|
* Shift key being depressed and <p Unit> = tomLine/tomParagraph for
|
|
* Ctrl key not being/being depressed. In addition, <p Unit> can equal
|
|
* tomWindow/tomWindowEnd for the Ctrl key not being/being depressed.
|
|
* This second pair emulates PgDn behavior. The method returns
|
|
* *<p pDelta> = actual count of units moved.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtSelection::MoveDown (
|
|
long Unit, //@parm Unit to use
|
|
long Count, //@parm Number of Units to move
|
|
long Extend, //@parm Extend selection or go to IP
|
|
long * pDelta) //@parm Out parm to receive actual count of
|
|
// Units moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::MoveDown");
|
|
return GeoMover(Unit, Count, Extend, pDelta, 3);
|
|
}
|
|
|
|
/*
|
|
* CTxtSelection::MoveLeft (Unit, Count, Extend, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Act as UI left arrow, such that <p Extend> is TRUE corresponds to the
|
|
* Shift key being depressed and <p Unit> = tomChar/tomWord for Ctrl key
|
|
* not being/being depressed. Returns *<p pDelta> = actual count of
|
|
* units moved
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtSelection::MoveLeft (
|
|
long Unit, //@parm Unit to use
|
|
long Count, //@parm Number of Units to move
|
|
long Extend, //@parm Extend selection or go to IP
|
|
long * pDelta) //@parm Out parm to receive actual count of
|
|
// Units moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::MoveLeft");
|
|
|
|
return GeoMover(Unit, Count, Extend, pDelta, 0);
|
|
}
|
|
|
|
/*
|
|
* CTxtSelection::MoveRight (Unit, Count, Extend, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Act as UI right arrow, such that <p Extend> is TRUE corresponds to the
|
|
* Shift key being depressed and <p Unit> = tomChar/tomWord for Ctrl key
|
|
* not being/being depressed. Returns *<p pDelta> = actual count of
|
|
* units moved
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtSelection::MoveRight (
|
|
long Unit, //@parm Unit to use
|
|
long Count, //@parm Number of Units to move
|
|
long Extend, //@parm Extend selection or go to IP
|
|
long * pDelta) //@parm Out parm to receive actual count of
|
|
// Units moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::MoveRight");
|
|
return GeoMover(Unit, Count, Extend, pDelta, 1);
|
|
}
|
|
|
|
/*
|
|
* CTxtSelection::MoveUp (Unit, Count, Extend, pDelta)
|
|
*
|
|
* @mfunc
|
|
* Act as UI Up arrow, such that <p Extend> is TRUE corresponds to the
|
|
* Shift key being depressed and <p Unit> = tomLine/tomParagraph for
|
|
* Ctrl key not being/being depressed. In addition, <p Unit> can equal
|
|
* tomWindow/tomWindowEnd for the Ctrl key not being/being depressed.
|
|
* This second pair emulates PgUp behavior. The method returns
|
|
* *<p pDelta> = actual count of units moved.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR : S_FALSE
|
|
*/
|
|
STDMETHODIMP CTxtSelection::MoveUp (
|
|
long Unit, //@parm Unit to use
|
|
long Count, //@parm Number of Units to move
|
|
long Extend, //@parm Extend selection or go to IP
|
|
long * pDelta) //@parm Out parm to receive actual count of
|
|
// Units moved
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::MoveUp");
|
|
|
|
return GeoMover(Unit, Count, Extend, pDelta, 2);
|
|
}
|
|
|
|
/*
|
|
* CTxtSelection::SetFlags (Flags)
|
|
*
|
|
* @mfunc
|
|
* Set this text selection's flags = Flags
|
|
*
|
|
* @rdesc
|
|
* HRESULT = NOERROR
|
|
*
|
|
* @comm
|
|
* RichEdit ignores tomSelReplace since it's always on
|
|
*/
|
|
STDMETHODIMP CTxtSelection::SetFlags(
|
|
long Flags) //@parm New flag values
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::SetFlags");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
_fCaretNotAtBOL = (Flags & tomSelAtEOL) != 0;
|
|
GetPed()->_fOverstrike = (Flags & tomSelOvertype) != 0;
|
|
|
|
if(!(Flags & tomSelStartActive) ^ (_cch > 0))
|
|
FlipRange();
|
|
|
|
if((Flags & tomSelActive) && !GetPed()->_fFocus)
|
|
GetPed()->TxSetFocus();
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtRange::SetPoint (x, y, Extend)
|
|
*
|
|
* @mfunc
|
|
* Select text at or up through (depending on <p Extend>) the point
|
|
* (<p x>, <p y>).
|
|
*
|
|
* @rdesc
|
|
* HRESULT = NOERROR
|
|
*/
|
|
STDMETHODIMP CTxtSelection::SetPoint (
|
|
long x, //@parm Horizontal coord of point to select
|
|
long y, //@parm Vertical coord of point to select
|
|
long Extend) //@parm Whether to extend selection to point
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::SelectPoint");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
POINT ptxy = {x, y};
|
|
POINTUV pt;
|
|
|
|
_pdp->PointuvFromPoint(pt, ptxy);
|
|
|
|
if(Extend)
|
|
ExtendSelection (pt);
|
|
else
|
|
SetCaret(pt, FALSE);
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtSelection::TypeText (bstr)
|
|
*
|
|
* @mfunc
|
|
* Type the string given by bstr at this selection as if someone typed it.
|
|
* This is similar to the underlying ITextRange::SetText() method, but is
|
|
* sensitive to the Ins/Ovr key state.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = !<p bstr> ? E_INVALIDARG :
|
|
* (whole string typed) ? NOERROR : S_FALSE
|
|
* @comm
|
|
* This is faster than sending chars via SendMessage(), but it's slower
|
|
* than using ITextRange::SetText()
|
|
*/
|
|
STDMETHODIMP CTxtSelection::TypeText (
|
|
BSTR bstr) //@parm String to type into this selection
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtSelection::TypeText");
|
|
|
|
if(!bstr)
|
|
return E_INVALIDARG;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
|
|
if(!GetPed()->IsntProtectedOrReadOnly(WM_CHAR, 0, 0))
|
|
return E_ACCESSDENIED;
|
|
|
|
CFreezeDisplay fd(_pdp);
|
|
DWORD dwFlags = GetPed()->_fOverstrike;
|
|
DWORD dwFlagsPutChar;
|
|
OLECHAR * pch = bstr;
|
|
IUndoBuilder * publdr;
|
|
CGenUndoBuilder undobldr(GetPed(), UB_AUTOCOMMIT, &publdr);
|
|
|
|
if(GetPed()->_fIMEInProgress) // Suppress autocorrect until last
|
|
dwFlags |= KBD_NOAUTOCORRECT | KBD_CHAR; // character during IME
|
|
|
|
dwFlagsPutChar = dwFlags;
|
|
for(LONG cch = SysStringLen(bstr); cch > 0; dwFlags = dwFlagsPutChar)
|
|
{
|
|
unsigned ch = *pch++;
|
|
cch--;
|
|
|
|
if(IN_RANGE(0xD800, ch, 0xDBFF) && cch && IN_RANGE(0xDC00, *pch, 0xDFFF))
|
|
{
|
|
ch = (*pch++ & 0x3FF) + ((ch & 0x3FF) << 10) + 0x10000;
|
|
cch--;
|
|
dwFlags &= ~KBD_CHAR; // Need font binding
|
|
}
|
|
else if ((IN_RANGE(0x03400, ch, 0x04DFF) || IN_RANGE(0xE000, ch, 0x0F8FF)))
|
|
dwFlags &= ~KBD_CHAR; // Need font binding
|
|
|
|
if(!cch) // ch is last character: allow autocorrect
|
|
dwFlags &= ~KBD_NOAUTOCORRECT;
|
|
if(!PutChar(ch, dwFlags, publdr))
|
|
break;
|
|
undobldr.Done(); // Simulate one char input at a time
|
|
}
|
|
return cch ? S_FALSE : NOERROR;
|
|
}
|
|
|
|
|
|
//--------------------- ITextSelection PRIVATE helper methods -----------------------------
|
|
|
|
/*
|
|
* @doc INTERNAL
|
|
*
|
|
* CTxtSelection::GeoMover (Unit, Count, Extend, pDelta, iDir)
|
|
*
|
|
* @mfunc
|
|
* Helper function to move active end <p Count> <p Unit>s geometrically
|
|
*
|
|
* Extends range if <p Extend> is TRUE; else 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 ITextSelection::Left(), Right(), Up(), and Down()
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (if change) ? NOERROR : (if Unit supported) ? S_FALSE
|
|
* : E_NOTIMPL
|
|
*/
|
|
HRESULT CTxtSelection::GeoMover (
|
|
long Unit, //@parm Unit to use
|
|
long Count, //@parm Number of Units to move
|
|
long Extend, //@parm Extend selection or go to IP
|
|
long * pDelta, //@parm Out parm to receive count of Units moved
|
|
LONG iDir) //@parm Direction to move in if Count > 0
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtSelection::GeoMover");
|
|
|
|
if(pDelta) // Default no movement
|
|
*pDelta = 0;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
LONG CountSave = Count;
|
|
LONG cp;
|
|
LONG cUnit;
|
|
LONG iDefUnit = (iDir & 0xfe) == 2 ? tomLine : tomCharacter;
|
|
BOOL fCollapse = !Extend && _cch;
|
|
BOOL fCtrl = Unit != iDefUnit;
|
|
BOOL fExtend = Extend != 0;
|
|
|
|
if(Count < 0)
|
|
{
|
|
Count = -Count;
|
|
iDir ^= 1;
|
|
}
|
|
|
|
if(iDefUnit == tomLine) // Up or Down
|
|
{
|
|
if(Unit == tomPage && GetPed()->IsInPageView())
|
|
Unit = tomScreen;
|
|
|
|
if(Unit == tomScreen)
|
|
{
|
|
iDir ^= 6; // Convert Up/Down to PgUp/PgDn
|
|
fCtrl = FALSE;
|
|
}
|
|
else if(Unit == tomWindow) // Go to top/bottom of window
|
|
{
|
|
iDir ^= 6; // Convert Up/Down to PgUp/PgDn
|
|
Count = 1; // Be sure Count = 1
|
|
} // Leave fCtrl = 1
|
|
else if(fCtrl && Unit != tomParagraph)
|
|
return E_INVALIDARG;
|
|
}
|
|
else if(fCtrl && Unit != tomWord)
|
|
return E_INVALIDARG;
|
|
|
|
for (cUnit = Count; Count; Count--)
|
|
{
|
|
cp = GetCp(); // Save cp for comparison
|
|
switch(iDir) // iDir bit 0 inc/dec for 1/0
|
|
{ // iDir values are chosen contiguously
|
|
case 0: // to encourage compiler to use a
|
|
Left(fCtrl, fExtend); // jump table
|
|
break;
|
|
|
|
case 1: // tomCharacter/tomWord OK here
|
|
Right(fCtrl, fExtend);
|
|
break;
|
|
|
|
case 2: // tomLine/tomParagraph OK here
|
|
Up(fCtrl, fExtend);
|
|
break;
|
|
|
|
case 3: // tomLine/tomParagraph OK here
|
|
Down(fCtrl, fExtend);
|
|
break;
|
|
|
|
case 4: // tomWindow/tomScreen OK here
|
|
PageUp(fCtrl, fExtend);
|
|
break;
|
|
|
|
case 5: // tomWindow/tomScreen OK here
|
|
PageDown(fCtrl, fExtend);
|
|
}
|
|
if(cp == GetCp() && !fCollapse) // Didn't move or collapse
|
|
break; // so we're done
|
|
fCollapse = FALSE; // Collapse counts as a Unit
|
|
}
|
|
|
|
cUnit -= Count; // Count of Units moved
|
|
if(CountSave < 0)
|
|
cUnit = -cUnit; // Negative Counts get negative results
|
|
|
|
if(pDelta)
|
|
*pDelta = cUnit;
|
|
|
|
return cUnit ? NOERROR : S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* CTxtSelection::Homer (Unit, Extend, pDelta, pfn)
|
|
*
|
|
* @mfunc
|
|
* Helper function to move active end Home or End depending on pfn
|
|
*
|
|
* Extends range if <p Extend> is TRUE; else collapses range to Start if
|
|
* <p Count> <lt> 0 and to End if <p Count> <gt> 0.
|
|
*
|
|
* Sets *<p pDelta> = count of chars moved forward
|
|
*
|
|
* Used by ITextSelection::Home(), End()
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (invalid Unit) ? E_INVALIDARG :
|
|
* (if change) ? NOERROR : S_FALSE
|
|
*/
|
|
HRESULT CTxtSelection::Homer (
|
|
long Unit, //@parm Unit to use
|
|
long Extend, //@parm Extend selection or go to IP
|
|
long * pDelta, //@parm Out parm to receive count of Units moved
|
|
BOOL (CTxtSelection::*pfn)(BOOL, BOOL)) //@parm Direction to move in
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtSelection::Homer");
|
|
|
|
if(pDelta) // Default no movement
|
|
*pDelta = 0;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
if(Unit != tomLine && Unit != tomStory)
|
|
return E_INVALIDARG;
|
|
|
|
CCallMgr callmgr(GetPed());
|
|
LONG cch = GetCp();
|
|
|
|
(this->*pfn)(Unit != tomLine, Extend != 0);
|
|
cch = GetCp() - cch;
|
|
if(pDelta)
|
|
*pDelta = cch;
|
|
|
|
return cch ? NOERROR : S_FALSE;
|
|
}
|