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

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;
}