/* * @doc TOM * * @module tomsel.cpp - Implement the CTxtSelection Class | * * This module contains the TOM ITextSelection implementation for * the selection object * * History: * 5/24/95 - Alex Gounares: stubs * 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

is TRUE corresponds to the * Shift key being depressed and

= start of line/document for * Ctrl key not being/being depressed. Returns *

= 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

= this text selection's flags * * @rdesc * HRESULT = (

) ? 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 =

? 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

is TRUE corresponds to the * Shift key being depressed and

= start of line/document for * Ctrl key not being/being depressed. Returns *

= 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

is TRUE corresponds to the * Shift key being depressed and

= tomLine/tomParagraph for * Ctrl key not being/being depressed. In addition,

can equal * tomWindow/tomWindowEnd for the Ctrl key not being/being depressed. * This second pair emulates PgDn behavior. The method returns * *

= 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

is TRUE corresponds to the * Shift key being depressed and

= tomChar/tomWord for Ctrl key * not being/being depressed. Returns *

= 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

is TRUE corresponds to the * Shift key being depressed and

= tomChar/tomWord for Ctrl key * not being/being depressed. Returns *

= 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

is TRUE corresponds to the * Shift key being depressed and

= tomLine/tomParagraph for * Ctrl key not being/being depressed. In addition,

can equal * tomWindow/tomWindowEnd for the Ctrl key not being/being depressed. * This second pair emulates PgUp behavior. The method returns * *

= 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

) the point * (

,

). * * @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 = !

? 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

s geometrically * * Extends range if

is TRUE; else collapses range to Start if *

0 and to End if

0. * * Sets *

= 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

is TRUE; else collapses range to Start if *

0 and to End if

0. * * Sets *

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