mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-25 03:33:22 +01:00
1888 lines
43 KiB
C++
1888 lines
43 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
Author: ShonK
|
|
Project: Kauai
|
|
Reviewed:
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
For editing a text file or text stream as a document. Unlike the edit
|
|
controls in text.h/text.cpp, all the text need not be in memory (this
|
|
uses a BSF) and there can be multiple views on the same text.
|
|
|
|
***************************************************************************/
|
|
#include "frame.h"
|
|
ASSERTNAME
|
|
|
|
|
|
RTCLASS(TXDC)
|
|
RTCLASS(TXDD)
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for a text document.
|
|
***************************************************************************/
|
|
TXDC::TXDC(PDOCB pdocb, ulong grfdoc) : DOCB(pdocb, grfdoc)
|
|
{
|
|
_pbsf = pvNil;
|
|
_pfil = pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for a text document.
|
|
***************************************************************************/
|
|
TXDC::~TXDC(void)
|
|
{
|
|
ReleasePpo(&_pbsf);
|
|
ReleasePpo(&_pfil);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Create a new document based on the given text file and or text stream.
|
|
***************************************************************************/
|
|
PTXDC TXDC::PtxdcNew(PFNI pfni, PBSF pbsf, PDOCB pdocb, ulong grfdoc)
|
|
{
|
|
AssertNilOrPo(pfni, ffniFile);
|
|
AssertNilOrPo(pbsf, 0);
|
|
AssertNilOrPo(pdocb, 0);
|
|
PTXDC ptxdc;
|
|
|
|
if (pvNil == (ptxdc = NewObj TXDC(pdocb, grfdoc)))
|
|
return pvNil;
|
|
|
|
if (!ptxdc->_FInit(pfni, pbsf))
|
|
ReleasePpo(&ptxdc);
|
|
|
|
return ptxdc;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize the TXDC.
|
|
***************************************************************************/
|
|
bool TXDC::_FInit(PFNI pfni, PBSF pbsf)
|
|
{
|
|
AssertNilOrPo(pfni, ffniFile);
|
|
AssertNilOrPo(pbsf, 0);
|
|
|
|
if (pvNil != pfni)
|
|
{
|
|
if (pvNil == (_pfil = FIL::PfilOpen(pfni)))
|
|
return fFalse;
|
|
}
|
|
|
|
if (pvNil != pbsf)
|
|
{
|
|
pbsf->AddRef();
|
|
_pbsf = pbsf;
|
|
}
|
|
else if (pvNil == (_pbsf = NewObj BSF))
|
|
return fFalse;
|
|
else if (pvNil != _pfil && _pfil->FpMac() > 0)
|
|
{
|
|
//initialize the BSF to just point to the file
|
|
FLO flo;
|
|
|
|
flo.pfil = _pfil;
|
|
flo.fp = 0;
|
|
flo.cb = _pfil->FpMac();
|
|
if (!_pbsf->FReplaceFlo(&flo, fFalse, 0, 0))
|
|
return fFalse;
|
|
}
|
|
AssertThis(0);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Create a new TXDD to display the TXDC.
|
|
***************************************************************************/
|
|
PDDG TXDC::PddgNew(PGCB pgcb)
|
|
{
|
|
AssertThis(0);
|
|
return TXDD::PtxddNew(this, pgcb, _pbsf, vpappb->OnnDefFixed(),
|
|
fontNil, vpappb->DypTextDef());
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the current FNI for the doc. Return false if the doc is not
|
|
currently based on an FNI (it's a new doc or an internal one).
|
|
***************************************************************************/
|
|
bool TXDC::FGetFni(FNI *pfni)
|
|
{
|
|
AssertThis(0);
|
|
AssertBasePo(pfni, 0);
|
|
if (pvNil == _pfil || _pfil->FTemp())
|
|
return fFalse;
|
|
|
|
_pfil->GetFni(pfni);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Save the document and optionally set this fni as the current one.
|
|
If the doc is currently based on an FNI, pfni may be nil, indicating
|
|
that this is a normal save (not save as). If pfni is not nil and
|
|
fSetFni is false, this just writes a copy of the doc but doesn't change
|
|
the doc one bit.
|
|
***************************************************************************/
|
|
bool TXDC::FSaveToFni(FNI *pfni, bool fSetFni)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrPo(pfni, ffniFile);
|
|
FLO flo;
|
|
FNI fniT;
|
|
|
|
if (pvNil == pfni)
|
|
{
|
|
if (pvNil == _pfil)
|
|
{
|
|
Bug("Can't do a normal save - no file");
|
|
return fFalse;
|
|
}
|
|
_pfil->GetFni(&fniT);
|
|
pfni = &fniT;
|
|
fSetFni = fTrue;
|
|
}
|
|
|
|
if (pvNil == (flo.pfil = FIL::PfilCreateTemp(pfni)))
|
|
goto LFail;
|
|
|
|
flo.fp = 0;
|
|
flo.cb = _pbsf->IbMac();
|
|
if (!_pbsf->FWriteRgb(&flo))
|
|
goto LFail;
|
|
|
|
// redirect the BSF to the new file
|
|
if (fSetFni)
|
|
_pbsf->FReplaceFlo(&flo, fFalse, 0, flo.cb);
|
|
|
|
if (!flo.pfil->FSetFni(pfni))
|
|
{
|
|
LFail:
|
|
ReleasePpo(&flo.pfil);
|
|
return fFalse;
|
|
}
|
|
flo.pfil->SetTemp(fFalse);
|
|
|
|
if (fSetFni)
|
|
{
|
|
ReleasePpo(&_pfil);
|
|
_pfil = flo.pfil;
|
|
_fDirty = fFalse;
|
|
}
|
|
else
|
|
ReleasePpo(&flo.pfil);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a TXDC.
|
|
***************************************************************************/
|
|
void TXDC::AssertValid(ulong grf)
|
|
{
|
|
TXDC_PAR::AssertValid(0);
|
|
AssertPo(_pbsf, 0);
|
|
AssertNilOrPo(_pfil, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the TXDC.
|
|
***************************************************************************/
|
|
void TXDC::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
TXDC_PAR::MarkMem();
|
|
MarkMemObj(_pbsf);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for a text document display gob.
|
|
***************************************************************************/
|
|
TXDD::TXDD(PDOCB pdocb, PGCB pgcb, PBSF pbsf, long onn, ulong grfont, long dypFont)
|
|
: DDG(pdocb, pgcb)
|
|
{
|
|
AssertPo(pbsf, 0);
|
|
Assert(vntl.FValidOnn(onn), "bad onn");
|
|
AssertIn(dypFont, 1, kswMax);
|
|
|
|
_pbsf = pbsf;
|
|
_onn = onn;
|
|
_grfont = grfont;
|
|
_dypFont = dypFont;
|
|
|
|
//get the _dypLine and _dxpTab values
|
|
RC rc;
|
|
achar ch = kchSpace;
|
|
GNV gnv(this);
|
|
|
|
gnv.SetFont(_onn, _grfont, _dypFont);
|
|
gnv.GetRcFromRgch(&rc, &ch, 1, 0, 0);
|
|
_dypLine = rc.Dyp();
|
|
_dxpTab = LwMul(rc.Dxp(), 4);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for TXDD.
|
|
***************************************************************************/
|
|
TXDD::~TXDD(void)
|
|
{
|
|
ReleasePpo(&_pglichStarts);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Create a new TXDD.
|
|
***************************************************************************/
|
|
PTXDD TXDD::PtxddNew(PDOCB pdocb, PGCB pgcb, PBSF pbsf,
|
|
long onn, ulong grfont, long dypFont)
|
|
{
|
|
PTXDD ptxdd;
|
|
|
|
if (pvNil == (ptxdd = NewObj TXDD(pdocb, pgcb, pbsf, onn, grfont, dypFont)))
|
|
return pvNil;
|
|
|
|
if (!ptxdd->_FInit())
|
|
{
|
|
ReleasePpo(&ptxdd);
|
|
return pvNil;
|
|
}
|
|
ptxdd->Activate(fTrue);
|
|
|
|
AssertPo(ptxdd, 0);
|
|
return ptxdd;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize the TXDD.
|
|
***************************************************************************/
|
|
bool TXDD::_FInit(void)
|
|
{
|
|
long ib;
|
|
|
|
if (!TXDD_PAR::_FInit())
|
|
return fFalse;
|
|
if (pvNil == (_pglichStarts = GL::PglNew(size(long))))
|
|
return fFalse;
|
|
|
|
_pglichStarts->SetMinGrow(20);
|
|
ib = 0;
|
|
if (!_pglichStarts->FPush(&ib))
|
|
return fFalse;
|
|
_Reformat(0);
|
|
AssertThis(0);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
The TXDD has changed sizes, set the _clnDisp.
|
|
***************************************************************************/
|
|
void TXDD::_NewRc(void)
|
|
{
|
|
AssertThis(0);
|
|
RC rc;
|
|
|
|
GetRc(&rc, cooLocal);
|
|
_clnDisp = LwMax(1, LwDivAway(rc.Dyp(), _dypLine));
|
|
_clnDispWhole = LwMax(1, rc.Dyp()/ _dypLine);
|
|
_Reformat(_clnDisp);
|
|
TXDD_PAR::_NewRc();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Deactivate the TXDD - turn off the selection.
|
|
***************************************************************************/
|
|
void TXDD::_Activate(bool fActive)
|
|
{
|
|
AssertThis(0);
|
|
TXDD_PAR::_Activate(fActive);
|
|
if (!fActive)
|
|
_SwitchSel(fFalse, fFalse);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find new line starts starting at lnMin.
|
|
***************************************************************************/
|
|
void TXDD::_Reformat(long lnMin, long *pclnIns, long *pclnDel)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(lnMin, 0, kcbMax);
|
|
long ich, ichT;
|
|
long ln, clnIns, clnDel;
|
|
|
|
lnMin = LwMin(lnMin, _pglichStarts->IvMac() - 1);
|
|
ich = *_QichLn(lnMin);
|
|
clnIns = clnDel = 0;
|
|
for (ln = lnMin + 1; ln <= _clnDisp; ln++)
|
|
{
|
|
if (!_FFindNextLineStart(ich, &ich))
|
|
{
|
|
_pglichStarts->FSetIvMac(ln);
|
|
return;
|
|
}
|
|
|
|
//delete starts that are before ich
|
|
for (;;)
|
|
{
|
|
if (ln >= _pglichStarts->IvMac())
|
|
break;
|
|
ichT = *_QichLn(ln);
|
|
if (ichT == ich)
|
|
goto LDone;
|
|
if (ichT > ich)
|
|
break;
|
|
_pglichStarts->Delete(ln);
|
|
clnDel++;
|
|
}
|
|
|
|
//add the new line start
|
|
if (!_pglichStarts->FInsert(ln, &ich))
|
|
{
|
|
if (ln >= _pglichStarts->IvMac())
|
|
{
|
|
Warn("Reformatting failed");
|
|
return;
|
|
}
|
|
*_QichLn(ln) = ich;
|
|
}
|
|
clnIns++;
|
|
}
|
|
|
|
LDone:
|
|
//truncate the list of line starts
|
|
if (_pglichStarts->IvMac() > _clnDisp + 1)
|
|
_pglichStarts->FSetIvMac(_clnDisp + 1);
|
|
if (pvNil != pclnIns)
|
|
*pclnIns = clnIns;
|
|
if (pvNil != pclnDel)
|
|
*pclnDel = clnDel;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find new line starts starting at lnMin.
|
|
***************************************************************************/
|
|
void TXDD::_ReformatEdit(long ichMinEdit, long cchIns, long cchDel,
|
|
long *plnNew, long *pclnIns, long *pclnDel)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(ichMinEdit, 0, kcbMax);
|
|
AssertIn(cchIns, 0, _pbsf->IbMac() - ichMinEdit + 1);
|
|
AssertIn(cchDel, 0, kcbMax);
|
|
AssertNilOrVarMem(plnNew);
|
|
AssertNilOrVarMem(pclnIns);
|
|
AssertNilOrVarMem(pclnDel);
|
|
long ich;
|
|
long ln, clnDel;
|
|
|
|
// if the first displayed character was affected, reset it
|
|
// to a valid line start
|
|
if (FIn(*_QichLn(0) - 1, ichMinEdit, ichMinEdit + cchDel))
|
|
{
|
|
AssertDo(_FFindLineStart(ichMinEdit, &ich), 0);
|
|
*_QichLn(0) = ich;
|
|
}
|
|
|
|
//skip unaffected lines
|
|
for (ln = 0; ln < _pglichStarts->IvMac() && *_QichLn(ln) <= ichMinEdit; ln++)
|
|
;
|
|
|
|
clnDel = 0;
|
|
if (cchDel > 0)
|
|
{
|
|
//remove any deleted lines
|
|
while (ln < _pglichStarts->IvMac() && *_QichLn(ln) <= ichMinEdit + cchDel)
|
|
{
|
|
_pglichStarts->Delete(ln);
|
|
clnDel++;
|
|
}
|
|
}
|
|
|
|
if (cchIns != cchDel)
|
|
{
|
|
long lnT;
|
|
|
|
for (lnT = ln; lnT < _pglichStarts->IvMac(); lnT++)
|
|
*_QichLn(lnT) += cchIns - cchDel;
|
|
}
|
|
|
|
_Reformat(LwMax(0, ln - 1), pclnIns);
|
|
_Reformat(_clnDisp);
|
|
if (pvNil != plnNew)
|
|
*plnNew = ln;
|
|
if (pvNil != pclnDel)
|
|
*pclnDel = clnDel;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fetch a character of the stream through the cache.
|
|
***************************************************************************/
|
|
bool TXDD::_FFetchCh(long ich, achar *pch)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(_ichMinCache, 0, _pbsf->IbMac() + 1);
|
|
AssertIn(_ichLimCache, _ichMinCache, _pbsf->IbMac() + 1);
|
|
|
|
if (!FIn(ich, _ichMinCache, _ichLimCache))
|
|
{
|
|
//not a cache hit
|
|
long ichMinCache, ichLimCache;
|
|
long ichLim = _pbsf->IbMac();
|
|
|
|
if (!FIn(ich, 0, ichLim))
|
|
{
|
|
TrashVar(pch);
|
|
return fFalse;
|
|
}
|
|
|
|
//need to fetch some characters - try to center ich in the new cached data
|
|
ichMinCache = LwMax(0,
|
|
LwMin(ich - size(_rgchCache) / 2, ichLim - size(_rgchCache)));
|
|
ichLimCache = LwMin(ichLim, ichMinCache + size(_rgchCache));
|
|
AssertIn(ich, ichMinCache, ichLimCache);
|
|
|
|
//see if we can use some of the currently cached characters
|
|
if (_ichMinCache >= _ichLimCache)
|
|
goto LFetchAll;
|
|
if (FIn(_ichMinCache, ich, ichLimCache))
|
|
{
|
|
if (ichLimCache > _ichLimCache)
|
|
{
|
|
ichLimCache = _ichLimCache;
|
|
ichMinCache = LwMax(0, ichLimCache - size(_rgchCache));
|
|
}
|
|
BltPb(_rgchCache, _rgchCache + (_ichMinCache - ichMinCache),
|
|
ichLimCache - _ichMinCache);
|
|
_pbsf->FetchRgb(ichMinCache, _ichMinCache - ichMinCache,
|
|
_rgchCache);
|
|
}
|
|
else if (FIn(_ichLimCache, ichMinCache, ich + 1))
|
|
{
|
|
if (ichMinCache < _ichMinCache)
|
|
{
|
|
ichMinCache = _ichMinCache;
|
|
ichLimCache = LwMin(ichLim, ichMinCache + size(_rgchCache));
|
|
}
|
|
BltPb(_rgchCache + (ichMinCache - _ichMinCache), _rgchCache,
|
|
_ichLimCache - ichMinCache);
|
|
_pbsf->FetchRgb(_ichLimCache, ichLimCache - _ichLimCache,
|
|
_rgchCache + (_ichLimCache - ichMinCache));
|
|
}
|
|
else
|
|
{
|
|
LFetchAll:
|
|
_pbsf->FetchRgb(ichMinCache, ichLimCache - ichMinCache, _rgchCache);
|
|
}
|
|
_ichMinCache = ichMinCache;
|
|
_ichLimCache = ichLimCache;
|
|
AssertIn(ich, _ichMinCache, _ichLimCache);
|
|
}
|
|
|
|
*pch = _rgchCache[ich - _ichMinCache];
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the start of the line that ich is on.
|
|
***************************************************************************/
|
|
bool TXDD::_FFindLineStart(long ich, long *pich)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pich);
|
|
achar ch;
|
|
long dichLine = 0;
|
|
long ichOrig = ich;
|
|
|
|
if (ich > 0 && _FFetchCh(ich, &ch) && ch == kchLineFeed)
|
|
dichLine++;
|
|
|
|
for (;;)
|
|
{
|
|
if (!_FFetchCh(--ich, &ch))
|
|
{
|
|
if (ich == -1)
|
|
{
|
|
*pich = 0;
|
|
return fTrue;
|
|
}
|
|
TrashVar(pich);
|
|
return fFalse;
|
|
}
|
|
|
|
switch (ch)
|
|
{
|
|
case kchLineFeed:
|
|
dichLine++;
|
|
break;
|
|
case kchReturn:
|
|
if ((*pich = ich + 1 + dichLine) <= ichOrig)
|
|
return fTrue;
|
|
//fall through
|
|
default:
|
|
dichLine = 0;
|
|
break;
|
|
}
|
|
}
|
|
Bug("How did we get here?");
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the next line start after ich. If prgch is not nil, fills it
|
|
with the characters between ich and the line start (but not more
|
|
than cchMax characters).
|
|
***************************************************************************/
|
|
bool TXDD::_FFindNextLineStart(long ich, long *pich, achar *prgch, long cchMax)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pich);
|
|
AssertIn(cchMax, 0, kcbMax);
|
|
achar ch;
|
|
bool fCr = fFalse;
|
|
|
|
if (pvNil != prgch)
|
|
AssertPvCb(prgch, cchMax);
|
|
else
|
|
cchMax = 0;
|
|
|
|
for ( ; ; ich++)
|
|
{
|
|
if (!_FFetchCh(ich, &ch))
|
|
{
|
|
if (fCr && ich == _pbsf->IbMac())
|
|
{
|
|
*pich = ich;
|
|
return fTrue;
|
|
}
|
|
TrashVar(pich);
|
|
return fFalse;
|
|
}
|
|
|
|
switch (ch)
|
|
{
|
|
case kchLineFeed:
|
|
break;
|
|
case kchReturn:
|
|
if (!fCr)
|
|
{
|
|
fCr = fTrue;
|
|
break;
|
|
}
|
|
//fall through
|
|
default:
|
|
if (fCr)
|
|
{
|
|
*pich = ich;
|
|
return fTrue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (cchMax > 0)
|
|
{
|
|
*prgch++ = ch;
|
|
cchMax--;
|
|
}
|
|
}
|
|
Bug("how did we get here?");
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the start of the line that ich is on. This routine assumes
|
|
that _pglichStarts is valid and tries to use it.
|
|
***************************************************************************/
|
|
bool TXDD::_FFindLineStartCached(long ich, long *pich)
|
|
{
|
|
AssertThis(0);
|
|
long ln = _LnFromIch(ich);
|
|
|
|
if (FIn(ln, 0, _clnDisp))
|
|
{
|
|
*pich = *_QichLn(ln);
|
|
return fTrue;
|
|
}
|
|
return _FFindLineStart(ich, pich);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the next line start after ich. If prgch is not nil, fills it
|
|
with the characters between ich and the line start (but not more
|
|
than cchMax characters).
|
|
***************************************************************************/
|
|
bool TXDD::_FFindNextLineStartCached(long ich, long *pich, achar *prgch, long cchMax)
|
|
{
|
|
AssertThis(0);
|
|
if (pvNil == prgch || cchMax == 0)
|
|
{
|
|
long ln = _LnFromIch(ich);
|
|
|
|
if (FIn(ln, 0, _pglichStarts->IvMac() - 1))
|
|
{
|
|
*pich = *_QichLn(ln + 1);
|
|
return fTrue;
|
|
}
|
|
}
|
|
|
|
return _FFindNextLineStart(ich, pich, prgch, cchMax);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draw the contents of the gob.
|
|
***************************************************************************/
|
|
void TXDD::Draw(PGNV pgnv, RC *prcClip)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pgnv, 0);
|
|
AssertVarMem(prcClip);
|
|
RC rc;
|
|
long yp;
|
|
long ln, lnLim;
|
|
long cchDraw;
|
|
achar rgch[kcchMaxLine];
|
|
|
|
GetRc(&rc, cooLocal);
|
|
if (!rc.FIntersect(prcClip))
|
|
return;
|
|
|
|
pgnv->SetFont(_onn, _grfont, _dypFont);
|
|
ln = rc.ypTop / _dypLine;
|
|
yp = LwMul(ln, _dypLine);
|
|
lnLim = LwMin(_pglichStarts->IvMac(), LwDivAway(rc.ypBottom, _dypLine));
|
|
for ( ; ln < lnLim; yp += _dypLine)
|
|
{
|
|
_FetchLineLn(ln++, rgch, size(rgch), &cchDraw);
|
|
_DrawLine(pgnv, prcClip, yp, rgch, cchDraw);
|
|
}
|
|
rc.ypTop = yp;
|
|
pgnv->FillRc(&rc, kacrWhite);
|
|
if (_fSelOn)
|
|
_InvertSel(pgnv, fTrue);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fetch the characters for the given line.
|
|
***************************************************************************/
|
|
void TXDD::_FetchLineLn(long ln, achar *prgch, long cchMax, long *pcch, long *pichMin)
|
|
{
|
|
AssertThis(0);
|
|
long ichMin, ichLim;
|
|
|
|
ichMin = _IchMinLn(ln);
|
|
ichLim = _IchMinLn(ln + 1);
|
|
*pcch = LwMin(cchMax, ichLim - ichMin);
|
|
_pbsf->FetchRgb(ichMin, *pcch, prgch);
|
|
if (pvNil != pichMin)
|
|
*pichMin = ichMin;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fetch the characters for the given line.
|
|
***************************************************************************/
|
|
void TXDD::_FetchLineIch(long ich, achar *prgch, long cchMax, long *pcch, long *pichMin)
|
|
{
|
|
AssertThis(0);
|
|
long ichMin, ichLim;
|
|
|
|
AssertDo(_FFindLineStartCached(ich, &ichMin), 0);
|
|
if (!_FFindNextLineStartCached(ichMin, &ichLim, prgch, cchMax))
|
|
ichLim = _pbsf->IbMac();
|
|
*pcch = LwMin(cchMax, ichLim - ichMin);
|
|
if (pvNil != pichMin)
|
|
*pichMin = ichMin;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draw the line in the given GNV.
|
|
***************************************************************************/
|
|
void TXDD::_DrawLine(PGNV pgnv, RC *prcClip, long yp, achar *prgch, long cch)
|
|
{
|
|
AssertThis(0);
|
|
long xp, xpOrigin, xpPrev;
|
|
long ich, ichMin;
|
|
RC rc;
|
|
|
|
xpPrev = 0;
|
|
xp = xpOrigin = kdxpIndentTxdd - _scvHorz;
|
|
ich = 0;
|
|
while (ich < cch)
|
|
{
|
|
while (ich < cch)
|
|
{
|
|
switch (prgch[ich])
|
|
{
|
|
case kchTab:
|
|
xp = xpOrigin + LwRoundAway(xp - xpOrigin + 1, _dxpTab);
|
|
break;
|
|
case kchReturn:
|
|
case kchLineFeed:
|
|
break;
|
|
default:
|
|
goto LNonWhite;
|
|
}
|
|
ich++;
|
|
}
|
|
|
|
LNonWhite:
|
|
//erase any blank portion of the line
|
|
if (xp > xpPrev && xp > prcClip->xpLeft && xpPrev < prcClip->xpRight)
|
|
{
|
|
rc.Set(xpPrev, yp, xp, yp + _dypLine);
|
|
pgnv->FillRc(&rc, kacrWhite);
|
|
}
|
|
|
|
ichMin = ich;
|
|
while (ich < cch)
|
|
{
|
|
switch (prgch[ich])
|
|
{
|
|
case kchTab:
|
|
case kchReturn:
|
|
case kchLineFeed:
|
|
goto LEndRun;
|
|
}
|
|
ich++;
|
|
}
|
|
|
|
LEndRun:
|
|
if (ich > ichMin)
|
|
{
|
|
pgnv->GetRcFromRgch(&rc, prgch + ichMin, ich - ichMin);
|
|
if (xp + rc.Dxp() > prcClip->xpLeft && xp < prcClip->xpRight)
|
|
pgnv->DrawRgch(prgch + ichMin, ich - ichMin, xp, yp, kacrBlack, kacrWhite);
|
|
xp += rc.Dxp();
|
|
}
|
|
xpPrev = xp;
|
|
}
|
|
|
|
//erase any remaining portion of the line
|
|
if (xpPrev < prcClip->xpRight)
|
|
{
|
|
rc.Set(xpPrev, yp, prcClip->xpRight, yp + _dypLine);
|
|
pgnv->FillRc(&rc, kacrWhite);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the maximum scroll value for this view of the doc.
|
|
***************************************************************************/
|
|
long TXDD::_ScvMax(bool fVert)
|
|
{
|
|
if (fVert)
|
|
{
|
|
long ich;
|
|
|
|
if (!_FFindLineStartCached(_pbsf->IbMac() - 1, &ich))
|
|
ich = 0;
|
|
return ich;
|
|
}
|
|
|
|
return LwMul(_dxpTab, 100);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Perform a scroll according to scaHorz and scaVert.
|
|
***************************************************************************/
|
|
void TXDD::_Scroll(long scaHorz, long scaVert, long scvHorz, long scvVert)
|
|
{
|
|
AssertThis(0);
|
|
RC rc;
|
|
long dxp, dyp;
|
|
|
|
GetRc(&rc, cooLocal);
|
|
dxp = 0;
|
|
switch (scaHorz)
|
|
{
|
|
default:
|
|
Assert(scaHorz == scaNil, "bad scaHorz");
|
|
break;
|
|
case scaPageUp:
|
|
dxp = -LwMulDiv(rc.Dxp(), 9, 10);
|
|
goto LHorz;
|
|
case scaPageDown:
|
|
dxp = LwMulDiv(rc.Dxp(), 9, 10);
|
|
goto LHorz;
|
|
case scaLineUp:
|
|
dxp = -rc.Dxp() / 10;
|
|
goto LHorz;
|
|
case scaLineDown:
|
|
dxp = rc.Dxp() / 10;
|
|
goto LHorz;
|
|
case scaToVal:
|
|
dxp = scvHorz - _scvHorz;
|
|
LHorz:
|
|
dxp = LwBound(_scvHorz + dxp, 0, _ScvMax(fFalse) + 1) - _scvHorz;
|
|
_scvHorz += dxp;
|
|
break;
|
|
}
|
|
|
|
dyp = 0;
|
|
if (scaVert != scaNil)
|
|
{
|
|
long ichMin;
|
|
long ichMinNew;
|
|
long cln;
|
|
long ichT;
|
|
|
|
ichMin = *_QichLn(0);
|
|
switch (scaVert)
|
|
{
|
|
default:
|
|
Bug("bad scaVert");
|
|
ichMinNew = ichMin;
|
|
break;
|
|
case scaToVal:
|
|
scvVert = LwBound(scvVert, 0, _ScvMax(fTrue) + 1);
|
|
AssertDo(_FFindLineStartCached(scvVert, &ichMinNew), 0);
|
|
|
|
for (cln = 0, ichT = LwMin(ichMin, ichMinNew);
|
|
cln < _clnDisp && _FFindNextLineStartCached(ichT, &ichT) &&
|
|
(ichT <= ichMin || ichT <= ichMinNew); )
|
|
{
|
|
cln++;
|
|
}
|
|
|
|
if (ichMin > ichMinNew)
|
|
cln = -cln;
|
|
dyp = LwMul(cln, _dypLine);
|
|
break;
|
|
|
|
case scaPageDown:
|
|
cln = LwMax(1, _clnDispWhole - 1);
|
|
goto LDown;
|
|
case scaLineDown:
|
|
cln = 1;
|
|
LDown:
|
|
cln = LwMin(cln, _pglichStarts->IvMac() - 1);
|
|
dyp = LwMul(cln, _dypLine);
|
|
ichMinNew = *_QichLn(cln);
|
|
break;
|
|
|
|
case scaPageUp:
|
|
cln = LwMax(1, _clnDispWhole - 1);
|
|
goto LUp;
|
|
case scaLineUp:
|
|
cln = 1;
|
|
LUp:
|
|
ichMinNew = ichMin;
|
|
while (cln-- > 0 && _FFindLineStart(ichMinNew - 1, &ichT))
|
|
{
|
|
dyp -= _dypLine;
|
|
ichMinNew = ichT;
|
|
}
|
|
break;
|
|
}
|
|
_scvVert = ichMinNew;
|
|
if (ichMinNew != ichMin)
|
|
{
|
|
*_QichLn(0) = ichMinNew;
|
|
_Reformat(0);
|
|
_Reformat(_clnDisp);
|
|
}
|
|
}
|
|
|
|
_SetScrollValues();
|
|
if (dxp != 0 || dyp != 0)
|
|
_ScrollDxpDyp(dxp, dyp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Do idle processing. If this handler has the active selection, make sure
|
|
the selection is on or off according to rglw[0] (non-zero means on)
|
|
and set rglw[0] to false. Always return false.
|
|
***************************************************************************/
|
|
bool TXDD::FCmdSelIdle(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
|
|
//if rglw[1] is this one's hid, don't change the sel state.
|
|
if (pcmd->rglw[1] != Hid())
|
|
{
|
|
if (!pcmd->rglw[0])
|
|
_SwitchSel(fFalse, fFalse);
|
|
else if (_ichAnchor != _ichOther || _tsSel == 0)
|
|
_SwitchSel(fTrue, fTrue);
|
|
else if (DtsCaret() < TsCurrent() - _tsSel)
|
|
_SwitchSel(!_fSelOn, fTrue);
|
|
}
|
|
pcmd->rglw[0] = fFalse;
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the selection.
|
|
***************************************************************************/
|
|
void TXDD::SetSel(long ichAnchor, long ichOther, bool fDraw)
|
|
{
|
|
AssertThis(0);
|
|
long ichMac = _pbsf->IbMac();
|
|
|
|
ichAnchor = LwBound(ichAnchor, 0, ichMac + 1);
|
|
ichOther = LwBound(ichOther, 0, ichMac + 1);
|
|
|
|
if (ichAnchor == _ichAnchor && ichOther == _ichOther)
|
|
return;
|
|
|
|
if (_fSelOn)
|
|
{
|
|
GNV gnv(this);
|
|
|
|
if (_ichAnchor != ichAnchor || _ichAnchor == _ichOther ||
|
|
ichAnchor == ichOther)
|
|
{
|
|
_InvertSel(&gnv, fDraw);
|
|
_ichAnchor = ichAnchor;
|
|
_ichOther = ichOther;
|
|
_InvertSel(&gnv, fDraw);
|
|
_tsSel = TsCurrent();
|
|
}
|
|
else
|
|
{
|
|
//they have the same anchor and neither is an insertion
|
|
_InvertIchRange(&gnv, _ichOther, ichOther, fDraw);
|
|
_ichOther = ichOther;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_ichAnchor = ichAnchor;
|
|
_ichOther = ichOther;
|
|
_tsSel = 0L;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Turn the sel on or off according to fOn.
|
|
***************************************************************************/
|
|
void TXDD::_SwitchSel(bool fOn, bool fDraw)
|
|
{
|
|
AssertThis(0);
|
|
if (FPure(fOn) != FPure(_fSelOn))
|
|
{
|
|
GNV gnv(this);
|
|
|
|
_InvertSel(&gnv, fDraw);
|
|
_fSelOn = FPure(fOn);
|
|
_tsSel = TsCurrent();
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Invert the current selection.
|
|
***************************************************************************/
|
|
void TXDD::_InvertSel(PGNV pgnv, bool fDraw)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pgnv, 0);
|
|
RC rc, rcT;
|
|
long ln;
|
|
|
|
if (_ichAnchor == _ichOther)
|
|
{
|
|
//insertion bar
|
|
ln = _LnFromIch(_ichAnchor);
|
|
if (!FIn(ln, 0, _clnDisp))
|
|
return;
|
|
|
|
rc.xpLeft = _XpFromLnIch(pgnv, ln, _ichAnchor) - 1;
|
|
rc.xpRight = rc.xpLeft + 2;
|
|
rc.ypTop = LwMul(ln, _dypLine);
|
|
rc.ypBottom = rc.ypTop + _dypLine;
|
|
GetRc(&rcT, cooLocal);
|
|
if (rcT.FIntersect(&rc))
|
|
{
|
|
if (fDraw)
|
|
pgnv->FillRc(&rcT, kacrInvert);
|
|
else
|
|
InvalRc(&rcT, kginMark);
|
|
}
|
|
}
|
|
else
|
|
_InvertIchRange(pgnv, _ichAnchor, _ichOther, fDraw);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Invert a range.
|
|
***************************************************************************/
|
|
void TXDD::_InvertIchRange(PGNV pgnv, long ich1, long ich2, bool fDraw)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pgnv, 0);
|
|
AssertIn(ich1, 0, _pbsf->IbMac() + 1);
|
|
AssertIn(ich2, 0, _pbsf->IbMac() + 1);
|
|
RC rc, rcClip, rcT;
|
|
long ln1, ln2, xp2;
|
|
|
|
if (ich1 == ich2)
|
|
return;
|
|
SortLw(&ich1, &ich2);
|
|
ln1 = _LnFromIch(ich1);
|
|
ln2 = _LnFromIch(ich2);
|
|
if (ln1 >= _clnDisp || ln2 < 0)
|
|
return;
|
|
|
|
GetRc(&rcClip, cooLocal);
|
|
rc.xpLeft = _XpFromLnIch(pgnv, ln1, ich1);
|
|
rc.ypTop = LwMul(ln1, _dypLine);
|
|
rc.ypBottom = LwMul(ln1 + 1, _dypLine);
|
|
xp2 = _XpFromLnIch(pgnv, ln2, ich2);
|
|
|
|
if (ln2 == ln1)
|
|
{
|
|
//only one line involved
|
|
rc.xpRight = xp2;
|
|
if (rcT.FIntersect(&rc, &rcClip))
|
|
{
|
|
if (fDraw)
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
else
|
|
InvalRc(&rcT);
|
|
}
|
|
return;
|
|
}
|
|
|
|
//invert the sel on the first line
|
|
rc.xpRight = rcClip.xpRight;
|
|
if (rcT.FIntersect(&rc, &rcClip))
|
|
{
|
|
if (fDraw)
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
else
|
|
InvalRc(&rcT);
|
|
}
|
|
|
|
//invert the main rectangular block
|
|
rc.xpLeft = kdxpIndentTxdd - _scvHorz;
|
|
rc.ypTop = rc.ypBottom;
|
|
rc.ypBottom = LwMul(ln2, _dypLine);
|
|
if (rcT.FIntersect(&rc, &rcClip))
|
|
{
|
|
if (fDraw)
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
else
|
|
InvalRc(&rcT);
|
|
}
|
|
|
|
//invert the last line
|
|
rc.ypTop = rc.ypBottom;
|
|
rc.ypBottom = LwMul(ln2 + 1, _dypLine);
|
|
rc.xpRight = xp2;
|
|
if (rcT.FIntersect(&rc, &rcClip))
|
|
{
|
|
if (fDraw)
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
else
|
|
InvalRc(&rcT);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the line in the TXDD that is displaying the given ich. Returns -1
|
|
if the ich is before the first displayed ich and returns _clnDisp if
|
|
ich is after the last displayed ich.
|
|
***************************************************************************/
|
|
long TXDD::_LnFromIch(long ich)
|
|
{
|
|
AssertThis(0);
|
|
long lnMin, lnLim, ln;
|
|
long ichT;
|
|
|
|
lnMin = 0;
|
|
lnLim = LwMin(_clnDisp + 1, _pglichStarts->IvMac());
|
|
while (lnMin < lnLim)
|
|
{
|
|
ln = (lnMin + lnLim) / 2;
|
|
ichT = *_QichLn(ln);
|
|
if (ichT < ich)
|
|
lnMin = ln + 1;
|
|
else if (ichT > ich)
|
|
lnLim = ln;
|
|
else
|
|
return ln;
|
|
}
|
|
|
|
return lnMin - 1;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the ich of the first character on the given line. If ln < 0,
|
|
returns 0; if ln >= _clnDisp, returns IbMac().
|
|
***************************************************************************/
|
|
long TXDD::_IchMinLn(long ln)
|
|
{
|
|
AssertThis(0);
|
|
long ich;
|
|
|
|
if (ln < 0)
|
|
return 0;
|
|
if (ln >= _pglichStarts->IvMac())
|
|
return _pbsf->IbMac();
|
|
ich = *_QichLn(ln);
|
|
return ich;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the xp location of the given ich on the given line.
|
|
***************************************************************************/
|
|
long TXDD::_XpFromLnIch(PGNV pgnv, long ln, long ich)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pgnv, 0);
|
|
RC rc;
|
|
long cch, ichT;
|
|
achar rgch[kcchMaxLine];
|
|
|
|
if (!FIn(ln, 0, LwMin(_clnDisp, _pglichStarts->IvMac())))
|
|
return 0;
|
|
|
|
ichT = *_QichLn(ln);
|
|
_FetchLineLn(ln, rgch, LwMin(ich - ichT, size(rgch)), &cch);
|
|
return _XpFromRgch(pgnv, rgch, cch);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the xp location of the given ich.
|
|
***************************************************************************/
|
|
long TXDD::_XpFromIch(long ich)
|
|
{
|
|
AssertThis(0);
|
|
long ichMin, cch;
|
|
achar rgch[kcchMaxLine];
|
|
|
|
if (!_FFindLineStartCached(ich, &ichMin))
|
|
return 0;
|
|
|
|
GNV gnv(this);
|
|
|
|
cch = LwMin(size(rgch), ich - ichMin);
|
|
_pbsf->FetchRgb(ichMin, cch, rgch);
|
|
return _XpFromRgch(&gnv, rgch, cch);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the xp location of the end of the given (rgch, cch), assuming
|
|
it starts at the beginning of a line.
|
|
***************************************************************************/
|
|
long TXDD::_XpFromRgch(PGNV pgnv, achar *prgch, long cch)
|
|
{
|
|
AssertThis(0);
|
|
long xp, xpOrigin;
|
|
long ich, ichMin;
|
|
RC rc;
|
|
|
|
pgnv->SetFont(_onn, _grfont, _dypFont);
|
|
xp = xpOrigin = kdxpIndentTxdd - _scvHorz;
|
|
ich = 0;
|
|
while (ich < cch)
|
|
{
|
|
while (ich < cch)
|
|
{
|
|
switch (prgch[ich])
|
|
{
|
|
case kchTab:
|
|
xp = xpOrigin + LwRoundAway(xp - xpOrigin + 1, _dxpTab);
|
|
break;
|
|
case kchReturn:
|
|
case kchLineFeed:
|
|
break;
|
|
default:
|
|
goto LNonWhite;
|
|
}
|
|
ich++;
|
|
}
|
|
|
|
LNonWhite:
|
|
ichMin = ich;
|
|
while (ich < cch)
|
|
{
|
|
switch (prgch[ich])
|
|
{
|
|
case kchTab:
|
|
case kchReturn:
|
|
case kchLineFeed:
|
|
goto LEndRun;
|
|
}
|
|
ich++;
|
|
}
|
|
|
|
LEndRun:
|
|
if (ich > ichMin)
|
|
{
|
|
pgnv->GetRcFromRgch(&rc, prgch + ichMin, ich - ichMin);
|
|
xp += rc.Dxp();
|
|
}
|
|
}
|
|
|
|
return xp;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the character that is closest to xp on the given line.
|
|
***************************************************************************/
|
|
long TXDD::_IchFromLnXp(long ln, long xp)
|
|
{
|
|
AssertThis(0);
|
|
long ichMin, cch;
|
|
achar rgch[kcchMaxLine];
|
|
|
|
if (ln < 0)
|
|
return 0;
|
|
|
|
_FetchLineLn(ln, rgch, size(rgch), &cch, &ichMin);
|
|
return _IchFromRgchXp(rgch, cch, ichMin, xp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the character that is closest to xp on the same line as the given
|
|
character.
|
|
***************************************************************************/
|
|
long TXDD::_IchFromIchXp(long ich, long xp)
|
|
{
|
|
AssertThis(0);
|
|
long ichMin, cch;
|
|
achar rgch[kcchMaxLine];
|
|
|
|
_FetchLineIch(ich, rgch, size(rgch), &cch, &ichMin);
|
|
return _IchFromRgchXp(rgch, cch, ichMin, xp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the character that is closest to xp on the given line.
|
|
***************************************************************************/
|
|
long TXDD::_IchFromRgchXp(achar *prgch, long cch, long ichMinLine, long xp)
|
|
{
|
|
AssertThis(0);
|
|
long xpT;
|
|
long ich, ichMin, ichLim;
|
|
GNV gnv(this);
|
|
|
|
while (cch > 0 && (prgch[cch - 1] == kchReturn || prgch[cch - 1] == kchLineFeed))
|
|
cch--;
|
|
ichLim = ichMinLine + cch;
|
|
ichMin = ichMinLine;
|
|
while (ichMin < ichLim)
|
|
{
|
|
ich = (ichMin + ichLim) / 2;
|
|
xpT = _XpFromRgch(&gnv, prgch, ich - ichMinLine);
|
|
if (xpT < xp)
|
|
ichMin = ich + 1;
|
|
else
|
|
ichLim = ich;
|
|
}
|
|
if (ichMin > ichMinLine &&
|
|
LwAbs(xp - _XpFromRgch(&gnv, prgch, ichMin - 1 - ichMinLine))
|
|
< LwAbs(xp - _XpFromRgch(&gnv, prgch, ichMin - ichMinLine)))
|
|
{
|
|
ichMin--;
|
|
}
|
|
return ichMin;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Make sure the selection is visible (or at least _ichOther is).
|
|
***************************************************************************/
|
|
void TXDD::ShowSel(bool fDraw)
|
|
{
|
|
AssertThis(0);
|
|
long ln, lnHope;
|
|
long dxpScroll, dichScroll;
|
|
long xpMin, xpLim;
|
|
RC rc;
|
|
long ichAnchor = _ichAnchor;
|
|
|
|
//find the lines we want to show
|
|
ln = _LnFromIch(_ichOther);
|
|
lnHope = _LnFromIch(ichAnchor);
|
|
GetRc(&rc, cooLocal);
|
|
|
|
dichScroll = 0;
|
|
if (!FIn(ln, 0, _clnDispWhole) || !FIn(lnHope, 0, _clnDispWhole))
|
|
{
|
|
//count the number of lines between _ichOther and ichAnchor
|
|
long ichMinLine, ich;
|
|
long ichMin = LwMin(ichAnchor, _ichOther);
|
|
long ichLim = LwMax(ichAnchor, _ichOther);
|
|
long cln = 0;
|
|
|
|
AssertDo(_FFindLineStartCached(ichMin, &ichMinLine), 0);
|
|
for (ich = ichMin; cln < _clnDispWhole && _FFindNextLineStartCached(ich, &ich) &&
|
|
ich < ichLim; cln++)
|
|
{
|
|
}
|
|
|
|
if (cln >= _clnDispWhole)
|
|
{
|
|
//just show _ichOther
|
|
AssertDo(_FFindLineStartCached(_ichOther, &ichMinLine), 0);
|
|
ichAnchor = _ichOther;
|
|
lnHope = ln;
|
|
cln = 0;
|
|
}
|
|
|
|
if (ln < 0 || lnHope < 0)
|
|
{
|
|
//scroll up
|
|
dichScroll = ichMinLine - _scvVert;
|
|
}
|
|
else if (ln >= _clnDispWhole || lnHope >= _clnDispWhole)
|
|
{
|
|
//scroll down
|
|
cln = LwMax(0, _clnDispWhole - cln - 1);
|
|
|
|
//move cln lines back from ichMinLine
|
|
while (cln-- > 0 && _FFindLineStartCached(ichMinLine - 1, &ichMin))
|
|
ichMinLine = ichMin;
|
|
dichScroll = ichMinLine - _scvVert;
|
|
}
|
|
}
|
|
|
|
//now do the horizontal stuff
|
|
xpMin = _XpFromIch(_ichOther);
|
|
xpLim = _XpFromIch(ichAnchor);
|
|
if (LwAbs(xpLim - xpMin) > rc.Dxp())
|
|
{
|
|
//can't show both
|
|
if (xpMin > xpLim)
|
|
{
|
|
xpLim = xpMin;
|
|
xpMin = xpLim - rc.Dxp();
|
|
}
|
|
else
|
|
xpLim = xpMin + rc.Dxp();
|
|
}
|
|
else
|
|
SortLw(&xpMin, &xpLim);
|
|
dxpScroll = LwMax(LwMin(0, rc.xpRight - xpLim), rc.xpLeft - xpMin);
|
|
|
|
if (dxpScroll != 0 || dichScroll != 0)
|
|
{
|
|
_Scroll(scaToVal, scaToVal, _scvHorz - dxpScroll,
|
|
_scvVert + dichScroll);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Handle a mousedown in the TXDD.
|
|
***************************************************************************/
|
|
bool TXDD::FCmdTrackMouse(PCMD_MOUSE pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
RC rc;
|
|
long ich;
|
|
long scaHorz, scaVert;
|
|
long xp = pcmd->xp;
|
|
long yp = pcmd->yp;
|
|
|
|
if (pcmd->cid == cidMouseDown)
|
|
{
|
|
Assert(vpcex->PgobTracking() == pvNil, "mouse already being tracked!");
|
|
vpcex->TrackMouse(this);
|
|
}
|
|
else
|
|
{
|
|
Assert(vpcex->PgobTracking() == this, "not tracking mouse!");
|
|
Assert(pcmd->cid == cidTrackMouse, 0);
|
|
}
|
|
|
|
//do autoscrolling
|
|
GetRc(&rc, cooLocal);
|
|
if (!FIn(xp, rc.xpLeft, rc.xpRight))
|
|
{
|
|
scaHorz = (xp < rc.xpLeft) ? scaLineUp : scaLineDown;
|
|
xp = LwBound(xp, rc.xpLeft, rc.xpRight);
|
|
}
|
|
else
|
|
scaHorz = scaNil;
|
|
if (!FIn(yp, rc.ypTop, rc.ypBottom))
|
|
{
|
|
scaVert = (yp < rc.ypTop) ? scaLineUp : scaLineDown;
|
|
yp = LwBound(yp, rc.ypTop, rc.ypBottom);
|
|
}
|
|
else
|
|
scaVert = scaNil;
|
|
if (scaHorz != scaNil || scaVert != scaNil)
|
|
_Scroll(scaHorz, scaVert);
|
|
|
|
//set the selection
|
|
ich = _IchFromLnXp(yp / _dypLine, xp);
|
|
if (pcmd->cid != cidMouseDown || (pcmd->grfcust & fcustShift))
|
|
SetSel(_ichAnchor, ich, fTrue);
|
|
else
|
|
SetSel(ich, ich, fTrue);
|
|
_SwitchSel(fTrue, fTrue); //make sure the selection is on
|
|
ShowSel(fTrue);
|
|
|
|
if (!(pcmd->grfcust & fcustMouse))
|
|
vpcex->EndMouseTracking();
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Handle a key down.
|
|
***************************************************************************/
|
|
bool TXDD::FCmdKey(PCMD_KEY pcmd)
|
|
{
|
|
AssertThis(0);
|
|
const long kcchInsBuf = 64;
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
ulong grfcust;
|
|
long vkDone;
|
|
long dich, dln, ichLim, ichT, ichMin;
|
|
achar ch;
|
|
long cact;
|
|
CMD cmd;
|
|
achar rgch[kcchInsBuf + 1];
|
|
|
|
// keep fetching characters until we get a cursor key, delete key or
|
|
// until the buffer is full.
|
|
vkDone = vkNil;
|
|
ichLim = 0;
|
|
do
|
|
{
|
|
grfcust = pcmd->grfcust;
|
|
switch (pcmd->vk)
|
|
{
|
|
//these keys all terminate the key fetching loop
|
|
case kvkHome:
|
|
case kvkEnd:
|
|
case kvkLeft:
|
|
case kvkRight:
|
|
case kvkUp:
|
|
case kvkDown:
|
|
case kvkPageUp:
|
|
case kvkPageDown:
|
|
case kvkDelete:
|
|
case kvkBack:
|
|
vkDone = pcmd->vk;
|
|
goto LInsert;
|
|
|
|
default:
|
|
if (chNil == pcmd->ch)
|
|
break;
|
|
for (cact = 0; cact < pcmd->cact && ichLim < kcchInsBuf; cact++)
|
|
{
|
|
rgch[ichLim++] = (achar)pcmd->ch;
|
|
#ifdef WIN
|
|
if ((achar)pcmd->ch == kchReturn)
|
|
rgch[ichLim++] = kchLineFeed;
|
|
#endif //WIN
|
|
}
|
|
break;
|
|
}
|
|
|
|
pcmd = (PCMD_KEY)&cmd;
|
|
}
|
|
while (ichLim < kcchInsBuf && vpcex->FGetNextKey(&cmd));
|
|
|
|
LInsert:
|
|
if (ichLim > 0)
|
|
{
|
|
//have some characters to insert
|
|
FReplace(rgch, ichLim, _ichAnchor, _ichOther, fTrue);
|
|
}
|
|
|
|
dich = 0;
|
|
dln = 0;
|
|
switch (vkDone)
|
|
{
|
|
case kvkHome:
|
|
if (grfcust & fcustCmd)
|
|
dich = -_pbsf->IbMac() - ichLim - 1;
|
|
else if (_FFindLineStartCached(_ichOther, &ichT))
|
|
dich = ichT - _ichOther;
|
|
_fXpValid = fFalse;
|
|
goto LSetSel;
|
|
case kvkEnd:
|
|
if (grfcust & fcustCmd)
|
|
dich = _pbsf->IbMac() + ichLim + 1;
|
|
else
|
|
{
|
|
if (!_FFindNextLineStartCached(_ichOther, &ichT))
|
|
ichT = _pbsf->IbMac();
|
|
|
|
//don't advance past trailing line feed and return characters
|
|
while (ichT > _ichOther && _FFetchCh(ichT - 1, &ch) &&
|
|
(ch == kchReturn || ch == kchLineFeed))
|
|
{
|
|
ichT--;
|
|
}
|
|
dich = ichT - _ichOther;
|
|
}
|
|
_fXpValid = fFalse;
|
|
goto LSetSel;
|
|
case kvkLeft:
|
|
dich = -1;
|
|
while (_FFetchCh(_ichOther + dich, &ch) && ch == kchLineFeed)
|
|
dich--;
|
|
_fXpValid = fFalse;
|
|
goto LSetSel;
|
|
case kvkRight:
|
|
dich = 1;
|
|
while (_FFetchCh(_ichOther + dich, &ch) && ch == kchLineFeed)
|
|
dich++;
|
|
_fXpValid = fFalse;
|
|
goto LSetSel;
|
|
|
|
case kvkUp:
|
|
dln = -1;
|
|
goto LLineSel;
|
|
case kvkDown:
|
|
dln = 1;
|
|
goto LLineSel;
|
|
case kvkPageUp:
|
|
dln = -LwMax(1, _clnDispWhole - 1);
|
|
goto LLineSel;
|
|
case kvkPageDown:
|
|
dln = LwMax(1, _clnDispWhole - 1);
|
|
LLineSel:
|
|
if (!_fXpValid)
|
|
{
|
|
//get the xp of _ichOther
|
|
_xpSel = _XpFromIch(_ichOther) + _scvHorz;
|
|
_fXpValid = fTrue;
|
|
}
|
|
if (dln > 0)
|
|
{
|
|
ichMin = _ichOther;
|
|
while (dln-- > 0 && _FFindNextLineStartCached(ichMin, &ichT))
|
|
ichMin = ichT;
|
|
if (dln >= 0)
|
|
{
|
|
//goto end of doc
|
|
dich = _pbsf->IbMac() - _ichOther;
|
|
_fXpValid = fFalse;
|
|
}
|
|
else
|
|
goto LFindIch;
|
|
}
|
|
else
|
|
{
|
|
AssertDo(_FFindLineStartCached(_ichOther, &ichT), 0);
|
|
ichMin = ichT;
|
|
while (dln++ < 0 && _FFindLineStartCached(ichMin - 1, &ichT))
|
|
ichMin = ichT;
|
|
if (dln <= 0)
|
|
{
|
|
//goto top of doc
|
|
dich = -_ichOther;
|
|
_fXpValid = fFalse;
|
|
}
|
|
else
|
|
{
|
|
LFindIch:
|
|
//ichMin is the start of the line to move the selection to
|
|
dich = _IchFromIchXp(ichMin, _xpSel - _scvHorz) - _ichOther;
|
|
}
|
|
}
|
|
LSetSel:
|
|
//move the selection
|
|
if (grfcust & fcustShift)
|
|
{
|
|
//extend selection
|
|
SetSel(_ichAnchor, _ichOther + dich, fTrue);
|
|
ShowSel(fTrue);
|
|
}
|
|
else
|
|
{
|
|
long ichAnchor = _ichAnchor;
|
|
|
|
if (ichAnchor == _ichOther)
|
|
ichAnchor += dich;
|
|
else if ((dich > 0) != (ichAnchor > _ichOther))
|
|
ichAnchor = _ichOther;
|
|
SetSel(ichAnchor, ichAnchor, fTrue);
|
|
ShowSel(fTrue);
|
|
}
|
|
break;
|
|
|
|
case kvkDelete:
|
|
dich = 1;
|
|
goto LDelete;
|
|
case kvkBack:
|
|
dich = -1;
|
|
LDelete:
|
|
if (_ichAnchor != _ichOther)
|
|
dich = _ichOther - _ichAnchor;
|
|
else
|
|
dich = LwBound(_ichAnchor + dich, 0, _pbsf->IbMac() + 1) - _ichAnchor;
|
|
if (dich != 0)
|
|
FReplace(pvNil, 0, _ichAnchor, _ichAnchor + dich, fTrue);
|
|
break;
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Replaces the characters between ich1 and ich2 with the given ones.
|
|
***************************************************************************/
|
|
bool TXDD::FReplace(achar *prgch, long cch, long ich1, long ich2, bool fDraw)
|
|
{
|
|
AssertThis(0);
|
|
_SwitchSel(fFalse, fTrue);
|
|
SortLw(&ich1, &ich2);
|
|
if (!_pbsf->FReplace(prgch, cch, ich1, ich2 - ich1))
|
|
return fFalse;
|
|
|
|
_InvalAllTxdd(ich1, cch, ich2 - ich1);
|
|
ich1 += cch;
|
|
SetSel(ich1, ich1, fTrue);
|
|
ShowSel(fTrue);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Invalidate all TXDDs on this text doc. Also dirties the document.
|
|
Should be called by any code that edits the document.
|
|
***************************************************************************/
|
|
void TXDD::_InvalAllTxdd(long ich, long cchIns, long cchDel)
|
|
{
|
|
AssertThis(0);
|
|
long ipddg;
|
|
PDDG pddg;
|
|
|
|
//mark the document dirty
|
|
_pdocb->SetDirty();
|
|
|
|
//inform the TXDDs
|
|
for (ipddg = 0; pvNil != (pddg = _pdocb->PddgGet(ipddg)); ipddg++)
|
|
{
|
|
if (pddg->FIs(kclsTXDD))
|
|
((PTXDD)pddg)->_InvalIch(ich, cchIns, cchDel);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Invalidate the display from ich. If we're the active TXDD, also redraw.
|
|
***************************************************************************/
|
|
void TXDD::_InvalIch(long ich, long cchIns, long cchDel)
|
|
{
|
|
AssertThis(0);
|
|
Assert(!_fSelOn, "why is the sel on during an invalidation?");
|
|
RC rcLoc, rc;
|
|
long ichAnchor, ichOther;
|
|
long lnNew, clnIns, clnDel;
|
|
long yp, dypIns, dypDel;
|
|
|
|
//adjust the sel
|
|
ichAnchor = _ichAnchor;
|
|
ichOther = _ichOther;
|
|
FAdjustIv(&ichAnchor, ich, cchIns, cchDel);
|
|
FAdjustIv(&ichOther, ich, cchIns, cchDel);
|
|
if (ichAnchor != _ichAnchor || ichOther != _ichOther)
|
|
SetSel(ichAnchor, ichOther, fFalse);
|
|
|
|
//adjust the cache
|
|
if (_ichLimCache > _ichMinCache)
|
|
{
|
|
if (FPure(_ichLimCache <= ich) != FPure(_ichMinCache <= ich) ||
|
|
FPure(_ichLimCache >= ich + cchDel) !=
|
|
FPure(_ichMinCache >= ich + cchDel) ||
|
|
!FAdjustIv(&_ichLimCache, ich, cchIns, cchDel) ||
|
|
!FAdjustIv(&_ichMinCache, ich, cchIns, cchDel))
|
|
{
|
|
_ichMinCache = _ichLimCache = 0;
|
|
}
|
|
}
|
|
|
|
//reformat
|
|
_ReformatEdit(ich, cchIns, cchDel, &lnNew, &clnIns, &clnDel);
|
|
if (lnNew > 0)
|
|
{
|
|
lnNew--;
|
|
clnIns++;
|
|
clnDel++;
|
|
}
|
|
|
|
//determine the dirty rectangles and if we're active, update them
|
|
GetRc(&rcLoc, cooLocal);
|
|
if (!_fActive)
|
|
{
|
|
rc = rcLoc;
|
|
rc.ypTop = LwMul(lnNew, _dypLine);
|
|
if (clnIns == clnDel)
|
|
rc.ypBottom = LwMul(lnNew + clnIns, _dypLine);
|
|
InvalRc(&rc);
|
|
return;
|
|
}
|
|
|
|
dypIns = LwMul(clnIns, _dypLine);
|
|
dypDel = LwMul(clnDel, _dypLine);
|
|
yp = LwMul(lnNew, _dypLine);
|
|
rc = rcLoc;
|
|
rc.ypTop = yp;
|
|
rc.ypBottom = yp + LwMin(dypIns, dypDel);
|
|
if (_clnDisp > lnNew + clnIns - clnDel && clnIns != clnDel)
|
|
{
|
|
//have some bits to blt vertically
|
|
rc = rcLoc;
|
|
rc.ypTop = yp + LwMin(dypIns, dypDel);
|
|
Scroll(&rc, 0, dypIns - dypDel, kginDraw);
|
|
rc.ypBottom = rc.ypTop;
|
|
rc.ypTop = yp;
|
|
}
|
|
if (!rc.FEmpty())
|
|
InvalRc(&rc, kginDraw);
|
|
|
|
_fXpValid = fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
If ppdocb != pvNil, copy the selection to a new document and return
|
|
true. If ppdocb == pvNil just return whether the selection is
|
|
non-empty.
|
|
***************************************************************************/
|
|
bool TXDD::_FCopySel(PDOCB *ppdocb)
|
|
{
|
|
AssertThis(0);
|
|
PTXDC ptxdc;
|
|
long ich1, ich2;
|
|
|
|
if ((ich1 = _ichOther) == (ich2 = _ichAnchor))
|
|
return fFalse;
|
|
|
|
if (pvNil == ppdocb)
|
|
return fTrue;
|
|
|
|
SortLw(&ich1, &ich2);
|
|
if (pvNil != (ptxdc = TXDC::PtxdcNew()))
|
|
{
|
|
if (!ptxdc->Pbsf()->FReplaceBsf(_pbsf, ich1, ich2 - ich1, 0, 0))
|
|
ReleasePpo(&ptxdc);
|
|
}
|
|
|
|
*ppdocb = ptxdc;
|
|
return pvNil != *ppdocb;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Clear (delete) the current selection.
|
|
***************************************************************************/
|
|
void TXDD::_ClearSel(void)
|
|
{
|
|
AssertThis(0);
|
|
FReplace(pvNil, 0, _ichAnchor, _ichOther, fTrue);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Paste the given doc into this one.
|
|
***************************************************************************/
|
|
bool TXDD::_FPaste(PCLIP pclip, bool fDoIt, long cid)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pclip, 0);
|
|
long ich1, ich2, cch;
|
|
PTXDC ptxdc;
|
|
PBSF pbsf;
|
|
|
|
if (cidPaste != cid || !pclip->FGetFormat(kclsTXDC))
|
|
return fFalse;
|
|
|
|
if (!fDoIt)
|
|
return fTrue;
|
|
|
|
if (!pclip->FGetFormat(kclsTXDC, (PDOCB *)&ptxdc))
|
|
return fFalse;
|
|
|
|
AssertPo(ptxdc, 0);
|
|
if (pvNil == (pbsf = ptxdc->Pbsf()) || 0 >= (cch = pbsf->IbMac()))
|
|
{
|
|
ReleasePpo(&ptxdc);
|
|
return fTrue;
|
|
}
|
|
|
|
_SwitchSel(fFalse, fTrue);
|
|
ich1 = _ichAnchor;
|
|
ich2 = _ichOther;
|
|
SortLw(&ich1, &ich2);
|
|
|
|
if (!_pbsf->FReplaceBsf(pbsf, 0, cch, ich1, ich2 - ich1))
|
|
{
|
|
ReleasePpo(&ptxdc);
|
|
return fFalse;
|
|
}
|
|
ReleasePpo(&ptxdc);
|
|
|
|
_InvalAllTxdd(ich1, cch, ich2 - ich1);
|
|
ich1 += cch;
|
|
SetSel(ich1, ich1, fTrue);
|
|
ShowSel(fTrue);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a TXDD.
|
|
***************************************************************************/
|
|
void TXDD::AssertValid(ulong grf)
|
|
{
|
|
//REVIEW shonk: fill in more
|
|
TXDD_PAR::AssertValid(0);
|
|
AssertPo(_pbsf, 0);
|
|
AssertPo(_pglichStarts, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the TXDD.
|
|
***************************************************************************/
|
|
void TXDD::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
TXDD_PAR::MarkMem();
|
|
MarkMemObj(_pbsf);
|
|
MarkMemObj(_pglichStarts);
|
|
}
|
|
#endif //DEBUG
|