mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-22 10:22:40 +01:00
3418 lines
78 KiB
C++
3418 lines
78 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
Author: ShonK
|
|
Project: Kauai
|
|
Reviewed:
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Rich text document and associated DDG, continued
|
|
|
|
***************************************************************************/
|
|
#include "frame.h"
|
|
ASSERTNAME
|
|
|
|
|
|
RTCLASS(TRUL)
|
|
|
|
const long kdxpMax = 0x01000000;
|
|
|
|
/***************************************************************************
|
|
Character run data.
|
|
***************************************************************************/
|
|
typedef struct CHRD *PCHRD;
|
|
struct CHRD
|
|
{
|
|
long cpLim;
|
|
long cpLimDraw;
|
|
long xpLim;
|
|
long xpLimDraw;
|
|
};
|
|
|
|
|
|
/***************************************************************************
|
|
Character run class. This is used to format a line, draw a line,
|
|
map between cp and xp on a line, etc.
|
|
***************************************************************************/
|
|
const long kcchMaxChr = 128;
|
|
class CHR
|
|
{
|
|
ASSERT
|
|
|
|
private:
|
|
CHP _chp;
|
|
PAP _pap;
|
|
PTXTB _ptxtb;
|
|
PGNV _pgnv;
|
|
bool _fMustAdvance: 1;
|
|
bool _fBreak: 1;
|
|
bool _fObject: 1;
|
|
|
|
long _cpMin;
|
|
long _cpLim;
|
|
long _cpLimFetch;
|
|
long _xpMin;
|
|
long _xpBreak;
|
|
|
|
CHRD _chrd;
|
|
CHRD _chrdBop;
|
|
|
|
long _dypAscent;
|
|
long _dypDescent;
|
|
|
|
achar _rgch[kcchMaxChr];
|
|
|
|
bool _FFit(void);
|
|
void _SetToBop(void);
|
|
void _SkipIgnores(void);
|
|
void _DoTab(void);
|
|
|
|
public:
|
|
void Init(CHP *pchp, PAP *ppap, PTXTB ptxtb, PGNV pgnv,
|
|
long cpMin, long cpLim, long xpBase, long xpLimLine, long xpBreak);
|
|
|
|
void GetNextRun(bool fMustAdvance = fFalse);
|
|
bool FBreak(void)
|
|
{ return _fBreak; }
|
|
void GetChrd(PCHRD pchrd, PCHRD pchrdBop = pvNil)
|
|
{
|
|
AssertNilOrVarMem(pchrd);
|
|
AssertNilOrVarMem(pchrdBop);
|
|
|
|
if (pvNil != pchrd)
|
|
*pchrd = _chrd;
|
|
if (pvNil != pchrdBop)
|
|
*pchrdBop = _chrdBop;
|
|
}
|
|
long DypAscent(void)
|
|
{ return _dypAscent; }
|
|
long DypDescent(void)
|
|
{ return _dypDescent; }
|
|
long XpMin(void)
|
|
{ return _xpMin; }
|
|
long XpBreak(void)
|
|
{ return _xpBreak; }
|
|
achar *Prgch(void)
|
|
{ return _rgch; }
|
|
long CpMin(void)
|
|
{ return _cpMin; }
|
|
bool FObject(void)
|
|
{ return _fObject; }
|
|
};
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a CHR.
|
|
***************************************************************************/
|
|
void CHR::AssertValid(ulong grf)
|
|
{
|
|
AssertThisMem();
|
|
AssertPo(_ptxtb, 0);
|
|
AssertPo(_pgnv, 0);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize the CHR.
|
|
***************************************************************************/
|
|
void CHR::Init(CHP *pchp, PAP *ppap, PTXTB ptxtb, PGNV pgnv,
|
|
long cpMin, long cpLim, long xpBase, long xpLimLine, long xpBreak)
|
|
{
|
|
AssertVarMem(pchp);
|
|
AssertVarMem(ppap);
|
|
AssertPo(ptxtb, 0);
|
|
AssertPo(pgnv, 0);
|
|
AssertIn(cpMin, 0, ptxtb->CpMac());
|
|
AssertIn(cpLim, cpMin + 1, ptxtb->CpMac() + 1);
|
|
Assert(xpBase <= xpLimLine, "xpBase > xpLimLine");
|
|
|
|
RC rc;
|
|
|
|
_chp = *pchp;
|
|
_pap = *ppap;
|
|
_ptxtb = ptxtb;
|
|
_pgnv = pgnv;
|
|
_fBreak = fFalse;
|
|
|
|
_cpMin = cpMin;
|
|
_cpLim = cpLim;
|
|
_cpLimFetch = cpMin;
|
|
_xpMin = xpBase;
|
|
_xpBreak = LwMin(xpBreak, xpLimLine);
|
|
|
|
// apply any indenting
|
|
if (0 == xpBase)
|
|
{
|
|
switch (_pap.nd)
|
|
{
|
|
case ndFirst:
|
|
if (_ptxtb->FMinPara(_cpMin))
|
|
_xpMin += _pap.dxpTab;
|
|
break;
|
|
case ndRest:
|
|
if (!_ptxtb->FMinPara(_cpMin))
|
|
_xpMin += _pap.dxpTab;
|
|
break;
|
|
case ndAll:
|
|
_xpMin += _pap.dxpTab;
|
|
break;
|
|
}
|
|
}
|
|
if (ndAll == _pap.nd)
|
|
_xpBreak = LwMin(_xpBreak, xpLimLine - _pap.dxpTab);
|
|
|
|
_chrd.cpLim = _chrd.cpLimDraw = _cpMin;
|
|
_chrd.xpLim = _chrd.xpLimDraw = _xpMin;
|
|
_chrdBop = _chrd;
|
|
|
|
// get the vertical dimensions
|
|
_pgnv->SetFont(_chp.onn, _chp.grfont, _chp.dypFont, tahLeft, tavBaseline);
|
|
|
|
#ifndef SOC_BUG_1500 // REVIEW shonk: Win95 bug workaround
|
|
// If we don't draw to the _pgnv before getting the metrics, the metrics
|
|
// can be different than after we draw!
|
|
achar ch = kchSpace;
|
|
_pgnv->DrawRgch(&ch, 1, 0, 0);
|
|
#endif //!REVIEW
|
|
|
|
_pgnv->GetRcFromRgch(&rc, pvNil, 0);
|
|
_dypAscent = LwMax(0, -rc.ypTop - _chp.dypOffset);
|
|
_dypDescent = LwMax(0, rc.ypBottom + _chp.dypOffset);
|
|
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the next run (within the bounds we were inited with).
|
|
***************************************************************************/
|
|
void CHR::GetNextRun(bool fMustAdvance)
|
|
{
|
|
AssertThis(0);
|
|
ulong grfch;
|
|
achar ch;
|
|
RC rc;
|
|
|
|
// Start the next run
|
|
if (FIn(_chrd.cpLim, _cpMin + 1, _cpLimFetch))
|
|
BltPb(_rgch + _chrd.cpLim - _cpMin, _rgch, _cpLimFetch - _chrd.cpLim);
|
|
_cpMin = _chrd.cpLimDraw = _chrd.cpLim;
|
|
_xpMin = _chrd.xpLimDraw = _chrd.xpLim;
|
|
_xpBreak = LwMax(_xpBreak, _xpMin);
|
|
_chrdBop = _chrd;
|
|
|
|
if (_cpMin >= _cpLim)
|
|
return;
|
|
|
|
// Refill the buffer
|
|
if (_cpLimFetch < _cpLim && _cpLimFetch < _cpMin + kcchMaxChr)
|
|
{
|
|
long cpFetch = LwMax(_cpMin, _cpLimFetch);
|
|
|
|
_cpLimFetch = LwMin(kcchMaxChr, _cpLim - _cpMin) + _cpMin;
|
|
_ptxtb->FetchRgch(cpFetch, _cpLimFetch - cpFetch,
|
|
_rgch + cpFetch - _cpMin);
|
|
}
|
|
|
|
_fMustAdvance = FPure(fMustAdvance);
|
|
_fBreak = fFalse;
|
|
_fObject = fFalse;
|
|
|
|
_pgnv->SetFont(_chp.onn, _chp.grfont, _chp.dypFont, tahLeft, tavBaseline);
|
|
for (;;)
|
|
{
|
|
if (_chrd.cpLim >= _cpLimFetch ||
|
|
(fchIgnore & (grfch = GrfchFromCh(
|
|
ch = _rgch[_chrd.cpLim - _cpMin]))))
|
|
{
|
|
// we're out of characters or this is an ignoreable character -
|
|
// return the run
|
|
if (!_FFit())
|
|
_SetToBop();
|
|
else
|
|
_SkipIgnores();
|
|
break;
|
|
}
|
|
|
|
if (grfch & fchTab)
|
|
{
|
|
// handle the string of tabs, then return the run
|
|
_DoTab();
|
|
break;
|
|
}
|
|
|
|
if (grfch & fchBreak)
|
|
{
|
|
// This line must break after this character - return the run.
|
|
if (!_FFit())
|
|
{
|
|
_SetToBop();
|
|
break;
|
|
}
|
|
|
|
_chrd.cpLim++;
|
|
_SkipIgnores();
|
|
|
|
// this is a BOP
|
|
_chrdBop = _chrd;
|
|
_fBreak = fTrue;
|
|
break;
|
|
}
|
|
|
|
if (grfch & fchMayBreak)
|
|
{
|
|
_chrd.cpLimDraw = ++_chrd.cpLim;
|
|
if (_FFit())
|
|
{
|
|
// this is a BOP
|
|
_chrdBop = _chrd;
|
|
continue;
|
|
}
|
|
|
|
_fBreak = fTrue;
|
|
if (grfch & fchWhiteOverhang)
|
|
{
|
|
// see if everything but this character fits
|
|
long xp = _chrd.xpLim;
|
|
|
|
_chrd.cpLimDraw--;
|
|
if (_FFit())
|
|
{
|
|
// fits with the overhang
|
|
_chrd.xpLim = xp;
|
|
_chrdBop = _chrd;
|
|
_SkipIgnores();
|
|
break;
|
|
}
|
|
}
|
|
|
|
_SetToBop();
|
|
break;
|
|
}
|
|
|
|
if (kchObject == ch)
|
|
{
|
|
// this is an object character
|
|
if (_chrd.cpLim > _cpMin)
|
|
{
|
|
// return the run before processing the object - objects
|
|
// go in their own run.
|
|
if (!_FFit())
|
|
_SetToBop();
|
|
else
|
|
_chrdBop = _chrd;
|
|
break;
|
|
}
|
|
|
|
// return just the object (if it really is an object)
|
|
if (!_ptxtb->FGetObjectRc(_chrd.cpLim, _pgnv, &_chp, &rc))
|
|
{
|
|
// treat as a normal character
|
|
_chrd.cpLimDraw = ++_chrd.cpLim;
|
|
continue;
|
|
}
|
|
|
|
rc.Offset(0, _chp.dypOffset);
|
|
Assert(!rc.FEmpty() && rc.xpRight >= 0,
|
|
"bad rectangle for the object");
|
|
|
|
if (fMustAdvance || _xpMin + rc.xpRight <= _xpBreak)
|
|
{
|
|
_fObject = fTrue;
|
|
_chrd.cpLimDraw = ++_chrd.cpLim;
|
|
_chrd.xpLim = _chrd.xpLimDraw = _xpMin + rc.xpRight;
|
|
_chrdBop = _chrd;
|
|
|
|
_dypAscent = LwMax(_dypAscent, -rc.ypTop);
|
|
_dypDescent = LwMax(_dypDescent, rc.ypBottom);
|
|
|
|
if (_xpMin + rc.xpRight >= _xpBreak)
|
|
_fBreak = fTrue;
|
|
_SkipIgnores();
|
|
}
|
|
break;
|
|
}
|
|
|
|
// normal character
|
|
_chrd.cpLimDraw = ++_chrd.cpLim;
|
|
}
|
|
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Test whether everything from _cpMin to _chrd.cpLimDraw fits. Assumes the
|
|
font is set in the _pgnv.
|
|
***************************************************************************/
|
|
bool CHR::_FFit(void)
|
|
{
|
|
AssertThis(0);
|
|
RC rc;
|
|
|
|
if (_chrd.cpLimDraw == _cpMin)
|
|
_chrd.xpLim = _chrd.xpLimDraw = _xpMin;
|
|
else
|
|
{
|
|
_pgnv->GetRcFromRgch(&rc, _rgch, _chrd.cpLimDraw - _cpMin);
|
|
_chrd.xpLim = _chrd.xpLimDraw = _xpMin + rc.Dxp();
|
|
}
|
|
|
|
return _chrd.xpLimDraw <= _xpBreak;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the CHR to the last break opportunity. If there wasn't one and
|
|
_fMustAdvance is true, gobble as many characters as we can, but at least
|
|
one.
|
|
***************************************************************************/
|
|
void CHR::_SetToBop(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_fBreak = fTrue;
|
|
if (_chrdBop.cpLim <= _cpMin && _fMustAdvance)
|
|
{
|
|
// no break opportunity seen - gobble as many characters as we can,
|
|
// but at least one.
|
|
// do a binary search for the character to break at
|
|
Assert(_chrd.cpLimDraw > _cpMin, "why is _chrd.cpLimDraw == _cpMin?");
|
|
|
|
RC rc;
|
|
long ivMin, ivLim, iv;
|
|
long dxp = _xpBreak - _xpMin;
|
|
|
|
for (ivMin = 0, ivLim = _chrd.cpLimDraw - _cpMin; ivMin < ivLim; )
|
|
{
|
|
iv = (ivMin + ivLim) / 2 + 1;
|
|
AssertIn(iv, ivMin + 1, ivLim + 1);
|
|
_pgnv->GetRcFromRgch(&rc, _rgch, iv);
|
|
if (rc.Dxp() <= dxp)
|
|
ivMin = iv;
|
|
else
|
|
ivLim = iv - 1;
|
|
}
|
|
AssertIn(ivMin, 0, _chrd.cpLimDraw - _cpMin + 1);
|
|
if (ivMin == 0)
|
|
{
|
|
// nothing fits - use one character
|
|
ivMin = 1;
|
|
}
|
|
|
|
// set _chrd.cpLim and _chrd.cpLimDraw, then set the _xp values
|
|
_chrd.cpLim = _chrd.cpLimDraw = _cpMin + ivMin;
|
|
_FFit();
|
|
}
|
|
else
|
|
_chrd = _chrdBop;
|
|
|
|
_SkipIgnores();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Skip any trailing ignore characters. Just changes _chrd.cpLim.
|
|
***************************************************************************/
|
|
void CHR::_SkipIgnores(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
while (_chrd.cpLim < _cpLimFetch &&
|
|
(fchIgnore & GrfchFromCh(_rgch[_chrd.cpLim - _cpMin])))
|
|
{
|
|
_chrd.cpLim++;
|
|
}
|
|
|
|
if (_chrd.cpLim == _cpLimFetch)
|
|
{
|
|
achar ch;
|
|
long cpMac = _ptxtb->CpMac();
|
|
|
|
while (_chrd.cpLim < cpMac)
|
|
{
|
|
_ptxtb->FetchRgch(_chrd.cpLim, 1, &ch);
|
|
if (!(fchIgnore & GrfchFromCh(ch)))
|
|
return;
|
|
_chrd.cpLim++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Swallow as many tabs as possible.
|
|
***************************************************************************/
|
|
void CHR::_DoTab(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (!_FFit())
|
|
{
|
|
_SetToBop();
|
|
return;
|
|
}
|
|
|
|
while (_chrd.cpLim < _cpLimFetch &&
|
|
(fchTab & GrfchFromCh(_rgch[_chrd.cpLim - _cpMin])))
|
|
{
|
|
_chrd.cpLim++;
|
|
_chrd.xpLim = LwRoundAway(_chrd.xpLim + 1, _pap.dxpTab);
|
|
if (_chrd.xpLim > _xpBreak)
|
|
{
|
|
// this tab would carry us over the edge.
|
|
if (_chrd.cpLim == _cpMin + 1 && _fMustAdvance)
|
|
{
|
|
// the line is empty, so we have to force the tab onto the line
|
|
_SkipIgnores();
|
|
_fBreak = fTrue;
|
|
}
|
|
else
|
|
_SetToBop();
|
|
return;
|
|
}
|
|
|
|
// this is a BOP
|
|
_chrd.xpLimDraw = _chrd.xpLim;
|
|
_chrdBop = _chrd;
|
|
}
|
|
|
|
_SkipIgnores();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for the text document display GOB.
|
|
***************************************************************************/
|
|
TXTG::TXTG(PTXTB ptxtb, PGCB pgcb) : TXTG_PAR(ptxtb, pgcb)
|
|
{
|
|
AssertBaseThis(0);
|
|
_ptxtb = ptxtb;
|
|
_fMark = (kginMark == pgcb->_gin ||
|
|
kginDefault == pgcb->_gin && kginMark == GOB::GinDefault());
|
|
_pgnv = pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for TXTG.
|
|
***************************************************************************/
|
|
TXTG::~TXTG(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
ReleasePpo(&_pgllin);
|
|
ReleasePpo(&_pgnv);
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a TXTG.
|
|
***************************************************************************/
|
|
void TXTG::AssertValid(ulong grf)
|
|
{
|
|
TXTG_PAR::AssertValid(0);
|
|
AssertPo(_pgllin, 0);
|
|
AssertIn(_ilinInval, 0, _pgllin->IvMac() + 1);
|
|
AssertPo(_pgnv, 0);
|
|
AssertNilOrPo(_ptrul, 0);
|
|
//REVIEW shonk: TXTG::AssertValid: fill out.
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the TXTG.
|
|
***************************************************************************/
|
|
void TXTG::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
TXTG_PAR::MarkMem();
|
|
MarkMemObj(_pgllin);
|
|
MarkMemObj(_pgnv);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize the text document display gob.
|
|
***************************************************************************/
|
|
bool TXTG::_FInit(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
PGPT pgpt;
|
|
|
|
if (!TXTG_PAR::_FInit())
|
|
return fFalse;
|
|
if (pvNil == (_pgllin = GL::PglNew(size(LIN))))
|
|
return fFalse;
|
|
|
|
// Allocate the GNV for formatting. Use an offscreen one iff _fMark
|
|
// is set.
|
|
if (_fMark)
|
|
{
|
|
RC rc(0, 0, 1, 1);
|
|
|
|
if (pvNil == (pgpt = GPT::PgptNewOffscreen(&rc, 8)))
|
|
return fFalse;
|
|
}
|
|
else
|
|
{
|
|
pgpt = Pgpt();
|
|
pgpt->AddRef();
|
|
}
|
|
|
|
_pgnv = NewObj GNV(this, pgpt);
|
|
ReleasePpo(&pgpt);
|
|
|
|
if (pvNil == _pgnv)
|
|
return fFalse;
|
|
|
|
_pgllin->SetMinGrow(20);
|
|
_ilinDisp = 0;
|
|
_cpDisp = 0;
|
|
_dypDisp = 0;
|
|
_ilinInval = 0;
|
|
if (_DxpDoc() > 0)
|
|
{
|
|
long cpMac = _ptxtb->CpMac();
|
|
_Reformat(0, cpMac, cpMac);
|
|
}
|
|
|
|
AssertThis(0);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Deactivate the TXTG - turn off the selection.
|
|
***************************************************************************/
|
|
void TXTG::_Activate(bool fActive)
|
|
{
|
|
AssertThis(0);
|
|
|
|
TXTG_PAR::_Activate(fActive);
|
|
if (!fActive)
|
|
_SwitchSel(fFalse, kginSysInval);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the LIN for the given ilin. If ilin is past the end of _pgllin,
|
|
_CalcLine is called repeatedly and new lines are added to _pgllin.
|
|
The actual index of the returned line is put in *pilinActual (if not nil).
|
|
***************************************************************************/
|
|
void TXTG::_FetchLin(long ilin, LIN *plin, long *pilinActual)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(ilin, 0, kcbMax);
|
|
AssertVarMem(plin);
|
|
AssertNilOrVarMem(pilinActual);
|
|
|
|
long cpLim, cpMac;
|
|
long dypTot;
|
|
LIN *qlin;
|
|
bool fAdd;
|
|
long ilinLim = LwMin(_pgllin->IvMac(), ilin + 1);
|
|
|
|
if (pvNil != pilinActual)
|
|
*pilinActual = ilin;
|
|
|
|
if (_ilinInval < ilinLim)
|
|
{
|
|
qlin = (LIN *)_pgllin->QvGet(_ilinInval);
|
|
if (_ilinInval == 0)
|
|
{
|
|
cpLim = 0;
|
|
dypTot = 0;
|
|
}
|
|
else
|
|
{
|
|
qlin--;
|
|
cpLim = qlin->cpMin + qlin->ccp;
|
|
dypTot = qlin->dypTot + qlin->dyp;
|
|
qlin++;
|
|
}
|
|
|
|
// adjust LINs up to ilinLim
|
|
for ( ; _ilinInval < ilinLim; _ilinInval++, qlin++)
|
|
{
|
|
qlin->cpMin = cpLim;
|
|
qlin->dypTot = dypTot;
|
|
cpLim += qlin->ccp;
|
|
dypTot += qlin->dyp;
|
|
}
|
|
}
|
|
Assert(_ilinInval >= ilinLim, 0);
|
|
|
|
if (ilin < _ilinInval)
|
|
{
|
|
*plin = *(LIN *)_pgllin->QvGet(ilin);
|
|
return;
|
|
}
|
|
|
|
Assert(ilinLim == _pgllin->IvMac(), 0);
|
|
if (ilinLim == 0)
|
|
{
|
|
cpLim = 0;
|
|
dypTot = 0;
|
|
ClearPb(plin, size(LIN));
|
|
}
|
|
else
|
|
{
|
|
// get the LIN in case we don't actuall calc any lines below
|
|
*plin = *(LIN *)_pgllin->QvGet(ilinLim - 1);
|
|
cpLim = plin->cpMin + plin->ccp;
|
|
dypTot = plin->dypTot + plin->dyp;
|
|
}
|
|
|
|
cpMac = _ptxtb->CpMac();
|
|
fAdd = fTrue;
|
|
for ( ; ilinLim <= ilin && cpLim < cpMac; ilinLim++)
|
|
{
|
|
_CalcLine(cpLim, dypTot, plin);
|
|
fAdd = fAdd && _pgllin->FAdd(plin);
|
|
cpLim += plin->ccp;
|
|
dypTot += plin->dyp;
|
|
}
|
|
_ilinInval = _pgllin->IvMac();
|
|
if (pvNil != pilinActual)
|
|
*pilinActual = ilinLim - 1;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the LIN that contains the given cpFind. pilin and/or plin can be nil.
|
|
If fCalcLines is false, we won't calculate any new lines and the
|
|
returned LIN may be before cpFind.
|
|
***************************************************************************/
|
|
void TXTG::_FindCp(long cpFind, LIN *plin, long *pilin, bool fCalcLines)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(cpFind, 0, _ptxtb->CpMac());
|
|
AssertNilOrVarMem(pilin);
|
|
AssertNilOrVarMem(plin);
|
|
|
|
LIN *qlin;
|
|
LIN lin;
|
|
long dypTot;
|
|
long cpLim;
|
|
long ilinMac;
|
|
bool fAdd;
|
|
|
|
// get the starting cp and dypTot values for _ilinInval
|
|
qlin = (LIN *)_pgllin->QvGet(_ilinInval);
|
|
if (_ilinInval == 0)
|
|
{
|
|
cpLim = 0;
|
|
dypTot = 0;
|
|
}
|
|
else
|
|
{
|
|
qlin--;
|
|
cpLim = qlin->cpMin + qlin->ccp;
|
|
dypTot = qlin->dypTot + qlin->dyp;
|
|
qlin++;
|
|
}
|
|
|
|
if (cpFind < cpLim)
|
|
{
|
|
// do a binary search to find the LIN containing cpFind
|
|
long ivMin, ivLim, iv;
|
|
|
|
for (ivMin = 0, ivLim = _ilinInval; ivMin < ivLim; )
|
|
{
|
|
iv = (ivMin + ivLim) / 2;
|
|
qlin = (LIN *)_pgllin->QvGet(iv);
|
|
if (cpFind < qlin->cpMin)
|
|
ivLim = iv;
|
|
else if (cpFind >= qlin->cpMin + qlin->ccp)
|
|
ivMin = iv + 1;
|
|
else
|
|
{
|
|
if (pvNil != pilin)
|
|
*pilin = iv;
|
|
if (pvNil != plin)
|
|
*plin = *qlin;
|
|
return;
|
|
}
|
|
}
|
|
|
|
Bug("Invalid LINs");
|
|
cpLim = 0;
|
|
dypTot = 0;
|
|
_ilinInval = 0;
|
|
}
|
|
|
|
Assert(cpFind >= cpLim, "why isn't cpFind >= cpLim?");
|
|
if (_ilinInval < (ilinMac = _pgllin->IvMac()))
|
|
{
|
|
// adjust LINs up to cpFind
|
|
qlin = (LIN *)_pgllin->QvGet(_ilinInval);
|
|
for ( ; _ilinInval < ilinMac && cpFind >= cpLim; _ilinInval++, qlin++)
|
|
{
|
|
qlin->cpMin = cpLim;
|
|
qlin->dypTot = dypTot;
|
|
cpLim += qlin->ccp;
|
|
dypTot += qlin->dyp;
|
|
}
|
|
|
|
if (cpFind < cpLim)
|
|
{
|
|
AssertIn(cpFind, qlin[-1].cpMin, cpLim);
|
|
if (pvNil != pilin)
|
|
*pilin = _ilinInval - 1;
|
|
if (pvNil != plin)
|
|
*plin = qlin[-1];
|
|
return;
|
|
}
|
|
}
|
|
Assert(_ilinInval == ilinMac, "why isn't _ilinInval == ilinMac?");
|
|
Assert(cpFind >= cpLim, "why isn't cpFind >= cpLim?");
|
|
|
|
if (!fCalcLines)
|
|
{
|
|
if (pvNil != pilin)
|
|
*pilin = ilinMac - (ilinMac > 0);
|
|
if (pvNil != plin)
|
|
{
|
|
if (ilinMac > 0)
|
|
*plin = *(LIN *)_pgllin->QvGet(ilinMac - 1);
|
|
else
|
|
ClearPb(plin, size(LIN));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// have to calculate some lines
|
|
for (fAdd = fTrue; cpFind >= cpLim; ilinMac++)
|
|
{
|
|
_CalcLine(cpLim, dypTot, &lin);
|
|
fAdd = fAdd && _pgllin->FAdd(&lin);
|
|
cpLim += lin.ccp;
|
|
dypTot += lin.dyp;
|
|
}
|
|
_ilinInval = _pgllin->IvMac();
|
|
|
|
if (pvNil != pilin)
|
|
*pilin = ilinMac - 1;
|
|
if (pvNil != plin)
|
|
*plin = lin;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the LIN that contains the given dypFind value (measured from the top
|
|
of the document). pilin and/or plin can be nil.
|
|
If fCalcLines is false, we won't calculate any new lines and the
|
|
returned LIN may be before dypFind.
|
|
***************************************************************************/
|
|
void TXTG::_FindDyp(long dypFind, LIN *plin, long *pilin, bool fCalcLines)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(dypFind, 0, kcbMax);
|
|
AssertNilOrVarMem(pilin);
|
|
AssertNilOrVarMem(plin);
|
|
|
|
LIN *qlin;
|
|
LIN lin;
|
|
long dypTot;
|
|
long cpLim, cpMac;
|
|
long ilinMac;
|
|
bool fAdd;
|
|
|
|
// get the starting cp and dypTot values for _ilinInval
|
|
qlin = (LIN *)_pgllin->QvGet(_ilinInval);
|
|
if (_ilinInval == 0)
|
|
{
|
|
cpLim = 0;
|
|
dypTot = 0;
|
|
}
|
|
else
|
|
{
|
|
qlin--;
|
|
cpLim = qlin->cpMin + qlin->ccp;
|
|
dypTot = qlin->dypTot + qlin->dyp;
|
|
qlin++;
|
|
}
|
|
|
|
if (dypFind < dypTot)
|
|
{
|
|
// do a binary search to find the LIN containing dypFind
|
|
long ivMin, ivLim, iv;
|
|
|
|
for (ivMin = 0, ivLim = _ilinInval; ivMin < ivLim; )
|
|
{
|
|
iv = (ivMin + ivLim) / 2;
|
|
qlin = (LIN *)_pgllin->QvGet(iv);
|
|
if (dypFind < qlin->dypTot)
|
|
ivLim = iv;
|
|
else if (dypFind >= qlin->dypTot + qlin->dyp)
|
|
ivMin = iv + 1;
|
|
else
|
|
{
|
|
if (pvNil != pilin)
|
|
*pilin = iv;
|
|
if (pvNil != plin)
|
|
*plin = *qlin;
|
|
return;
|
|
}
|
|
}
|
|
|
|
Bug("Invalid LINs");
|
|
cpLim = 0;
|
|
dypTot = 0;
|
|
_ilinInval = 0;
|
|
}
|
|
|
|
Assert(dypFind >= dypTot, "why isn't dypFind >= dypTot?");
|
|
if (_ilinInval < (ilinMac = _pgllin->IvMac()))
|
|
{
|
|
// adjust LINs up to cp
|
|
qlin = (LIN *)_pgllin->QvGet(_ilinInval);
|
|
for ( ; _ilinInval < ilinMac && dypFind >= dypTot; _ilinInval++, qlin++)
|
|
{
|
|
qlin->cpMin = cpLim;
|
|
qlin->dypTot = dypTot;
|
|
cpLim += qlin->ccp;
|
|
dypTot += qlin->dyp;
|
|
}
|
|
|
|
if (dypFind < dypTot)
|
|
{
|
|
AssertIn(dypFind, qlin[-1].dypTot, dypTot);
|
|
if (pvNil != pilin)
|
|
*pilin = _ilinInval - 1;
|
|
if (pvNil != plin)
|
|
*plin = qlin[-1];
|
|
return;
|
|
}
|
|
}
|
|
Assert(_ilinInval == ilinMac, "why isn't _ilinInval == ilinMac?");
|
|
Assert(dypFind >= dypTot, "why isn't dypFind >= dypTot?");
|
|
|
|
if (!fCalcLines)
|
|
{
|
|
if (pvNil != pilin)
|
|
*pilin = ilinMac - (ilinMac > 0);
|
|
if (pvNil != plin)
|
|
{
|
|
if (ilinMac > 0)
|
|
*plin = *(LIN *)_pgllin->QvGet(ilinMac - 1);
|
|
else
|
|
ClearPb(plin, size(LIN));
|
|
}
|
|
return;
|
|
}
|
|
|
|
cpMac = _ptxtb->CpMac();
|
|
if (cpLim >= cpMac)
|
|
{
|
|
if (pvNil != plin)
|
|
{
|
|
// Get a valid lin.
|
|
if (ilinMac > 0)
|
|
_pgllin->Get(ilinMac - 1, &lin);
|
|
else
|
|
ClearPb(&lin, size(LIN));
|
|
}
|
|
_ilinInval = ilinMac;
|
|
}
|
|
else
|
|
{
|
|
Assert(dypFind >= dypTot && cpLim < cpMac, 0);
|
|
for (fAdd = fTrue; dypFind >= dypTot && cpLim < cpMac; ilinMac++)
|
|
{
|
|
_CalcLine(cpLim, dypTot, &lin);
|
|
fAdd = fAdd && _pgllin->FAdd(&lin);
|
|
cpLim += lin.ccp;
|
|
dypTot += lin.dyp;
|
|
}
|
|
_ilinInval = _pgllin->IvMac();
|
|
}
|
|
|
|
if (pvNil != pilin)
|
|
*pilin = ilinMac - 1;
|
|
if (pvNil != plin)
|
|
*plin = lin;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Recalculate the _pgllin after an edit. Sets *pyp, *pdypIns, *pdypDel
|
|
to indicate the vertical display space that was affected.
|
|
***************************************************************************/
|
|
void TXTG::_Reformat(long cp, long ccpIns, long ccpDel,
|
|
long *pyp, long *pdypIns, long *pdypDel)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(cp, 0, _ptxtb->CpMac());
|
|
AssertIn(ccpIns, 0, _ptxtb->CpMac() - cp + 1);
|
|
AssertIn(ccpDel, 0, kcbMax);
|
|
AssertNilOrVarMem(pyp);
|
|
AssertNilOrVarMem(pdypIns);
|
|
AssertNilOrVarMem(pdypDel);
|
|
|
|
long ypDel, dypDel, dypIns, dypCur;
|
|
long ccp, cpCur, cpNext, cpMac;
|
|
long ilin, ilinOld;
|
|
LIN linOld, lin, linT;
|
|
|
|
_fClear = _ptxtb->AcrBack() == kacrClear;
|
|
_fXpValid = fFalse;
|
|
cpMac = _ptxtb->CpMac();
|
|
|
|
// Find the LIN that contains cp (if there is one) - don't calc any lines
|
|
// to get the LIN.
|
|
_FindCp(cp, &linOld, &ilinOld, fFalse);
|
|
|
|
if (cp >= linOld.cpMin + linOld.ccp)
|
|
{
|
|
// the LIN for this cp was not cached - recalc from the beginning of
|
|
// the last lin.
|
|
ccpIns += cp - linOld.cpMin;
|
|
ccpDel += cp - linOld.cpMin;
|
|
cp = linOld.cpMin;
|
|
}
|
|
AssertIn(cp, linOld.cpMin, linOld.cpMin + linOld.ccp + (linOld.ccp == 0));
|
|
|
|
// make sure the previous line is formatted correctly
|
|
if (ilinOld > 0 && !_ptxtb->FMinPara(linOld.cpMin))
|
|
{
|
|
_FetchLin(ilinOld - 1, &lin);
|
|
_CalcLine(lin.cpMin, lin.dypTot, &linT);
|
|
if (linT.ccp != lin.ccp)
|
|
{
|
|
//edit affected previous line - so start
|
|
//formatting from there
|
|
ilinOld--;
|
|
linOld = lin;
|
|
}
|
|
}
|
|
AssertIn(cp, linOld.cpMin, cpMac);
|
|
|
|
// remove deleted lines
|
|
Assert(ilinOld <= _ilinInval, 0);
|
|
_ilinInval = ilinOld;
|
|
for (ccp = dypDel = 0;
|
|
linOld.cpMin + ccp <= cp + ccpDel && ilinOld < _pgllin->IvMac(); )
|
|
{
|
|
_pgllin->Get(ilinOld, &lin);
|
|
dypDel += lin.dyp;
|
|
ccp += lin.ccp;
|
|
_pgllin->Delete(ilinOld);
|
|
}
|
|
|
|
//insert the new lines
|
|
cpCur = linOld.cpMin;
|
|
dypCur = linOld.dypTot;
|
|
|
|
// cpNext is the cp of the next (possibly stale) LIN in _pgllin
|
|
if (ilinOld < _pgllin->IvMac())
|
|
{
|
|
cpNext = linOld.cpMin + ccp - ccpDel + ccpIns;
|
|
AssertIn(cpNext, linOld.cpMin, cpMac + 1);
|
|
}
|
|
else
|
|
cpNext = cpMac;
|
|
|
|
AssertIn(ilinOld, 0, _pgllin->IvMac() + 1);
|
|
dypIns = 0;
|
|
for (ilin = ilinOld; ; )
|
|
{
|
|
AssertIn(cpCur, linOld.cpMin, cpMac + 1);
|
|
AssertIn(dypCur, linOld.dypTot, kcbMax);
|
|
while (cpNext < cpCur && ilin < _pgllin->IvMac())
|
|
{
|
|
_pgllin->Get(ilin, &lin);
|
|
_pgllin->Delete(ilin);
|
|
cpNext += lin.ccp;
|
|
dypDel += lin.dyp;
|
|
}
|
|
AssertIn(cpNext, cpCur, cpMac + 1);
|
|
|
|
if (ilin >= _pgllin->IvMac())
|
|
{
|
|
// no more LINs that might be preservable, so we may as well
|
|
// stop trying.
|
|
ilin = _pgllin->IvMac();
|
|
if (cpNext < cpMac)
|
|
dypDel = kswMax;
|
|
if (cpCur < cpMac)
|
|
dypIns = kswMax;
|
|
break;
|
|
}
|
|
|
|
AssertIn(ilin, 0, _pgllin->IvMac());
|
|
if (cpCur == cpNext)
|
|
{
|
|
// everything from here on should be correct - we still need to
|
|
// set _ilinDisp
|
|
break;
|
|
}
|
|
|
|
_CalcLine(cpCur, dypCur, &lin);
|
|
if (!_pgllin->FInsert(ilin, &lin))
|
|
{
|
|
AssertIn(ilin, 0, _pgllin->IvMac());
|
|
_pgllin->Get(ilin, &linT);
|
|
cpNext += linT.ccp;
|
|
dypDel += linT.dyp;
|
|
_pgllin->Put(ilin, &lin);
|
|
}
|
|
|
|
dypIns += lin.dyp;
|
|
cpCur += lin.ccp;
|
|
dypCur += lin.dyp;
|
|
ilin++;
|
|
}
|
|
_ilinInval = ilin;
|
|
|
|
if (_cpDisp <= linOld.cpMin)
|
|
ypDel = linOld.dypTot - _dypDisp;
|
|
else
|
|
{
|
|
if (_cpDisp > cp)
|
|
{
|
|
if (_cpDisp >= cp + ccpDel)
|
|
_cpDisp += ccpIns - ccpDel;
|
|
else if (_cpDisp >= cp + ccpIns)
|
|
_cpDisp = cp + ccpIns;
|
|
}
|
|
ypDel = 0;
|
|
_FindCp(_cpDisp, &lin, &_ilinDisp);
|
|
_cpDisp = lin.cpMin;
|
|
dypDel = LwMax(0, linOld.dypTot + dypDel - _dypDisp);
|
|
_dypDisp = lin.dypTot;
|
|
dypIns = LwMax(0, linOld.dypTot + dypIns - _dypDisp);
|
|
}
|
|
|
|
if (pvNil != pyp)
|
|
*pyp = ypDel;
|
|
if (pvNil != pdypIns)
|
|
*pdypIns = dypIns;
|
|
if (pvNil != pdypDel)
|
|
*pdypDel = dypDel;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Calculate the end of the line, the left position of the line, the height
|
|
of the line and the ascent of the line.
|
|
***************************************************************************/
|
|
void TXTG::_CalcLine(long cpMin, long dypBase, LIN *plin)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(cpMin, 0, _ptxtb->CpMac());
|
|
AssertVarMem(plin);
|
|
|
|
struct RUN
|
|
{
|
|
long cpLim;
|
|
long xpLim;
|
|
long dypAscent;
|
|
long dypDescent;
|
|
};
|
|
|
|
long dxpDoc;
|
|
PAP pap;
|
|
CHP chp;
|
|
long cpLimPap, cpLimChp;
|
|
CHR chr;
|
|
CHRD chrd, chrdBop;
|
|
RUN run, runSure, runT;
|
|
|
|
dxpDoc = _DxpDoc();
|
|
_FetchPap(cpMin, &pap, pvNil, &cpLimPap);
|
|
|
|
cpLimChp = cpMin;
|
|
run.cpLim = cpMin;
|
|
run.xpLim = 0;
|
|
run.dypAscent = run.dypDescent = 0;
|
|
runSure = run;
|
|
|
|
for (;;)
|
|
{
|
|
// make sure the chp is valid
|
|
if (run.cpLim >= cpLimChp)
|
|
{
|
|
_FetchChp(run.cpLim, &chp, pvNil, &cpLimChp);
|
|
cpLimChp = LwMin(cpLimChp, cpLimPap);
|
|
if (cpLimChp <= run.cpLim)
|
|
{
|
|
Bug("why is run.cpLim >= cpLimChp?");
|
|
break;
|
|
}
|
|
|
|
chr.Init(&chp, &pap, _ptxtb, _pgnv, run.cpLim, cpLimChp,
|
|
run.xpLim, dxpDoc, dxpDoc);
|
|
}
|
|
|
|
chr.GetNextRun(runSure.cpLim == cpMin);
|
|
chr.GetChrd(&chrd, &chrdBop);
|
|
|
|
if (chrd.cpLim == run.cpLim)
|
|
{
|
|
// didn't move forward - use runSure
|
|
Assert(runSure.cpLim > cpMin, "why don't we have a BOP?");
|
|
run = runSure;
|
|
break;
|
|
}
|
|
|
|
runT = run;
|
|
run.cpLim = chrd.cpLim;
|
|
run.xpLim = chrd.xpLimDraw;
|
|
run.dypAscent = LwMax(run.dypAscent, chr.DypAscent());
|
|
run.dypDescent = LwMax(run.dypDescent, chr.DypDescent());
|
|
|
|
if (chr.FBreak())
|
|
{
|
|
// we know that this is the end of the line - use run or runT
|
|
if (run.xpLim > chr.XpBreak() && runT.cpLim > cpMin)
|
|
run = runT;
|
|
break;
|
|
}
|
|
|
|
if (chrdBop.cpLim > runT.cpLim)
|
|
{
|
|
// put chrdBop info into runSure
|
|
runSure.cpLim = chrdBop.cpLim;
|
|
runSure.xpLim = chrdBop.xpLimDraw;
|
|
runSure.dypAscent = LwMax(runSure.dypAscent, chr.DypAscent());
|
|
runSure.dypDescent = LwMax(runSure.dypDescent,
|
|
chr.DypDescent());
|
|
}
|
|
}
|
|
|
|
Assert(run.dypAscent > 0 || run.dypDescent > 0, "bad run");
|
|
Assert(run.cpLim > cpMin, "why is cch zero?");
|
|
|
|
switch (pap.jc)
|
|
{
|
|
default:
|
|
plin->xpLeft = 0;
|
|
break;
|
|
case jcRight:
|
|
plin->xpLeft = (short)(dxpDoc - run.xpLim);
|
|
break;
|
|
case jcCenter:
|
|
plin->xpLeft = (dxpDoc - run.xpLim) / 2;
|
|
break;
|
|
}
|
|
|
|
plin->ccp = (short)(run.cpLim - cpMin);
|
|
plin->dyp = (short)(run.dypAscent + run.dypDescent);
|
|
if (pap.numLine != kdenLine)
|
|
plin->dyp = (short)LwMulDiv(plin->dyp, pap.numLine, kdenLine);
|
|
plin->dyp += pap.dypExtraLine;
|
|
if (_ptxtb->FMinPara(run.cpLim) || run.cpLim == _ptxtb->CpMac())
|
|
{
|
|
if (pap.numAfter != kdenAfter)
|
|
plin->dyp = (short)LwMulDiv(plin->dyp, pap.numAfter, kdenAfter);
|
|
plin->dyp += pap.dypExtraAfter;
|
|
}
|
|
if (plin->dyp <= 0)
|
|
plin->dyp = 1;
|
|
plin->dypAscent = (short)run.dypAscent;
|
|
if (plin->dypAscent <= 0)
|
|
plin->dypAscent = 1;
|
|
plin->cpMin = cpMin;
|
|
plin->dypTot = dypBase;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the cp that the point is in. If fClosest is true, this finds the
|
|
cp boundary that the point is closest to (for traditional selection).
|
|
If fClosest is false, it finds the character that the point is over.
|
|
***************************************************************************/
|
|
bool TXTG::_FGetCpFromPt(long xp, long yp, long *pcp, bool fClosest)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcp);
|
|
|
|
LIN lin;
|
|
RC rc;
|
|
|
|
GetRc(&rc, cooLocal);
|
|
if (!FIn(yp, 0, rc.ypBottom))
|
|
return fFalse;
|
|
|
|
_FindDyp(_dypDisp + yp, &lin);
|
|
if (_dypDisp + yp >= lin.dypTot + lin.dyp)
|
|
{
|
|
*pcp = _ptxtb->CpMac() - 1;
|
|
return fTrue;
|
|
}
|
|
|
|
//we've found the line, now get the cp on the line
|
|
return _FGetCpFromXp(xp, &lin, pcp, fClosest);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the cp on the line given by *plin that the xp is in. If fClosest
|
|
is true, this finds the cp boundary that the point is closest to (for
|
|
traditional selection). If fClosest is false, it finds the character
|
|
that the xp is over. This only returns false if fClosest is false and
|
|
the xp is before the beginning of the line.
|
|
***************************************************************************/
|
|
bool TXTG::_FGetCpFromXp(long xp, LIN *plin, long *pcp, bool fClosest)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(plin);
|
|
AssertIn(plin->cpMin, 0, _ptxtb->CpMac());
|
|
AssertVarMem(plin);
|
|
AssertIn(plin->ccp, 1, _ptxtb->CpMac() + 1 - plin->cpMin);
|
|
AssertVarMem(pcp);
|
|
|
|
CHP chp;
|
|
PAP pap;
|
|
CHR chr;
|
|
long cpCur, cpLimChp, cpLim;
|
|
long xpCur, dxpDoc;
|
|
CHRD chrd;
|
|
|
|
xp -= plin->xpLeft + kdxpIndentTxtg - _scvHorz;
|
|
if (xp <= 0)
|
|
{
|
|
*pcp = plin->cpMin;
|
|
return FPure(fClosest);
|
|
}
|
|
|
|
if (xp >= (dxpDoc = _DxpDoc()))
|
|
{
|
|
*pcp = _ptxtb->CpPrev(plin->cpMin + plin->ccp);
|
|
return fTrue;
|
|
}
|
|
|
|
cpLimChp = cpCur = plin->cpMin;
|
|
cpLim = cpCur + plin->ccp;
|
|
xpCur = 0;
|
|
_FetchPap(cpCur, &pap);
|
|
|
|
for (;;)
|
|
{
|
|
// make sure the chp is valid
|
|
if (cpCur >= cpLimChp)
|
|
{
|
|
if (cpCur >= cpLim)
|
|
{
|
|
// everything fit
|
|
*pcp = LwMax(plin->cpMin, _ptxtb->CpPrev(cpLim));
|
|
return fTrue;
|
|
}
|
|
|
|
_FetchChp(cpCur, &chp, pvNil, &cpLimChp);
|
|
cpLimChp = LwMin(cpLimChp, cpLim);
|
|
Assert(cpLimChp > cpCur, "why is cpCur >= cpLimChp?");
|
|
chr.Init(&chp, &pap, _ptxtb, _pgnv, cpCur, cpLimChp, xpCur,
|
|
dxpDoc, xp);
|
|
}
|
|
|
|
chr.GetNextRun(fTrue);
|
|
chr.GetChrd(&chrd);
|
|
|
|
if (chr.FBreak() &&
|
|
(chrd.xpLim >= chr.XpBreak() || chrd.cpLim >= cpLim))
|
|
{
|
|
long cpPrev = _ptxtb->CpPrev(chrd.cpLim);
|
|
|
|
if (!fClosest || chrd.cpLim >= cpLim)
|
|
goto LPrev;
|
|
|
|
if (cpPrev > cpCur)
|
|
{
|
|
CHRD chrdT;
|
|
|
|
// get the length from cpCur to cpPrev
|
|
chr.Init(&chp, &pap, _ptxtb, _pgnv, cpCur, cpPrev, xpCur,
|
|
dxpDoc, xp);
|
|
chr.GetNextRun(fTrue);
|
|
chr.GetChrd(&chrdT);
|
|
cpCur = chrdT.cpLim;
|
|
xpCur = chrdT.xpLim;
|
|
}
|
|
|
|
Assert(xp >= xpCur && chrd.xpLim >= xp, "what?");
|
|
if (xp - xpCur > chrd.xpLim - xp)
|
|
{
|
|
*pcp = chrd.cpLim;
|
|
return fTrue;
|
|
}
|
|
LPrev:
|
|
*pcp = LwMax(plin->cpMin, cpPrev);
|
|
return fTrue;
|
|
}
|
|
xpCur = chrd.xpLim;
|
|
cpCur = chrd.cpLim;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the vertical bounds of the line containing cp and the horizontal
|
|
position of the cp on the line. If fView is true, the values are in
|
|
view coordinates. If fView is false, the values are in logical values
|
|
(independent of the current scrolling of the view).
|
|
***************************************************************************/
|
|
void TXTG::_GetXpYpFromCp(long cp, long *pypMin, long *pypLim, long *pxp,
|
|
long *pypBaseLine, bool fView)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(cp, 0, _ptxtb->CpMac());
|
|
AssertNilOrVarMem(pypMin);
|
|
AssertNilOrVarMem(pypLim);
|
|
AssertNilOrVarMem(pxp);
|
|
AssertNilOrVarMem(pypBaseLine);
|
|
|
|
LIN lin;
|
|
long xp;
|
|
|
|
_FindCp(cp, &lin);
|
|
xp = lin.xpLeft;
|
|
if (fView)
|
|
{
|
|
lin.dypTot -= _dypDisp;
|
|
xp -= _scvHorz;
|
|
}
|
|
|
|
if (pvNil != pypMin)
|
|
*pypMin = lin.dypTot;
|
|
if (pvNil != pypLim)
|
|
*pypLim = lin.dypTot + lin.dyp;
|
|
if (pvNil != pxp)
|
|
*pxp = xp + _DxpFromCp(lin.cpMin, cp);
|
|
if (pvNil != pypBaseLine)
|
|
*pypBaseLine = lin.dypTot + lin.dypAscent;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the xp location of the given cp. Assumes that cpLine is the start
|
|
of the line containing cp. This includes a buffer on the left of
|
|
kdxpIndentTxtg, but doesn't include centering or right justification
|
|
correction.
|
|
***************************************************************************/
|
|
long TXTG::_DxpFromCp(long cpLine, long cp)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(cpLine, 0, _ptxtb->CpMac());
|
|
AssertIn(cp, cpLine, _ptxtb->CpMac());
|
|
|
|
CHP chp;
|
|
PAP pap;
|
|
CHR chr;
|
|
long cpCur, cpLimChp, cpLim;
|
|
long xpCur;
|
|
CHRD chrd;
|
|
|
|
cpLimChp = cpCur = cpLine;
|
|
cpLim = cp + (cp == cpLine);
|
|
xpCur = 0;
|
|
_FetchPap(cpCur, &pap);
|
|
|
|
for (;;)
|
|
{
|
|
// make sure the chp is valid
|
|
if (cpCur >= cpLimChp)
|
|
{
|
|
_FetchChp(cpCur, &chp, pvNil, &cpLimChp);
|
|
cpLimChp = LwMin(cpLimChp, cpLim);
|
|
Assert(cpLimChp > cpCur, "why is cpCur >= cpLimChp?");
|
|
chr.Init(&chp, &pap, _ptxtb, _pgnv, cpCur, cpLimChp,
|
|
xpCur, kdxpMax, kdxpMax);
|
|
}
|
|
|
|
chr.GetNextRun();
|
|
chr.GetChrd(&chrd);
|
|
|
|
if (chrd.cpLim >= cp || chr.FBreak())
|
|
{
|
|
if (cp == cpLine)
|
|
return chr.XpMin() + kdxpIndentTxtg;
|
|
else
|
|
return chrd.xpLim + kdxpIndentTxtg;
|
|
}
|
|
cpCur = chrd.cpLim;
|
|
xpCur = chrd.xpLim;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Replaces the characters between cp1 and cp2 with the given ones.
|
|
***************************************************************************/
|
|
bool TXTG::FReplace(achar *prgch, long cch, long cp1, long cp2)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(cch, 0, kcbMax);
|
|
AssertPvCb(prgch, cch);
|
|
AssertIn(cp1, 0, _ptxtb->CpMac());
|
|
AssertIn(cp2, 0, _ptxtb->CpMac());
|
|
|
|
HideSel();
|
|
SortLw(&cp1, &cp2);
|
|
if (!_ptxtb->FReplaceRgch(prgch, cch, cp1, cp2 - cp1))
|
|
return fFalse;
|
|
|
|
cp1 += cch;
|
|
SetSel(cp1, cp1);
|
|
ShowSel();
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Invalidate the display from cp. If we're the active TXTG, also redraw.
|
|
***************************************************************************/
|
|
void TXTG::InvalCp(long cp, long ccpIns, long ccpDel)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(cp, 0, _ptxtb->CpMac() + 1);
|
|
AssertIn(ccpIns, 0, _ptxtb->CpMac() + 1 - cp);
|
|
AssertIn(ccpDel, 0, kcbMax);
|
|
Assert(!_fSelOn, "selection is on in InvalCp!");
|
|
long cpAnchor, cpOther;
|
|
|
|
//adjust the sel
|
|
cpAnchor = _cpAnchor;
|
|
cpOther = _cpOther;
|
|
FAdjustIv(&cpAnchor, cp, ccpIns, ccpDel);
|
|
FAdjustIv(&cpOther, cp, ccpIns, ccpDel);
|
|
if (cpAnchor != _cpAnchor || cpOther != _cpOther)
|
|
SetSel(cpAnchor, cpOther, ginNil);
|
|
|
|
_ReformatAndDraw(cp, ccpIns, ccpDel);
|
|
|
|
if (pvNil != _ptrul)
|
|
{
|
|
PAP pap;
|
|
|
|
_FetchPap(LwMin(_cpAnchor, _cpOther), &pap);
|
|
_ptrul->SetDxpTab(pap.dxpTab);
|
|
_ptrul->SetDxpDoc(_DxpDoc());
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Reformat the TXTG and update the display. If this TXTG is not the
|
|
active one, the display is invalidated instead of updated.
|
|
***************************************************************************/
|
|
void TXTG::_ReformatAndDraw(long cp, long ccpIns, long ccpDel)
|
|
{
|
|
RC rcLoc, rc;
|
|
long yp, dypIns, dypDel;
|
|
RC rcUpdate(0, 0, 0, 0);
|
|
|
|
//reformat
|
|
_Reformat(cp, ccpIns, ccpDel, &yp, &dypIns, &dypDel);
|
|
|
|
//determine the dirty rectangles and if we're active, update them
|
|
GetRc(&rcLoc, cooLocal);
|
|
if (!_fActive)
|
|
{
|
|
rc = rcLoc;
|
|
rc.ypTop = yp;
|
|
if (dypIns == dypDel)
|
|
rc.ypBottom = yp + dypIns;
|
|
InvalRc(&rc);
|
|
return;
|
|
}
|
|
|
|
rc = rcLoc;
|
|
rc.ypTop = yp;
|
|
rc.ypBottom = yp + dypIns;
|
|
if (dypIns != dypDel)
|
|
{
|
|
// Have some bits to blt vertically. If the background isn't clear,
|
|
// but _fMark is set, still do the scroll, since _fMark is intended
|
|
// to avoid flashing (allowing offscreen drawing) and scrolling doesn't
|
|
// flash anyway.
|
|
if (_fClear)
|
|
rc.ypBottom = rcLoc.ypBottom;
|
|
else
|
|
{
|
|
rc = rcLoc;
|
|
rc.ypTop = LwMax(rc.ypTop, yp + LwMin(dypIns, dypDel));
|
|
Scroll(&rc, 0, dypIns - dypDel, _fMark ? kginMark : kginDraw);
|
|
rc.ypBottom = rc.ypTop;
|
|
rc.ypTop = yp;
|
|
}
|
|
}
|
|
|
|
if (!rc.FEmpty())
|
|
InvalRc(&rc, _fClear || _fMark ? kginMark : kginDraw);
|
|
|
|
_fXpValid = fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Perform a scroll according to scaHorz and scaVert.
|
|
***************************************************************************/
|
|
void TXTG::_Scroll(long scaHorz, long scaVert, long scvHorz, long scvVert)
|
|
{
|
|
RC rc;
|
|
long dxp, dyp;
|
|
|
|
GetRc(&rc, cooLocal);
|
|
dxp = 0;
|
|
switch (scaHorz)
|
|
{
|
|
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;
|
|
|
|
if (pvNil != _ptrul)
|
|
_ptrul->SetXpLeft(kdxpIndentTxtg - _scvHorz);
|
|
break;
|
|
}
|
|
|
|
dyp = 0;
|
|
if (scaVert != scaNil)
|
|
{
|
|
long cpT;
|
|
LIN lin, linDisp;
|
|
long ilin;
|
|
RC rc;
|
|
|
|
switch (scaVert)
|
|
{
|
|
case scaToVal:
|
|
cpT = LwBound(scvVert, 0, _ptxtb->CpMac());
|
|
_FindCp(cpT, &lin, &ilin);
|
|
dyp = lin.dypTot - _dypDisp;
|
|
_ilinDisp = ilin;
|
|
_cpDisp = lin.cpMin;
|
|
_dypDisp = lin.dypTot;
|
|
break;
|
|
|
|
case scaPageDown:
|
|
// scroll down a page
|
|
GetRc(&rc, cooLocal);
|
|
_FindDyp(rc.Dyp() + _dypDisp, &lin, &ilin);
|
|
|
|
if (lin.cpMin <= _cpDisp)
|
|
{
|
|
// we didn't go anywhere so force going down a line
|
|
_FetchLin(_ilinDisp + 1, &lin, &ilin);
|
|
}
|
|
else if (lin.dypTot + lin.dyp > _dypDisp + rc.Dyp() &&
|
|
ilin > _ilinDisp + 1)
|
|
{
|
|
// the line crosses the bottom of the ddg so back up one
|
|
Assert(ilin > 0, 0);
|
|
_FetchLin(ilin - 1, &lin, &ilin);
|
|
}
|
|
|
|
dyp = lin.dypTot - _dypDisp;
|
|
_ilinDisp = ilin;
|
|
_cpDisp = lin.cpMin;
|
|
_dypDisp = lin.dypTot;
|
|
break;
|
|
|
|
case scaLineDown:
|
|
// scroll down a line
|
|
_FetchLin(_ilinDisp + 1, &lin, &_ilinDisp);
|
|
dyp = lin.dypTot - _dypDisp;
|
|
_cpDisp = lin.cpMin;
|
|
_dypDisp = lin.dypTot;
|
|
break;
|
|
|
|
case scaPageUp:
|
|
// scroll down a page
|
|
if (_ilinDisp <= 0)
|
|
break;
|
|
GetRc(&rc, cooLocal);
|
|
_FetchLin(_ilinDisp, &linDisp);
|
|
Assert(linDisp.dypTot == _dypDisp, 0);
|
|
|
|
// determine where to scroll to - try to keep the top line
|
|
// visible, but scroll up at least one line
|
|
dyp = LwMax(0, linDisp.dypTot + linDisp.dyp - rc.Dyp());
|
|
_FindDyp(dyp, &lin, &ilin);
|
|
if (lin.cpMin >= linDisp.cpMin)
|
|
{
|
|
// we didn't go anywhere so force going up a line
|
|
_FetchLin(_ilinDisp - 1, &lin, &ilin);
|
|
}
|
|
else if (linDisp.dypTot + linDisp.dyp > lin.dypTot + rc.Dyp() &&
|
|
ilin < _ilinDisp - 1)
|
|
{
|
|
// the previous disp line crosses the bottom of the ddg, so move
|
|
// down one line
|
|
_FetchLin(ilin + 1, &lin, &ilin);
|
|
}
|
|
|
|
dyp = lin.dypTot - _dypDisp;
|
|
_ilinDisp = ilin;
|
|
_cpDisp = lin.cpMin;
|
|
_dypDisp = lin.dypTot;
|
|
break;
|
|
|
|
case scaLineUp:
|
|
// scroll up a line
|
|
if (_ilinDisp <= 0)
|
|
break;
|
|
_FetchLin(_ilinDisp - 1, &lin, &_ilinDisp);
|
|
dyp = lin.dypTot - _dypDisp;
|
|
_cpDisp = lin.cpMin;
|
|
_dypDisp = lin.dypTot;
|
|
break;
|
|
}
|
|
|
|
AssertIn(_cpDisp, 0, _ptxtb->CpMac());
|
|
_scvVert = _cpDisp;
|
|
}
|
|
|
|
_SetScrollValues();
|
|
if (dxp != 0 || dyp != 0)
|
|
_ScrollDxpDyp(dxp, dyp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Move the bits in the window.
|
|
***************************************************************************/
|
|
void TXTG::_ScrollDxpDyp(long dxp, long dyp)
|
|
{
|
|
AssertThis(0);
|
|
RC rcLoc, rcBad1, rcBad2;
|
|
|
|
// determine the dirty rectangles and update them
|
|
GetRc(&rcLoc, cooLocal);
|
|
if (_fClear)
|
|
{
|
|
InvalRc(&rcLoc, kginMark);
|
|
vpappb->UpdateMarked();
|
|
return;
|
|
}
|
|
|
|
Scroll(&rcLoc, -dxp, -dyp, _fMark ? kginMark : kginDraw);
|
|
if (_fMark)
|
|
vpappb->UpdateMarked();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Update the display of the document.
|
|
***************************************************************************/
|
|
void TXTG::Draw(PGNV pgnv, RC *prcClip)
|
|
{
|
|
AssertPo(pgnv, 0);
|
|
AssertVarMem(prcClip);
|
|
|
|
DrawLines(pgnv, prcClip, kdxpIndentTxtg - _scvHorz, 0, _ilinDisp);
|
|
if (_fSelOn)
|
|
_InvertSel(pgnv);
|
|
_SetScrollValues();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draws some lines of the document.
|
|
***************************************************************************/
|
|
void TXTG::DrawLines(PGNV pgnv, RC *prcClip, long dxp, long dyp,
|
|
long ilinMin, long ilinLim, ulong grftxtg)
|
|
{
|
|
AssertPo(pgnv, 0);
|
|
AssertVarMem(prcClip);
|
|
CHP chp;
|
|
PAP pap;
|
|
CHR chr;
|
|
CHRD chrd;
|
|
RC rc;
|
|
long xpBase, xp, yp, xpChr, dxpDoc;
|
|
long cpLine, cpCur, cpLimChp, cpLim, cpLimLine;
|
|
LIN lin;
|
|
long ilin;
|
|
|
|
cpLim = _ptxtb->CpMac();
|
|
dxpDoc = _DxpDoc();
|
|
_FetchLin(ilinMin, &lin);
|
|
cpLine = lin.cpMin;
|
|
|
|
pgnv->FillRc(prcClip, _ptxtb->AcrBack());
|
|
yp = dyp;
|
|
for (ilin = ilinMin; ilin < ilinLim; ilin++)
|
|
{
|
|
if (yp >= prcClip->ypBottom || cpLine >= cpLim)
|
|
break;
|
|
|
|
_FetchLin(ilin, &lin);
|
|
if (yp + lin.dyp <= prcClip->ypTop)
|
|
{
|
|
yp += lin.dyp;
|
|
cpLine += lin.ccp;
|
|
continue;
|
|
}
|
|
|
|
_FetchPap(cpLine, &pap);
|
|
xpBase = lin.xpLeft + dxp;
|
|
cpLimChp = cpCur = cpLine;
|
|
cpLimLine = cpLine + lin.ccp;
|
|
xpChr = 0;
|
|
|
|
// draw the line
|
|
for (;;)
|
|
{
|
|
// make sure the chp is valid
|
|
if (cpCur >= cpLimChp)
|
|
{
|
|
_FetchChp(cpCur, &chp, pvNil, &cpLimChp);
|
|
cpLimChp = LwMin(cpLimChp, cpLimLine);
|
|
Assert(cpLimChp > cpCur, "why is cpCur >= cpLimChp?");
|
|
chr.Init(&chp, &pap, _ptxtb, _pgnv, cpCur, cpLimChp,
|
|
xpChr, dxpDoc, dxpDoc);
|
|
}
|
|
|
|
chr.GetNextRun(fTrue);
|
|
chr.GetChrd(&chrd);
|
|
|
|
if (chrd.cpLimDraw > cpCur)
|
|
{
|
|
// draw some text
|
|
xp = xpBase + chr.XpMin();
|
|
|
|
if (chr.FObject())
|
|
{
|
|
// draw the object
|
|
_ptxtb->FDrawObject(chr.CpMin(), pgnv, &xp,
|
|
yp + lin.dypAscent + chp.dypOffset, &chp, prcClip);
|
|
}
|
|
else
|
|
{
|
|
pgnv->SetFont(chp.onn, chp.grfont, chp.dypFont,
|
|
tahLeft, tavBaseline);
|
|
|
|
pgnv->DrawRgch(chr.Prgch(), chrd.cpLimDraw - chr.CpMin(),
|
|
xp, yp + lin.dypAscent + chp.dypOffset,
|
|
chp.acrFore, chp.acrBack);
|
|
}
|
|
}
|
|
|
|
if (chrd.cpLim >= cpLimLine || chr.FBreak())
|
|
break;
|
|
|
|
cpCur = chrd.cpLim;
|
|
xpChr = chrd.xpLim;
|
|
}
|
|
|
|
_DrawLinExtra(pgnv, prcClip, &lin, dxp, yp, grftxtg);
|
|
|
|
yp += lin.dyp;
|
|
cpLine = cpLimLine;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Gives a subclass an opportunity to draw extra stuff associated with
|
|
the line. Default does nothing.
|
|
***************************************************************************/
|
|
void TXTG::_DrawLinExtra(PGNV pgnv, PRC prcClip, LIN *plin,
|
|
long dxp, long yp, ulong grftxtg)
|
|
{
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Handle a mousedown in the TXTG.
|
|
***************************************************************************/
|
|
bool TXTG::FCmdTrackMouse(PCMD_MOUSE pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
RC rc;
|
|
long cp;
|
|
long scaHorz, scaVert;
|
|
long xp = pcmd->xp;
|
|
long yp = pcmd->yp;
|
|
|
|
if (pcmd->cid == cidMouseDown)
|
|
{
|
|
Assert(vpcex->PgobTracking() == pvNil, "mouse already being tracked!");
|
|
_fSelByWord = (pcmd->cact > 1) && !(pcmd->grfcust & fcustShift);
|
|
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
|
|
if (_FGetCpFromPt(xp, yp, &cp, !_fSelByWord))
|
|
{
|
|
if (pcmd->cid != cidMouseDown || (pcmd->grfcust & fcustShift))
|
|
{
|
|
if (_fSelByWord)
|
|
{
|
|
cp = (cp < _cpAnchor) ? _ptxtb->CpPrev(cp + 1, fTrue) :
|
|
_ptxtb->CpNext(cp, fTrue);
|
|
}
|
|
SetSel(_cpAnchor, cp);
|
|
}
|
|
else
|
|
{
|
|
if (_fSelByWord)
|
|
cp = _ptxtb->CpPrev(cp + 1, fTrue);
|
|
SetSel(cp, _fSelByWord ? _ptxtb->CpNext(cp, fTrue) : cp);
|
|
}
|
|
_fXpValid = fFalse;
|
|
}
|
|
_SwitchSel(fTrue); //make sure the selection is on
|
|
|
|
if (!(pcmd->grfcust & fcustMouse))
|
|
vpcex->EndMouseTracking();
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
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 TXTG::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, kginDefault);
|
|
else if (_cpAnchor != _cpOther || _tsSel == 0)
|
|
_SwitchSel(fTrue);
|
|
else if (DtsCaret() < TsCurrent() - _tsSel)
|
|
_SwitchSel(!_fSelOn);
|
|
}
|
|
pcmd->rglw[0] = fFalse;
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the current selection.
|
|
***************************************************************************/
|
|
void TXTG::GetSel(long *pcpAnchor, long *pcpOther)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcpAnchor);
|
|
AssertVarMem(pcpOther);
|
|
|
|
*pcpAnchor = _cpAnchor;
|
|
*pcpOther = _cpOther;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the selection.
|
|
***************************************************************************/
|
|
void TXTG::SetSel(long cpAnchor, long cpOther, long gin)
|
|
{
|
|
AssertThis(0);
|
|
long cpMac = _ptxtb->CpMac();
|
|
|
|
cpAnchor = LwBound(cpAnchor, 0, cpMac);
|
|
cpOther = LwBound(cpOther, 0, cpMac);
|
|
|
|
if (cpAnchor == _cpAnchor && cpOther == _cpOther)
|
|
return;
|
|
|
|
if (_fSelOn)
|
|
{
|
|
if ((_fMark || _fClear) && gin == kginDraw)
|
|
gin = kginMark;
|
|
_pgnv->SetGobRc(this);
|
|
if (_cpAnchor != cpAnchor || _cpAnchor == _cpOther ||
|
|
cpAnchor == cpOther)
|
|
{
|
|
_InvertSel(_pgnv, gin);
|
|
_cpAnchor = cpAnchor;
|
|
_cpOther = cpOther;
|
|
_InvertSel(_pgnv, gin);
|
|
_tsSel = TsCurrent();
|
|
}
|
|
else
|
|
{
|
|
//they have the same anchor and neither is an insertion
|
|
_InvertCpRange(_pgnv, _cpOther, cpOther, gin);
|
|
_cpOther = cpOther;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_cpAnchor = cpAnchor;
|
|
_cpOther = cpOther;
|
|
_tsSel = 0L;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Make sure the selection is visible (at least the _cpOther end of it).
|
|
***************************************************************************/
|
|
void TXTG::ShowSel(void)
|
|
{
|
|
AssertThis(0);
|
|
long cpScroll;
|
|
long ilinOther, ilinAnchor, ilin;
|
|
long dxpScroll, dypSel;
|
|
long xpMin, xpLim;
|
|
RC rc;
|
|
LIN linOther, linAnchor, lin;
|
|
long cpAnchor = _cpAnchor;
|
|
|
|
//find the lines we want to show
|
|
_FindCp(_cpOther, &linOther, &ilinOther);
|
|
_FindCp(_cpAnchor, &linAnchor, &ilinAnchor);
|
|
linOther.dypTot -= _dypDisp;
|
|
linAnchor.dypTot -= _dypDisp;
|
|
GetRc(&rc, cooLocal);
|
|
|
|
cpScroll = _cpDisp;
|
|
if (!FIn(linOther.dypTot, 0, rc.Dyp() - linOther.dyp) ||
|
|
!FIn(linAnchor.dypTot, 0, rc.Dyp() - linAnchor.dyp))
|
|
{
|
|
if (_cpOther < cpAnchor)
|
|
dypSel = linAnchor.dypTot - linOther.dypTot + linAnchor.dyp;
|
|
else
|
|
dypSel = linOther.dypTot - linAnchor.dypTot + linOther.dyp;
|
|
if (dypSel > rc.Dyp())
|
|
{
|
|
//just show _cpOther
|
|
cpAnchor = _cpOther;
|
|
ilinAnchor = ilinOther;
|
|
linAnchor = linOther;
|
|
dypSel = linOther.dyp;
|
|
}
|
|
|
|
if (linOther.dypTot < 0 || linAnchor.dypTot < 0)
|
|
{
|
|
//scroll up
|
|
cpScroll = LwMin(linOther.cpMin, linAnchor.cpMin);
|
|
}
|
|
else if (linOther.dypTot + linOther.dyp >= rc.Dyp() ||
|
|
linAnchor.dypTot + linAnchor.dyp >= rc.Dyp())
|
|
{
|
|
//scroll down
|
|
ilin = LwMin(ilinOther, ilinAnchor);
|
|
cpScroll = LwMin(linOther.cpMin, linAnchor.cpMin);
|
|
dypSel = rc.Dyp() - dypSel;
|
|
for (;;)
|
|
{
|
|
if (--ilin < 0)
|
|
break;
|
|
_FetchLin(ilin, &lin);
|
|
if (lin.dyp > dypSel)
|
|
break;
|
|
cpScroll -= lin.ccp;
|
|
dypSel -= lin.dyp;
|
|
}
|
|
}
|
|
}
|
|
|
|
//now do the horizontal stuff
|
|
xpMin = _DxpFromCp(linOther.cpMin, _cpOther) + linOther.xpLeft - _scvHorz;
|
|
xpLim = _DxpFromCp(linAnchor.cpMin, cpAnchor) + linAnchor.xpLeft - _scvHorz;
|
|
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 || cpScroll != _cpDisp)
|
|
_Scroll(scaToVal, scaToVal, _scvHorz - dxpScroll, cpScroll);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Turn the selection off.
|
|
***************************************************************************/
|
|
void TXTG::HideSel(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (!_fSelOn)
|
|
return;
|
|
|
|
_SwitchSel(fFalse);
|
|
_tsSel = 0;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Turn the sel on or off according to fOn.
|
|
***************************************************************************/
|
|
void TXTG::_SwitchSel(bool fOn, long gin)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (FPure(fOn) != FPure(_fSelOn))
|
|
{
|
|
if ((_fMark || _fClear) && gin == kginDraw)
|
|
gin = kginMark;
|
|
_pgnv->SetGobRc(this);
|
|
_InvertSel(_pgnv, gin);
|
|
_fSelOn = FPure(fOn);
|
|
_tsSel = TsCurrent();
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Invert the current selection.
|
|
***************************************************************************/
|
|
void TXTG::_InvertSel(PGNV pgnv, long gin)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pgnv, 0);
|
|
RC rc, rcT;
|
|
|
|
GetRc(&rc, cooLocal);
|
|
if (_cpAnchor == _cpOther)
|
|
{
|
|
//insertion bar
|
|
_GetXpYpFromCp(_cpAnchor, &rcT.ypTop, &rcT.ypBottom, &rcT.xpLeft);
|
|
rcT.xpRight = --rcT.xpLeft + 2;
|
|
if (rcT.FIntersect(&rc))
|
|
{
|
|
if (kginDraw == gin)
|
|
pgnv->FillRc(&rcT, kacrInvert);
|
|
else
|
|
InvalRc(&rcT, gin);
|
|
}
|
|
}
|
|
else
|
|
_InvertCpRange(pgnv, _cpAnchor, _cpOther, gin);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Invert a range.
|
|
***************************************************************************/
|
|
void TXTG::_InvertCpRange(PGNV pgnv, long cp1, long cp2, long gin)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pgnv, 0);
|
|
AssertIn(cp1, 0, _ptxtb->CpMac());
|
|
AssertIn(cp2, 0, _ptxtb->CpMac());
|
|
RC rc1, rc2, rcClip, rcT, rcDoc;
|
|
|
|
if (cp1 == cp2)
|
|
return;
|
|
SortLw(&cp1, &cp2);
|
|
_GetXpYpFromCp(cp1, &rc1.ypTop, &rc1.ypBottom, &rc1.xpLeft);
|
|
_GetXpYpFromCp(cp2, &rc2.ypTop, &rc2.ypBottom, &rc2.xpRight);
|
|
GetRc(&rcClip, cooLocal);
|
|
if (rc1.ypTop >= rcClip.ypBottom || rc2.ypBottom <= rcClip.ypTop)
|
|
return;
|
|
|
|
if (rc1.ypTop == rc2.ypTop && rc1.ypBottom == rc2.ypBottom)
|
|
{
|
|
// only one line involved
|
|
rc1.xpRight = rc2.xpRight;
|
|
if (rcT.FIntersect(&rc1, &rcClip))
|
|
{
|
|
if (kginDraw == gin)
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
else
|
|
InvalRc(&rcT, gin);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// invert the sel on the first line
|
|
rc1.xpRight = kdxpIndentTxtg - _scvHorz + _DxpDoc();
|
|
if (rcT.FIntersect(&rc1, &rcClip))
|
|
{
|
|
if (kginDraw == gin)
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
else
|
|
InvalRc(&rcT, gin);
|
|
}
|
|
|
|
//invert the main rectangular block
|
|
rc1.xpLeft = kdxpIndentTxtg - _scvHorz;
|
|
rc1.ypTop = rc1.ypBottom;
|
|
rc1.ypBottom = rc2.ypTop;
|
|
if (rcT.FIntersect(&rc1, &rcClip))
|
|
{
|
|
if (kginDraw == gin)
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
else
|
|
InvalRc(&rcT, gin);
|
|
}
|
|
|
|
//invert the last line
|
|
rc2.xpLeft = rc1.xpLeft;
|
|
if (rcT.FIntersect(&rc2, &rcClip))
|
|
{
|
|
if (kginDraw == gin)
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
else
|
|
InvalRc(&rcT, gin);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Handle a key down.
|
|
***************************************************************************/
|
|
bool TXTG::FCmdKey(PCMD_KEY pcmd)
|
|
{
|
|
const long kcchInsBuf = 64;
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
ulong grfcust;
|
|
long vkDone;
|
|
long ichLim;
|
|
long cact;
|
|
long dcp, cpT;
|
|
CMD cmd;
|
|
LIN lin, linT;
|
|
long ilin, ilinT;
|
|
RC rc;
|
|
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, _cpAnchor, _cpOther);
|
|
}
|
|
|
|
dcp = 0;
|
|
switch (vkDone)
|
|
{
|
|
case kvkHome:
|
|
if (grfcust & fcustCmd)
|
|
dcp = -_cpOther;
|
|
else
|
|
{
|
|
_FindCp(_cpOther, &lin);
|
|
dcp = lin.cpMin - _cpOther;
|
|
}
|
|
_fXpValid = fFalse;
|
|
goto LSetSel;
|
|
|
|
case kvkEnd:
|
|
if (grfcust & fcustCmd)
|
|
dcp = _ptxtb->CpMac() - _cpOther;
|
|
else
|
|
{
|
|
_FindCp(_cpOther, &lin);
|
|
cpT = _ptxtb->CpPrev(lin.cpMin + lin.ccp);
|
|
AssertIn(cpT, _cpOther, _ptxtb->CpMac());
|
|
dcp = cpT - _cpOther;
|
|
}
|
|
_fXpValid = fFalse;
|
|
goto LSetSel;
|
|
|
|
case kvkLeft:
|
|
dcp = _ptxtb->CpPrev(_cpOther, FPure(grfcust & fcustCmd)) - _cpOther;
|
|
_fXpValid = fFalse;
|
|
goto LSetSel;
|
|
|
|
case kvkRight:
|
|
dcp = _ptxtb->CpNext(_cpOther, FPure(grfcust & fcustCmd)) - _cpOther;
|
|
_fXpValid = fFalse;
|
|
goto LSetSel;
|
|
|
|
case kvkUp:
|
|
case kvkPageUp:
|
|
case kvkDown:
|
|
case kvkPageDown:
|
|
// get the LIN for _cpOther and make sure _xpSel is up to date
|
|
_FindCp(_cpOther, &lin, &ilin);
|
|
if (!_fXpValid)
|
|
{
|
|
// get the xp of _cpOther
|
|
_xpSel = lin.xpLeft + _DxpFromCp(lin.cpMin, _cpOther);
|
|
_fXpValid = fTrue;
|
|
}
|
|
|
|
switch (vkDone)
|
|
{
|
|
case kvkUp:
|
|
if (ilin == 0)
|
|
{
|
|
dcp = -_cpOther;
|
|
goto LSetSel;
|
|
}
|
|
_FetchLin(ilin - 1, &lin);
|
|
break;
|
|
|
|
case kvkPageUp:
|
|
if (ilin == 0)
|
|
{
|
|
dcp = -_cpOther;
|
|
goto LSetSel;
|
|
}
|
|
GetRc(&rc, cooLocal);
|
|
_FindDyp(LwMax(0, lin.dypTot - rc.Dyp() + lin.dyp), &linT, &ilinT);
|
|
|
|
if (linT.cpMin >= lin.cpMin)
|
|
{
|
|
// we didn't go anywhere so force going up a line
|
|
_FetchLin(ilin - 1, &lin);
|
|
}
|
|
else if (lin.dypTot + lin.dyp > linT.dypTot + rc.Dyp() &&
|
|
ilinT < ilin - 1)
|
|
{
|
|
// the previous line crosses the bottom of the ddg, so move
|
|
// down one line
|
|
_FetchLin(ilinT + 1, &lin);
|
|
}
|
|
else
|
|
lin = linT;
|
|
break;
|
|
|
|
case kvkDown:
|
|
if (lin.cpMin + lin.ccp >= _ptxtb->CpMac())
|
|
{
|
|
dcp = _ptxtb->CpMac() - _cpOther;
|
|
goto LSetSel;
|
|
}
|
|
_FetchLin(ilin + 1, &lin);
|
|
break;
|
|
|
|
case kvkPageDown:
|
|
if (lin.cpMin + lin.ccp >= _ptxtb->CpMac())
|
|
{
|
|
dcp = _ptxtb->CpMac() - _cpOther;
|
|
goto LSetSel;
|
|
}
|
|
|
|
GetRc(&rc, cooLocal);
|
|
_FindDyp(lin.dypTot + rc.Dyp(), &linT, &ilinT);
|
|
|
|
if (linT.cpMin <= lin.cpMin)
|
|
{
|
|
// we didn't go anywhere so force going down a line
|
|
_FetchLin(ilin + 1, &lin);
|
|
}
|
|
else if (linT.dypTot + linT.dyp > lin.dypTot + rc.Dyp()
|
|
&& ilinT > ilin + 1)
|
|
{
|
|
// the line crosses the bottom of the ddg so back up one line
|
|
Assert(ilinT > 0, 0);
|
|
_FetchLin(ilinT - 1, &lin);
|
|
}
|
|
else
|
|
lin = linT;
|
|
break;
|
|
}
|
|
|
|
// we have the line, now find the position on the line
|
|
_FGetCpFromXp(_xpSel - _scvHorz, &lin, &dcp);
|
|
dcp -= _cpOther;
|
|
|
|
LSetSel:
|
|
//move the selection
|
|
if (grfcust & fcustShift)
|
|
{
|
|
//extend selection
|
|
SetSel(_cpAnchor, _cpOther + dcp);
|
|
ShowSel();
|
|
}
|
|
else
|
|
{
|
|
cpT = _cpOther;
|
|
if (cpT == _cpAnchor || (grfcust & fcustCmd))
|
|
cpT += dcp;
|
|
else if ((dcp > 0) != (cpT > _cpAnchor))
|
|
cpT = _cpAnchor;
|
|
SetSel(cpT, cpT);
|
|
ShowSel();
|
|
}
|
|
break;
|
|
|
|
case kvkDelete:
|
|
case kvkBack:
|
|
if (_cpAnchor != _cpOther)
|
|
dcp = _cpOther - _cpAnchor;
|
|
else if (vkDone == kvkDelete)
|
|
{
|
|
dcp = _ptxtb->CpNext(_cpAnchor) - _cpAnchor;
|
|
if (_cpAnchor + dcp >= _ptxtb->CpMac())
|
|
dcp = 0;
|
|
}
|
|
else
|
|
dcp = _ptxtb->CpPrev(_cpAnchor) - _cpAnchor;
|
|
if (dcp != 0)
|
|
FReplace(pvNil, 0, _cpAnchor, _cpAnchor + dcp);
|
|
break;
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the maximum scroll value for this view of the doc.
|
|
***************************************************************************/
|
|
long TXTG::_ScvMax(bool fVert)
|
|
{
|
|
RC rc;
|
|
long dxp;
|
|
|
|
if (fVert)
|
|
return _ptxtb->CpMac() - 1;
|
|
|
|
dxp = _DxpDoc() + 2 * kdxpIndentTxtg;
|
|
GetRc(&rc, cooLocal);
|
|
return LwMax(0, dxp - rc.Dxp());
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the logical width of the text "page".
|
|
***************************************************************************/
|
|
long TXTG::_DxpDoc(void)
|
|
{
|
|
return _ptxtb->DxpDef();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the tab width. Default does nothing.
|
|
***************************************************************************/
|
|
void TXTG::SetDxpTab(long dxp)
|
|
{
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the document width. Default calls SetDxpDef on the TXTB.
|
|
***************************************************************************/
|
|
void TXTG::SetDxpDoc(long dxp)
|
|
{
|
|
AssertThis(0);
|
|
dxp = LwBound(dxp, 1, kcbMax);
|
|
_ptxtb->SetDxpDef(dxp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Show or hide the ruler.
|
|
***************************************************************************/
|
|
void TXTG::ShowRuler(bool fShow)
|
|
{
|
|
AssertThis(0);
|
|
RC rcAbs, rcRel;
|
|
long dyp;
|
|
|
|
if (FPure(fShow) == (_ptrul != pvNil))
|
|
return;
|
|
|
|
dyp = _DypTrul();
|
|
if (fShow)
|
|
{
|
|
PGOB pgob;
|
|
GCB gcb;
|
|
|
|
pgob = PgobPar();
|
|
if (pvNil == pgob || !pgob->FIs(kclsDSG))
|
|
return;
|
|
|
|
GetPos(&rcAbs, &rcRel);
|
|
rcAbs.ypTop += dyp;
|
|
SetPos(&rcAbs, &rcRel);
|
|
|
|
rcRel.ypBottom = rcRel.ypTop;
|
|
rcAbs.ypBottom = rcAbs.ypTop;
|
|
rcAbs.ypTop -= dyp;
|
|
gcb.Set(HidUnique(), pgob, fgobNil, kginDefault, &rcAbs, &rcRel);
|
|
|
|
if (pvNil == (_ptrul = _PtrulNew(&gcb)))
|
|
goto LFail;
|
|
}
|
|
else
|
|
{
|
|
ReleasePpo(&_ptrul);
|
|
LFail:
|
|
GetPos(&rcAbs, &rcRel);
|
|
rcAbs.ypTop -= dyp;
|
|
SetPos(&rcAbs, &rcRel);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the height of the ruler.
|
|
***************************************************************************/
|
|
long TXTG::_DypTrul(void)
|
|
{
|
|
AssertThis(0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Create the ruler.
|
|
***************************************************************************/
|
|
PTRUL TXTG::_PtrulNew(PGCB pgcb)
|
|
{
|
|
AssertThis(0);
|
|
return pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the natural width and height of the view on the document.
|
|
***************************************************************************/
|
|
void TXTG::GetNaturalSize(long *pdxp, long *pdyp)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(pdxp);
|
|
AssertNilOrVarMem(pdyp);
|
|
LIN lin;
|
|
|
|
if (pvNil != pdxp)
|
|
*pdxp = _DxpDoc() + 2 * kdxpIndentTxtg;
|
|
if (pvNil == pdyp)
|
|
return;
|
|
|
|
_FetchLin(kcbMax - 1, &lin);
|
|
*pdyp = lin.dypTot + lin.dyp;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for the plain line text document display gob.
|
|
***************************************************************************/
|
|
TXLG::TXLG(PTXTB ptxtb, PGCB pgcb, long onn, ulong grfont, long dypFont, long cchTab)
|
|
: TXLG_PAR(ptxtb, pgcb)
|
|
{
|
|
RC rc;
|
|
achar ch = kchSpace;
|
|
GNV gnv(this);
|
|
|
|
gnv.SetFont(onn, fontNil, dypFont);
|
|
gnv.GetRcFromRgch(&rc, &ch, 1);
|
|
_dxpChar = rc.Dxp();
|
|
_onn = onn;
|
|
_grfont = grfont;
|
|
_dypFont = dypFont;
|
|
_cchTab = LwBound(cchTab, 1, kcbMax);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static method to create a new plain line text doc display gob.
|
|
***************************************************************************/
|
|
PTXLG TXLG::PtxlgNew(PTXTB ptxtb, PGCB pgcb, long onn, ulong grfont,
|
|
long dypFont, long cchTab)
|
|
{
|
|
PTXLG ptxlg;
|
|
|
|
if (pvNil == (ptxlg = NewObj TXLG(ptxtb, pgcb, onn, grfont, dypFont, cchTab)))
|
|
return pvNil;
|
|
if (!ptxlg->_FInit())
|
|
{
|
|
ReleasePpo(&ptxlg);
|
|
return pvNil;
|
|
}
|
|
ptxlg->Activate(fTrue);
|
|
return ptxlg;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the width of the logical "page". For a TXLG, this is some big
|
|
value, so we do no word wrap.
|
|
***************************************************************************/
|
|
long TXLG::_DxpDoc(void)
|
|
{
|
|
return kswMax;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the tab width.
|
|
***************************************************************************/
|
|
void TXLG::SetDxpTab(long dxp)
|
|
{
|
|
AssertThis(0);
|
|
long cch;
|
|
|
|
cch = LwBound(dxp / _dxpChar, 1, kswMax / _dxpChar);
|
|
if (cch != _cchTab)
|
|
{
|
|
_cchTab = cch;
|
|
InvalCp(0, _ptxtb->CpMac(), _ptxtb->CpMac());
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the document width. Does nothing.
|
|
***************************************************************************/
|
|
void TXLG::SetDxpDoc(long dxp)
|
|
{
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the character properties for display. These are the same for all
|
|
characters in the TXLG.
|
|
***************************************************************************/
|
|
void TXLG::_FetchChp(long cp, PCHP pchp, long *pcpMin, long *pcpLim)
|
|
{
|
|
AssertIn(cp, 0, _ptxtb->CpMac());
|
|
AssertVarMem(pchp);
|
|
AssertNilOrVarMem(pcpMin);
|
|
AssertNilOrVarMem(pcpLim);
|
|
|
|
pchp->Clear();
|
|
pchp->grfont = _grfont;
|
|
pchp->onn = _onn;
|
|
pchp->dypFont = _dypFont;
|
|
pchp->acrFore = kacrBlack;
|
|
pchp->acrBack = kacrWhite;
|
|
if (pvNil != pcpMin)
|
|
*pcpMin = 0;
|
|
if (pvNil != pcpLim)
|
|
*pcpLim = _ptxtb->CpMac();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the paragraph properties for disply. These are constant for all
|
|
characters in the TXLG.
|
|
***************************************************************************/
|
|
void TXLG::_FetchPap(long cp, PPAP ppap, long *pcpMin, long *pcpLim)
|
|
{
|
|
AssertIn(cp, 0, _ptxtb->CpMac());
|
|
AssertVarMem(ppap);
|
|
AssertNilOrVarMem(pcpMin);
|
|
AssertNilOrVarMem(pcpLim);
|
|
|
|
ClearPb(ppap, size(PAP));
|
|
ppap->dxpTab = (short)LwMul(_cchTab, _dxpChar);
|
|
ppap->numLine = kdenLine;
|
|
ppap->numAfter = kdenAfter;
|
|
if (pvNil != pcpMin)
|
|
*pcpMin = 0;
|
|
if (pvNil != pcpLim)
|
|
*pcpLim = _ptxtb->CpMac();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Copy the selection.
|
|
***************************************************************************/
|
|
bool TXLG::_FCopySel(PDOCB *ppdocb)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(ppdocb);
|
|
PTXPD ptxpd;
|
|
|
|
if (_cpAnchor == _cpOther)
|
|
return fFalse;
|
|
|
|
if (pvNil == ppdocb)
|
|
return fTrue;
|
|
|
|
if (pvNil != (ptxpd = TXPD::PtxpdNew(pvNil)))
|
|
{
|
|
long cpMin = LwMin(_cpAnchor, _cpOther);
|
|
long cpLim = LwMax(_cpAnchor, _cpOther);
|
|
|
|
ptxpd->SuspendUndo();
|
|
if (!ptxpd->FReplaceBsf(_ptxtb->Pbsf(), cpMin, cpLim - cpMin,
|
|
0, 0, fdocNil))
|
|
{
|
|
ReleasePpo(&ptxpd);
|
|
}
|
|
else
|
|
ptxpd->ResumeUndo();
|
|
}
|
|
|
|
*ppdocb = ptxpd;
|
|
return pvNil != *ppdocb;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Delete the selection.
|
|
***************************************************************************/
|
|
void TXLG::_ClearSel(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
FReplace(pvNil, 0, _cpAnchor, _cpOther);
|
|
ShowSel();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Paste the selection.
|
|
***************************************************************************/
|
|
bool TXLG::_FPaste(PCLIP pclip, bool fDoIt, long cid)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pclip, 0);
|
|
long cp1, cp2;
|
|
long ccp;
|
|
PTXTB ptxtb;
|
|
|
|
if (cid != cidPaste || !pclip->FGetFormat(kclsTXTB))
|
|
return fFalse;
|
|
|
|
if (!fDoIt)
|
|
return fTrue;
|
|
|
|
if (!pclip->FGetFormat(kclsTXTB, (PDOCB *)&ptxtb))
|
|
return fFalse;
|
|
|
|
AssertPo(ptxtb, 0);
|
|
if ((ccp = ptxtb->CpMac() - 1) <= 0)
|
|
{
|
|
ReleasePpo(&ptxtb);
|
|
return fTrue;
|
|
}
|
|
|
|
HideSel();
|
|
cp1 = LwMin(_cpAnchor, _cpOther);
|
|
cp2 = LwMax(_cpAnchor, _cpOther);
|
|
if (!_ptxtb->FReplaceTxtb(ptxtb, 0, ccp, cp1, cp2 - cp1))
|
|
{
|
|
ReleasePpo(&ptxtb);
|
|
return fFalse;
|
|
}
|
|
ReleasePpo(&ptxtb);
|
|
|
|
cp1 += ccp;
|
|
SetSel(cp1, cp1);
|
|
ShowSel();
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for a rich text document display gob.
|
|
***************************************************************************/
|
|
TXRG::TXRG(PTXRD ptxrd, PGCB pgcb) : TXRG_PAR(ptxrd, pgcb)
|
|
{
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Create a new rich text document display GOB.
|
|
***************************************************************************/
|
|
PTXRG TXRG::PtxrgNew(PTXRD ptxrd, PGCB pgcb)
|
|
{
|
|
PTXRG ptxrg;
|
|
|
|
if (pvNil == (ptxrg = NewObj TXRG(ptxrd, pgcb)))
|
|
return pvNil;
|
|
if (!ptxrg->_FInit())
|
|
{
|
|
ReleasePpo(&ptxrg);
|
|
return pvNil;
|
|
}
|
|
ptxrg->Activate(fTrue);
|
|
return ptxrg;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a TXRG.
|
|
***************************************************************************/
|
|
void TXRG::AssertValid(ulong grf)
|
|
{
|
|
TXRG_PAR::AssertValid(0);
|
|
AssertNilOrPo(_ptrul, 0);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Get the character properties for displaying the given cp.
|
|
***************************************************************************/
|
|
void TXRG::_FetchChp(long cp, PCHP pchp, long *pcpMin, long *pcpLim)
|
|
{
|
|
((PTXRD)_ptxtb)->FetchChp(cp, pchp, pcpMin, pcpLim);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the paragraph properties for displaying the given cp.
|
|
***************************************************************************/
|
|
void TXRG::_FetchPap(long cp, PPAP ppap, long *pcpMin, long *pcpLim)
|
|
{
|
|
((PTXRD)_ptxtb)->FetchPap(cp, ppap, pcpMin, pcpLim);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the tab width for the currently selected paragraph(s).
|
|
***************************************************************************/
|
|
void TXRG::SetDxpTab(long dxp)
|
|
{
|
|
AssertThis(0);
|
|
long cpMin, cpLim, cpAnchor, cpOther;
|
|
PAP papOld, papNew;
|
|
|
|
dxp = LwRoundClosest(dxp, kdzpInch / 8);
|
|
dxp = LwBound(dxp, kdzpInch / 8, 100 * kdzpInch);
|
|
ClearPb(&papOld, size(PAP));
|
|
papNew = papOld;
|
|
|
|
cpMin = LwMin(cpAnchor = _cpAnchor, cpOther = _cpOther);
|
|
cpLim = LwMax(_cpAnchor, _cpOther);
|
|
|
|
papNew.dxpTab = (short)dxp;
|
|
_SwitchSel(fFalse, ginNil);
|
|
if (!((PTXRD)_ptxtb)->FApplyPap(cpMin, cpLim - cpMin, &papNew, &papOld,
|
|
&cpMin, &cpLim))
|
|
{
|
|
_SwitchSel(fTrue, kginMark);
|
|
}
|
|
|
|
SetSel(cpAnchor, cpOther);
|
|
ShowSel();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the selection for the TXRG. Invalidates _chpIns if the selection
|
|
changes.
|
|
***************************************************************************/
|
|
void TXRG::SetSel(long cpAnchor, long cpOther, long gin)
|
|
{
|
|
AssertThis(0);
|
|
long cpMac = _ptxtb->CpMac();
|
|
|
|
cpAnchor = LwBound(cpAnchor, 0, cpMac);
|
|
cpOther = LwBound(cpOther, 0, cpMac);
|
|
|
|
if (cpAnchor == _cpAnchor && cpOther == _cpOther)
|
|
return;
|
|
|
|
_fValidChp = fFalse;
|
|
TXRG_PAR::SetSel(cpAnchor, cpOther, gin);
|
|
if (pvNil != _ptrul)
|
|
{
|
|
PAP pap;
|
|
|
|
_FetchPap(LwMin(_cpAnchor, _cpOther), &pap);
|
|
_ptrul->SetDxpTab(pap.dxpTab);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the chp to use to replace the given range of characters. If the
|
|
range is empty and not at the beginning of a paragraph, gets the chp
|
|
of the previous character. Otherwise gets the chp of the character at
|
|
LwMin(cp1, cp2).
|
|
***************************************************************************/
|
|
void TXRG::_FetchChpSel(long cp1, long cp2, PCHP pchp)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pchp);
|
|
|
|
long cp = LwMin(cp1, cp2);
|
|
|
|
if (cp1 == cp2 && cp1 > 0)
|
|
{
|
|
if (!_ptxtb->FMinPara(cp) ||
|
|
_ptxtb->FMinPara(cp2 = _ptxtb->CpNext(cp)) ||
|
|
cp2 >= _ptxtb->CpMac())
|
|
{
|
|
cp = _ptxtb->CpPrev(cp);
|
|
}
|
|
}
|
|
|
|
// NOTE: don't just call _FetchChp here. _FetchChp allows derived
|
|
// classes to modify the chp used for display without affecting the
|
|
// chp used for editing. This chp is an editing chp. Same note
|
|
// applies several other places in this file.
|
|
((PTXRD)_ptxtb)->FetchChp(cp, pchp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Make sure the _chpIns is valid.
|
|
***************************************************************************/
|
|
void TXRG::_EnsureChpIns(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (!_fValidChp)
|
|
{
|
|
_FetchChpSel(_cpAnchor, _cpOther, &_chpIns);
|
|
_fValidChp = fTrue;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Replaces the characters between cp1 and cp2 with the given ones.
|
|
***************************************************************************/
|
|
bool TXRG::FReplace(achar *prgch, long cch, long cp1, long cp2)
|
|
{
|
|
AssertIn(cch, 0, kcbMax);
|
|
AssertPvCb(prgch, cch);
|
|
AssertIn(cp1, 0, _ptxtb->CpMac());
|
|
AssertIn(cp2, 0, _ptxtb->CpMac());
|
|
CHP chp;
|
|
bool fSetChpIns = fFalse;
|
|
|
|
HideSel();
|
|
SortLw(&cp1, &cp2);
|
|
if (cp1 == LwMin(_cpAnchor, _cpOther) && cp2 == LwMax(_cpAnchor, _cpOther))
|
|
{
|
|
_EnsureChpIns();
|
|
chp = _chpIns;
|
|
fSetChpIns = fTrue;
|
|
}
|
|
else
|
|
_FetchChpSel(cp1, cp2, &chp);
|
|
|
|
if (!((PTXRD)_ptxtb)->FReplaceRgch(prgch, cch, cp1, cp2 - cp1, &chp))
|
|
return fFalse;
|
|
|
|
cp1 += cch;
|
|
SetSel(cp1, cp1);
|
|
ShowSel();
|
|
|
|
if (fSetChpIns)
|
|
{
|
|
// preserve the _chpIns
|
|
_chpIns = chp;
|
|
_fValidChp = fTrue;
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Copy the selection.
|
|
***************************************************************************/
|
|
bool TXRG::_FCopySel(PDOCB *ppdocb)
|
|
{
|
|
AssertNilOrVarMem(ppdocb);
|
|
PTXRD ptxrd;
|
|
|
|
if (_cpAnchor == _cpOther)
|
|
return fFalse;
|
|
|
|
if (pvNil == ppdocb)
|
|
return fTrue;
|
|
|
|
if (pvNil != (ptxrd = TXRD::PtxrdNew(pvNil)))
|
|
{
|
|
long cpMin = LwMin(_cpAnchor, _cpOther);
|
|
long cpLim = LwMax(_cpAnchor, _cpOther);
|
|
|
|
ptxrd->SuspendUndo();
|
|
if (!ptxrd->FReplaceTxrd((PTXRD)_ptxtb, cpMin, cpLim - cpMin,
|
|
0, 0, fdocNil))
|
|
{
|
|
ReleasePpo(&ptxrd);
|
|
}
|
|
else
|
|
ptxrd->ResumeUndo();
|
|
}
|
|
|
|
*ppdocb = ptxrd;
|
|
return pvNil != *ppdocb;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Delete the selection.
|
|
***************************************************************************/
|
|
void TXRG::_ClearSel(void)
|
|
{
|
|
FReplace(pvNil, 0, _cpAnchor, _cpOther);
|
|
ShowSel();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Paste the selection.
|
|
***************************************************************************/
|
|
bool TXRG::_FPaste(PCLIP pclip, bool fDoIt, long cid)
|
|
{
|
|
AssertThis(0);
|
|
long cp1, cp2;
|
|
long ccp;
|
|
PTXTB ptxtb;
|
|
bool fRet;
|
|
|
|
if (cid != cidPaste || !pclip->FGetFormat(kclsTXTB))
|
|
return fFalse;
|
|
|
|
if (!fDoIt)
|
|
return fTrue;
|
|
|
|
if (!pclip->FGetFormat(kclsTXRD, (PDOCB *)&ptxtb) &&
|
|
!pclip->FGetFormat(kclsTXTB, (PDOCB *)&ptxtb))
|
|
{
|
|
return fFalse;
|
|
}
|
|
|
|
AssertPo(ptxtb, 0);
|
|
if ((ccp = ptxtb->CpMac() - 1) <= 0)
|
|
{
|
|
ReleasePpo(&ptxtb);
|
|
return fTrue;
|
|
}
|
|
|
|
HideSel();
|
|
cp1 = LwMin(_cpAnchor, _cpOther);
|
|
cp2 = LwMax(_cpAnchor, _cpOther);
|
|
_ptxtb->BumpCombineUndo();
|
|
if (ptxtb->FIs(kclsTXRD))
|
|
{
|
|
fRet = ((PTXRD)_ptxtb)->FReplaceTxrd((PTXRD)ptxtb, 0, ccp,
|
|
cp1, cp2 - cp1);
|
|
}
|
|
else
|
|
{
|
|
_EnsureChpIns();
|
|
fRet = ((PTXRD)_ptxtb)->FReplaceTxtb(ptxtb, 0, ccp, cp1, cp2 - cp1,
|
|
&_chpIns);
|
|
}
|
|
ReleasePpo(&ptxtb);
|
|
|
|
if (fRet)
|
|
{
|
|
_ptxtb->BumpCombineUndo();
|
|
cp1 += ccp;
|
|
SetSel(cp1, cp1);
|
|
ShowSel();
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Apply the given character properties to the current selection.
|
|
***************************************************************************/
|
|
bool TXRG::FApplyChp(PCHP pchp, PCHP pchpDiff)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pchp);
|
|
AssertNilOrVarMem(pchpDiff);
|
|
|
|
long cpMin, cpLim, cpAnchor, cpOther;
|
|
ulong grfont;
|
|
|
|
cpMin = LwMin(cpAnchor = _cpAnchor, cpOther = _cpOther);
|
|
cpLim = LwMax(_cpAnchor, _cpOther);
|
|
|
|
if (cpMin == cpLim)
|
|
{
|
|
if (pvNil == pchpDiff)
|
|
{
|
|
_chpIns = *pchp;
|
|
_fValidChp = fTrue;
|
|
return fTrue;
|
|
}
|
|
|
|
_EnsureChpIns();
|
|
if (fontNil != (grfont = pchp->grfont ^ pchpDiff->grfont))
|
|
_chpIns.grfont = (_chpIns.grfont & ~grfont) | (pchp->grfont & grfont);
|
|
if (pchp->onn != pchpDiff->onn)
|
|
_chpIns.onn = pchp->onn;
|
|
if (pchp->dypFont != pchpDiff->dypFont)
|
|
_chpIns.dypFont = pchp->dypFont;
|
|
if (pchp->dypOffset != pchpDiff->dypOffset)
|
|
_chpIns.dypOffset = pchp->dypOffset;
|
|
if (pchp->acrFore != pchpDiff->acrFore)
|
|
_chpIns.acrFore = pchp->acrFore;
|
|
if (pchp->acrBack != pchpDiff->acrBack)
|
|
_chpIns.acrBack = pchp->acrBack;
|
|
AssertThis(0);
|
|
return fTrue;
|
|
}
|
|
|
|
_SwitchSel(fFalse, ginNil);
|
|
if (!((PTXRD)_ptxtb)->FApplyChp(cpMin, cpLim - cpMin, pchp, pchpDiff))
|
|
{
|
|
_SwitchSel(fTrue, kginMark);
|
|
return fFalse;
|
|
}
|
|
_fValidChp = fFalse;
|
|
SetSel(cpAnchor, cpOther);
|
|
ShowSel();
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Apply the given paragraph properties to the current selection.
|
|
***************************************************************************/
|
|
bool TXRG::FApplyPap(PPAP ppap, PPAP ppapDiff, bool fExpand)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(ppap);
|
|
AssertNilOrVarMem(ppapDiff);
|
|
long cpMin, cpLim, cpAnchor, cpOther;
|
|
|
|
cpMin = LwMin(cpAnchor = _cpAnchor, cpOther = _cpOther);
|
|
cpLim = LwMax(_cpAnchor, _cpOther);
|
|
|
|
_SwitchSel(fFalse, ginNil);
|
|
if (!((PTXRD)_ptxtb)->FApplyPap(cpMin, cpLim - cpMin, ppap, ppapDiff,
|
|
pvNil, pvNil, fExpand))
|
|
{
|
|
_SwitchSel(fTrue, kginMark);
|
|
return fFalse;
|
|
}
|
|
|
|
SetSel(cpAnchor, cpOther);
|
|
ShowSel();
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Apply a character or paragraph property
|
|
***************************************************************************/
|
|
bool TXRG::FCmdApplyProperty(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
CHP chpOld, chpNew;
|
|
PAP papOld, papNew;
|
|
long onn;
|
|
STN stn;
|
|
|
|
ClearPb(&papOld, size(PAP));
|
|
papNew = papOld;
|
|
|
|
_EnsureChpIns();
|
|
chpOld = chpNew = _chpIns;
|
|
|
|
switch (pcmd->cid)
|
|
{
|
|
case cidPlain:
|
|
chpNew.grfont = fontNil;
|
|
chpOld.grfont = (ulong)~fontNil;
|
|
goto LApplyChp;
|
|
case cidBold:
|
|
chpNew.grfont ^= fontBold;
|
|
goto LApplyChp;
|
|
case cidItalic:
|
|
chpNew.grfont ^= fontItalic;
|
|
goto LApplyChp;
|
|
case cidUnderline:
|
|
chpNew.grfont ^= fontUnderline;
|
|
goto LApplyChp;
|
|
case cidChooseFont:
|
|
if (pvNil == pcmd->pgg || pcmd->pgg->IvMac() != 1 ||
|
|
!stn.FSetData(pcmd->pgg->QvGet(0), pcmd->pgg->Cb(0)))
|
|
{
|
|
break;
|
|
}
|
|
if (!vntl.FGetOnn(&stn, &onn))
|
|
break;
|
|
chpNew.onn = onn;
|
|
chpOld.onn = ~onn;
|
|
goto LApplyChp;
|
|
case cidChooseSubSuper:
|
|
// the amount to offset by is pcmd->rglw[0] ^ (1L << 31), so 0 can be
|
|
// used to indicate that we're to ask the user.
|
|
if (pcmd->rglw[0] == 0)
|
|
{
|
|
long dyp = _chpIns.dypOffset;
|
|
|
|
// ask the user for the amount to sub/superscript by
|
|
if (!_FGetOtherSubSuper(&dyp))
|
|
return fTrue;
|
|
pcmd->rglw[0] = dyp ^ (1L << 31);
|
|
}
|
|
chpNew.dypOffset = pcmd->rglw[0] ^ (1L << 31);
|
|
chpOld.dypOffset = ~chpNew.dypOffset;
|
|
goto LApplyChp;
|
|
case cidChooseFontSize:
|
|
if (pcmd->rglw[0] == 0)
|
|
{
|
|
long dyp = _chpIns.dypFont;
|
|
|
|
// ask the user for the font size
|
|
if (!_FGetOtherSize(&dyp))
|
|
return fTrue;
|
|
pcmd->rglw[0] = dyp;
|
|
}
|
|
if (!FIn(pcmd->rglw[0], 6, 256))
|
|
return fTrue;
|
|
chpNew.dypFont = pcmd->rglw[0];
|
|
chpOld.dypFont = ~pcmd->rglw[0];
|
|
LApplyChp:
|
|
FApplyChp(&chpNew, &chpOld);
|
|
break;
|
|
|
|
case cidJustifyLeft:
|
|
papNew.jc = jcLeft;
|
|
papOld.jc = jcLim;
|
|
goto LApplyPap;
|
|
case cidJustifyCenter:
|
|
papNew.jc = jcCenter;
|
|
papOld.jc = jcLim;
|
|
goto LApplyPap;
|
|
case cidJustifyRight:
|
|
papNew.jc = jcRight;
|
|
papOld.jc = jcLim;
|
|
goto LApplyPap;
|
|
case cidIndentNone:
|
|
papNew.nd = ndNone;
|
|
papOld.nd = ndLim;
|
|
goto LApplyPap;
|
|
case cidIndentFirst:
|
|
papNew.nd = ndFirst;
|
|
papOld.nd = ndLim;
|
|
goto LApplyPap;
|
|
case cidIndentRest:
|
|
papNew.nd = ndRest;
|
|
papOld.nd = ndLim;
|
|
goto LApplyPap;
|
|
case cidIndentAll:
|
|
papNew.nd = ndAll;
|
|
papOld.nd = ndLim;
|
|
LApplyPap:
|
|
FApplyPap(&papNew, &papOld);
|
|
break;
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get a font size from the user.
|
|
***************************************************************************/
|
|
bool TXRG::_FGetOtherSize(long *pdypFont)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pdypFont);
|
|
|
|
TrashVar(pdypFont);
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the amount to sub/superscript from the user.
|
|
***************************************************************************/
|
|
bool TXRG::_FGetOtherSubSuper(long *pdypOffset)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pdypOffset);
|
|
|
|
TrashVar(pdypOffset);
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Apply a character or paragraph property
|
|
***************************************************************************/
|
|
bool TXRG::FSetColor(ACR *pacrFore, ACR *pacrBack)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrPo(pacrFore, 0);
|
|
AssertNilOrPo(pacrBack, 0);
|
|
CHP chp, chpDiff;
|
|
|
|
chp.Clear();
|
|
chpDiff.Clear();
|
|
|
|
if (pvNil != pacrFore)
|
|
{
|
|
chp.acrFore = *pacrFore;
|
|
chpDiff.acrFore.SetToInvert();
|
|
if (chpDiff.acrFore == chp.acrFore)
|
|
chpDiff.acrFore.SetToClear();
|
|
}
|
|
if (pvNil != pacrBack)
|
|
{
|
|
chp.acrBack = *pacrBack;
|
|
chpDiff.acrBack.SetToInvert();
|
|
if (chpDiff. acrBack == chp.acrBack)
|
|
chpDiff.acrBack.SetToClear();
|
|
}
|
|
|
|
return FApplyChp(&chp, &chpDiff);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Enable, check/uncheck property commands.
|
|
***************************************************************************/
|
|
bool TXRG::FEnablePropCmd(PCMD pcmd, ulong *pgrfeds)
|
|
{
|
|
PAP pap;
|
|
bool fCheck;
|
|
STN stn, stnT;
|
|
|
|
_EnsureChpIns();
|
|
((PTXRD)_ptxtb)->FetchPap(LwMin(_cpAnchor, _cpOther), &pap);
|
|
|
|
switch (pcmd->cid)
|
|
{
|
|
case cidPlain:
|
|
fCheck = (_chpIns.grfont == fontNil);
|
|
break;
|
|
case cidBold:
|
|
fCheck = FPure(_chpIns.grfont & fontBold);
|
|
break;
|
|
case cidItalic:
|
|
fCheck = FPure(_chpIns.grfont & fontItalic);
|
|
break;
|
|
case cidUnderline:
|
|
fCheck = FPure(_chpIns.grfont & fontUnderline);
|
|
break;
|
|
|
|
case cidChooseFont:
|
|
if (pvNil == pcmd->pgg || pcmd->pgg->IvMac() != 1)
|
|
break;
|
|
if (!stnT.FSetData(pcmd->pgg->PvLock(0), pcmd->pgg->Cb(0)))
|
|
{
|
|
pcmd->pgg->Unlock();
|
|
break;
|
|
}
|
|
pcmd->pgg->Unlock();
|
|
vntl.GetStn(_chpIns.onn, &stn);
|
|
fCheck = stn.FEqual(&stnT);
|
|
break;
|
|
|
|
case cidChooseFontSize:
|
|
fCheck = (_chpIns.dypFont == pcmd->rglw[0]);
|
|
break;
|
|
case cidChooseSubSuper:
|
|
fCheck = (_chpIns.dypOffset == (pcmd->rglw[0] ^ (1L << 31)));
|
|
break;
|
|
|
|
case cidJustifyLeft:
|
|
fCheck = (pap.jc == jcLeft);
|
|
break;
|
|
case cidJustifyCenter:
|
|
fCheck = (pap.jc == jcCenter);
|
|
break;
|
|
case cidJustifyRight:
|
|
fCheck = (pap.jc == jcRight);
|
|
break;
|
|
case cidIndentNone:
|
|
fCheck = (pap.nd == ndNone);
|
|
break;
|
|
case cidIndentFirst:
|
|
fCheck = (pap.nd == ndFirst);
|
|
break;
|
|
case cidIndentRest:
|
|
fCheck = (pap.nd == ndRest);
|
|
break;
|
|
case cidIndentAll:
|
|
fCheck = (pap.nd == ndAll);
|
|
break;
|
|
}
|
|
*pgrfeds = fCheck ? fedsEnable | fedsCheck : fedsEnable | fedsUncheck;
|
|
|
|
return fTrue;
|
|
}
|
|
|