📄 select.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*
* @doc INTERNAL
*
* @module select.cpp -- Implement the CTxtSelection class |
*
* This module implements the internal CTxtSelection methods.
* See select2.c and range2.c for the ITextSelection methods
*
* Authors: <nl>
* Original RichEdit code: David R. Fulmer <nl>
* Christian Fortini <nl>
* Murray Sargent <nl>
*
* @devnote
* The selection UI is one of the more intricate parts of an editor.
* One common area of confusion is the "ambiguous cp", that is,
* a cp at the beginning of one line, which is also the cp at the
* end of the previous line. We control which location to use by
* the _fCaretNotAtBOL flag. Specifically, the caret is OK at the
* beginning of the line (BOL) (_fCaretNotAtBOL = FALSE) except in
* three cases:
*
* 1) the user clicked at or past the end of a wrapped line,
* 2) the user typed End key on a wrapped line,
* 3) the active end of a nondegenerate selection is at the EOL.
*
*/
#include "_common.h"
#include "_select.h"
#include "_edit.h"
#include "_disp.h"
#include "_measure.h"
#include "_font.h"
#include "_rtfconv.h"
#include "_ime.h"
// GuyBark JupiterJ IME:
#ifndef TARGET_NT
#include <keybd.h>
#endif // !TARGET_NT
#ifndef MACPORT
// default FE Fonts for handling autoFont switching
const TCHAR lpJapanFontName[] = { 0x0FF2D, 0x0FF33, L' ', 0x0660E, 0x0671D, 0x0 };
const TCHAR lpKoreanFontName[] = { 0x0AD74, 0x0B9BC, 0x0CCB4, 0x0 };
const TCHAR lpSChineseFontName[] = { 0x05B8B, 0x04F53, 0x0 };
const TCHAR lpTChineseFontName[] = { 0x065B0, 0x07D30, 0x0660E, 0x09AD4, 0x0 };
#endif
#define MAX_RUNTOSEARCH (256L)
extern WCHAR lfJapaneseFaceName[LF_FACESIZE];
extern WCHAR lfHangulFaceName[LF_FACESIZE];
extern WCHAR lfBig5FaceName[LF_FACESIZE];
extern WCHAR lfGB2312FaceName[LF_FACESIZE];
ASSERTDATA
// ======================= Invariant stuff and Constructors ======================================================
#define DEBUG_CLASSNAME CTxtSelection
#include "_invar.h"
#ifdef DEBUG
BOOL
CTxtSelection::Invariant( void ) const
{
// FUTURE: maybe add some thoughtful asserts...
static LONG numTests = 0;
numTests++; // how many times we've been called
if(IsInOutlineView() && _cch)
{
LONG cpMin, cpMost;
GetRange(cpMin, cpMost);
CTxtPtr tp(_rpTX); // Scan range for an EOP
tp.SetCp(cpMin);
AssertSz((unsigned)(tp.FindEOP(cpMost - cpMin) != 0) == _fSelHasEOP,
"Incorrect CTxtSelection::_fSelHasEOP");
}
return CTxtRange::Invariant();
}
#endif
CTxtSelection::CTxtSelection(CDisplay * const pdp) :
CTxtRange(pdp->GetED())
{
TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::CTxtSelection");
Assert(pdp);
_fSel = TRUE; // This range is a selection
_pdp = pdp;
// Set the show selection flag to the inverse of the hide selection flag in
// the PED.
_fShowSelection = !pdp->GetED()->fHideSelection();
// When we are initialized we don't have a selection therefore,
// we do want to show the caret.
_fShowCaret = TRUE;
}
inline void SelectionNull(CTxtEdit *ped)
{
TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "SelectionNull");
if(ped)
ped->SetSelectionToNull();
}
CTxtSelection::~CTxtSelection()
{
TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::~CTxtSelection");
// Notify edit object that we are gone (if there's a nonNULL ped, i.e.,
// if the selection isn't a zombie).
SelectionNull(GetPed());
}
//////////////////////////////// Assignments /////////////////////////////////////////
CRchTxtPtr& CTxtSelection::operator =(const CRchTxtPtr& rtp)
{
TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::operator =");
_TEST_INVARIANT_
return CTxtRange::operator =(rtp);
}
CTxtRange& CTxtSelection::operator =(const CTxtRange &rg)
{
TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::operator =");
_TEST_INVARIANT_
return CTxtRange::operator =(rg);
}
////////////////////// Update caret & selection mechanism ///////////////////////////////
/*
* CTxtSelection::Update(fScrollIntoView)
*
* @mfunc
* Update selection and/or caret on screen. As a side
* effect, this methods ends deferring updates.
*
* @rdesc
* TRUE if success, FALSE otherwise
*/
BOOL CTxtSelection::Update (
BOOL fScrollIntoView) //@parm TRUE if should scroll caret into view
{
TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::Update");
LONG cch;
LONG cchSave = _cch;
LONG cchText = GetTextLength();
LONG cp, cpMin, cpMost;
BOOL fMoveBack = _fMoveBack;
if(!_cch) // Update _cpAnchor, etc.
UpdateForAutoWord();
else if(GetPF()->InTable() && (_fSelHasEOP || _fSelHasCell))
{
Expander(_fSelHasEOP ? tomParagraph : tomCell,
TRUE, NULL, &cpMin, &cpMost);
}
if(IsInOutlineView() && !GetPed()->IsMouseDown())
{
CPFRunPtr rp(*this);
cp = GetCp();
GetRange(cpMin, cpMost);
if(_cch && (cpMin || cpMost < cchText))
{
LONG *pcpMin = &cpMin;
LONG *pcpMost = &cpMost;
// If selection contains an EOP, expand to para boundaries
if(_fSelHasEOP)
{
if(_fMoveBack ^ (_cch < 0)) // Decreasing selection
{ // size: move active end
if(_fMoveBack)
pcpMost = NULL; // to StartOf para
else
pcpMin = NULL; // to EndOf para
}
Expander(tomParagraph, TRUE, NULL, pcpMin, pcpMost);
}
LONG cpMinSave = cpMin; // Save initial cp's to see if
LONG cpMostSave = cpMost; // we need to Set() below
// The following handles selection expansion correctly, but
// not compression; need logic like that preceding Expander()
rp.AdvanceCp(cpMin - cp); // Start at cpMin
if(rp.IsCollapsed())
cpMin += rp.FindExpandedBackward();
rp.AdjustForward();
BOOL fCpMinCollapsed = rp.IsCollapsed();
rp.AdvanceCp(cpMost - cpMin); // Go to cpMost
if(rp.IsCollapsed())
cpMost += rp.FindExpandedForward();
// GuyBark Jupiter 17766.
//
// If fCpMinCollapsed is set, then cpMin is in a collapsed range. Therefore
// remove the selection and move the caret to cpMost. That's a reasonable
// thing to do. Also check to see if cpMost is in a collapsed region. If
// so, the selection is removed, and the caret is moved to cpMin, This is
// also a reasonable thing to do.
//
// However, the test of rp.IsCollapsed() is not complete. Say you tap the +
// for a collapsed header. The header and the entire collapsed range is
// selected on the pen down. This includes the final eop for the collapsed
// range. Then on the pen up, we end up here. The call to rp.IsCollapsed()
// here checks to see if the cp after the end of selection is in a collapsed range.
// But the end of the selection here is after the final eop in the range.
// It's actually at the start of the next visible text, which by definition
// cannot be collapsed. So the cpMost is found not to be collapsed, and we
// don't mess with cpMost here. BUT say the collapsed text for the header
// selected runs to the end of the document. In this case, there's nothin
// beyond the final eop in the collapsed range. So when we try to find if
// whatever's beyond that is collapsed, we can't. So we say that cpMost
// does lie in a collapsed range.
//
// So therefore, check if cpMost is at the end of the document. If so, we
// won't mess with cpMost here. This makes sense, as how can a non-existant
// cp beyond the end of the document lie in a collapsed range.
//
// This leads to one other change to the RichEdit behavior. In Normal View,
// select part way through the last header in the document up to the end of
// the document, (including the final eop). Change to outline view, such that
// as soon as you get there, the last header will be the last visible line.
// Previously we found here that cpMin was not in collapsed text, but that
// cpMost was. This meant the selection was removed, and the caret is set
// to the beginning of the header. With this change, we find that cpMost is
// not in collapsed text, so we don't mess with it. This means by the time
// we get to Outline View, the entire header, (and all its collapsed text),
// is selected. That's how other headers behave in the document too. So this
// change in behavior is a good thing.
// At least I think that's what's going on.
#ifdef PWD_JUPITER
if(fCpMinCollapsed || (rp.IsCollapsed() && (cpMost < cchText)))
#else
if(fCpMinCollapsed || rp.IsCollapsed())
#endif // PWD_JUPITER
{
if(rp.IsCollapsed())
{
rp.AdvanceCp(cpMin - cpMost);
rp.AdjustForward();
cpMost = cpMin;
}
else
cpMin = cpMost;
}
if(cpMin != cpMinSave || cpMost != cpMostSave)
Set(cpMost, cpMost - cpMin);
}
if(!_cch && rp.IsCollapsed()) // Note: above may have collapsed
{ // selection...
cch = fMoveBack ? rp.FindExpandedBackward() : 0;
if(rp.IsCollapsed())
#ifdef PWD_JUPITER // GuyBark Jupiter 18391
cch = rp.FindExpanded(FALSE);
#else
cch = rp.FindExpanded();
#endif // PWD_JUPITER
_fExtend = FALSE;
Advance(cch);
rp.AdjustForward();
if(cch <= 0 && rp.IsCollapsed() && _rpTX.IsAfterEOP())
BackupCRLF();
_fCaretNotAtBOL = FALSE;
}
}
// Don't let active end be in hidden text
CCFRunPtr rp(*this);
cp = GetCp();
GetRange(cpMin, cpMost);
if(_cch && (cpMin || cpMost < cchText))
{
rp.AdvanceCp(cpMin - cp); // Start at cpMin
BOOL fHidden = rp.IsInHidden();
rp.AdvanceCp(cpMost - cpMin); // Go to cpMost
if(fHidden) // It's hidden, so collapse
Collapser(tomEnd); // selection at End for treatment
else if(rp.IsInHidden() && // cpMin OK, how about cpMost?
cpMost < cchText)
{ // Check both sides of edge
Collapser(tomEnd); // collapse selection at end
}
}
if(!_cch && rp.IsInHidden()) // Note: above may have collapsed
{ // selection...
cch = fMoveBack ? rp.FindUnhiddenBackward() : 0;
if(!fMoveBack || rp.IsHidden())
cch = rp.FindUnhidden();
_fExtend = FALSE;
Advance(cch);
_fCaretNotAtBOL = FALSE;
}
if((cchSave ^ _cch) < 0) // Don't change active end
FlipRange();
if(!_cch && cchSave)
{
Update_iFormat(-1);
_fCaretNotAtBOL = FALSE;
}
_TEST_INVARIANT_
if( !GetPed()->fInplaceActive() || GetPed()->IsStreaming() )
{
// nothing to do while inactive or streaming in text or RTF data.
return TRUE;
}
// Recalc up to active end (caret)
if(!_pdp->WaitForRecalc(GetCp(), -1))
{ // Line recalc failure
Set(0, 0); // Put caret at start of text
}
ShowCaret(!_cch);
UpdateCaret(fScrollIntoView); // Update Caret position, possibly
// scrolling it into view
GetPed()->TxShowCaret(FALSE);
UpdateSelection(); // Show new selection
GetPed()->TxShowCaret(TRUE);
return TRUE;
}
/*
* CTxtSelection::CheckSynchCharSet()
*
* @mfunc
* Check if the current keyboard matches the current font's charset;
* if not, call CheckChangeFont to find the right font
*/
void CTxtSelection::CheckSynchCharSet()
{
CTxtEdit* ped = GetPed();
const CCharFormat *pCF;
UINT lcidKbd;
UINT uKbdCodePage;
if (_cch)
{
// for selection, we need to get the character format at cpMin+1
CTxtRange rg( ped, GetCpMin()+1, 0 );
Set_iCF(rg.Get_iCF ());
_fUseiFormat = TRUE;
}
pCF = ped->GetCharFormat(_iFormat);
// If current font is not set correctly,
// change to a font preferred by current keyboard.
lcidKbd = GetKeyboardLCID();
uKbdCodePage = ConvertLanguageIDtoCodePage(lcidKbd);
if (lcidKbd && pCF && !ped->_fSingleCodePage &&
((UINT)GetCodePage(pCF->bCharSet) != uKbdCodePage) &&
pCF->bCharSet != SYMBOL_CHARSET && pCF->bCharSet != DEFAULT_CHARSET &&
pCF->bCharSet != OEM_CHARSET &&
(!IsFELCID(lcidKbd) || pCF->bCharSet != ANSI_CHARSET ||
uKbdCodePage == _JAPAN_CP && ped->_fKANAMode && pCF->bCharSet != SHIFTJIS_CHARSET )
)
{
CheckChangeFont (ped, TRUE, lcidKbd, uKbdCodePage);
}
}
/*
* CTxtSelection::UpdateCaret(fScrollIntoView)
*
* @mfunc
* This routine updates caret/selection active end on screen.
* It figures its position, size, clipping, etc. It can optionally
* scroll the caret into view.
*
* @rdesc
* TRUE if view was scrolled, FALSE otherwise
*
* @devnote
* The caret is actually shown on screen only if _fShowCaret is TRUE.
*/
BOOL CTxtSelection::UpdateCaret (
BOOL fScrollIntoView) //@parm If TRUE, scroll caret into view if we have
// focus or if not and selection isn't hidden
{
TRACEBEGIN(TRCSUBSYSSEL, TRCSCOPEINTERN, "CTxtSelection::UpdateCaret");
// of focus
_TEST_INVARIANT_
// Is the display currently frozen
if (_pdp->IsFrozen())
{
// Save this call for another time.
_pdp->SaveUpdateCaret(fScrollIntoView);
return FALSE;
}
BOOL fAutoVScroll = FALSE;
BOOL fAutoHScroll = FALSE;
CTxtEdit* ped = GetPed();
POINT pt;
CLinePtr rp(_pdp);
RECT rcClient;
RECT rcView;
LONG xWidthView;
LONG yHeightView;
LONG xScroll = _pdp->GetXScroll();
LONG yScroll = _pdp->GetYScroll();
INT yAbove = 0; // ascent of line above & beyond IP
INT yAscent = 0; // ascent of IP
INT yAscentLine;
LONG yBase; // base of IP & line
INT yBelow = 0; // descent of line below & beyond IP
INT yDescent = 0; // descent of IP
INT yDescentLine;
const INT yHeightSave = _yHeightCaret;
INT ySum;
LONG yViewTop;
LONG yViewBottom;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -