|
|
/*
* @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; }
|