mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-22 10:22:40 +01:00
1270 lines
29 KiB
C++
1270 lines
29 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
|
|
Classes for the hex editor.
|
|
|
|
***************************************************************************/
|
|
#include "ched.h"
|
|
ASSERTNAME
|
|
|
|
|
|
#define kcbMaxLineDch 16
|
|
|
|
/***************************************************************************
|
|
A document class that holds a stream and is naturally displayed by the
|
|
hex editor (DCH). Used for the clipboard.
|
|
***************************************************************************/
|
|
typedef class DHEX *PDHEX;
|
|
#define DHEX_PAR DOCB
|
|
#define kclsDHEX 'DHEX'
|
|
class DHEX : public DHEX_PAR
|
|
{
|
|
RTCLASS_DEC
|
|
ASSERT
|
|
MARKMEM
|
|
|
|
protected:
|
|
BSF _bsf;
|
|
|
|
DHEX(PDOCB pdocb = pvNil, ulong grfdoc = fdocNil)
|
|
: DHEX_PAR(pdocb, grfdoc) {}
|
|
|
|
public:
|
|
static PDHEX PdhexNew(void);
|
|
|
|
PBSF Pbsf(void)
|
|
{ return &_bsf; }
|
|
|
|
virtual PDDG PddgNew(PGCB pgcb);
|
|
};
|
|
|
|
|
|
RTCLASS(DCH)
|
|
RTCLASS(DOCH)
|
|
RTCLASS(DHEX)
|
|
|
|
|
|
/***************************************************************************
|
|
Static method to create a new text stream document to be displayed
|
|
by the hex editor.
|
|
***************************************************************************/
|
|
PDHEX DHEX::PdhexNew(void)
|
|
{
|
|
PDHEX pdhex;
|
|
|
|
if (pvNil == (pdhex = NewObj DHEX()))
|
|
return pvNil;
|
|
|
|
return pdhex;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Create a new DCH displaying this stream.
|
|
***************************************************************************/
|
|
PDDG DHEX::PddgNew(PGCB pgcb)
|
|
{
|
|
return DCH::PdchNew(this, &_bsf, fFalse, pgcb);
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a DHEX.
|
|
***************************************************************************/
|
|
void DHEX::AssertValid(ulong grf)
|
|
{
|
|
DHEX_PAR::AssertValid(0);
|
|
AssertPo(&_bsf, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the DHEX.
|
|
***************************************************************************/
|
|
void DHEX::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
DHEX_PAR::MarkMem();
|
|
MarkMemObj(&_bsf);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for the DCH.
|
|
***************************************************************************/
|
|
DCH::DCH(PDOCB pdocb, PBSF pbsf, bool fFixed, PGCB pgcb) : DCLB(pdocb, pgcb)
|
|
{
|
|
_pbsf = pbsf;
|
|
_cbLine = kcbMaxLineDch;
|
|
_dypHeader = _dypLine + 1;
|
|
_fFixed = FPure(fFixed);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static method to create a new DCH.
|
|
***************************************************************************/
|
|
PDCH DCH::PdchNew(PDOCB pdocb, PBSF pbsf, bool fFixed, PGCB pgcb)
|
|
{
|
|
PDCH pdch;
|
|
|
|
if (pvNil == (pdch = NewObj DCH(pdocb, pbsf, fFixed, pgcb)))
|
|
return pvNil;
|
|
|
|
if (!pdch->_FInit())
|
|
{
|
|
ReleasePpo(&pdch);
|
|
return pvNil;
|
|
}
|
|
pdch->Activate(fTrue);
|
|
|
|
AssertPo(pdch, 0);
|
|
return pdch;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
We're being activated or deactivated, invert the sel.
|
|
***************************************************************************/
|
|
void DCH::_Activate(bool fActive)
|
|
{
|
|
AssertThis(0);
|
|
RC rc;
|
|
|
|
DDG::_Activate(fActive);
|
|
GetRc(&rc, cooLocal);
|
|
rc.ypBottom = _dypHeader;
|
|
InvalRc(&rc);
|
|
_SwitchSel(fActive);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draw the Hex doc in the port.
|
|
***************************************************************************/
|
|
void DCH::Draw(PGNV pgnv, RC *prcClip)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pgnv, 0);
|
|
AssertVarMem(prcClip);
|
|
STN stn;
|
|
byte rgb[kcbMaxLineDch];
|
|
RC rc, rcSrc;
|
|
long xp, yp, cb, ib, cbT, ibT;
|
|
byte bT;
|
|
|
|
pgnv->ClipRc(prcClip);
|
|
pgnv->GetRcSrc(&rcSrc);
|
|
pgnv->SetOnn(_onn);
|
|
|
|
if (prcClip->ypTop < _dypHeader)
|
|
_DrawHeader(pgnv);
|
|
|
|
Assert(_cbLine <= size(rgb), "lines too long");
|
|
cb = _pbsf->IbMac();
|
|
xp = _XpFromIch(0);
|
|
ib = LwMul(_cbLine, _LnFromYp(LwMax(_dypHeader, prcClip->ypTop)));
|
|
yp = _YpFromIb(ib);
|
|
rc.xpLeft = _XpFromCb(_cbLine, fFalse);
|
|
rc.xpRight = prcClip->xpRight;
|
|
rc.ypTop = yp;
|
|
rc.ypBottom = prcClip->ypBottom;
|
|
pgnv->FillRc(&rc, kacrWhite);
|
|
if (xp > 0)
|
|
{
|
|
//erase to the left of the text
|
|
rc.xpLeft = 0;
|
|
rc.xpRight = xp;
|
|
pgnv->FillRc(&rc, kacrWhite);
|
|
}
|
|
|
|
for ( ; ib < cb && yp < prcClip->ypBottom; ib += _cbLine)
|
|
{
|
|
cbT = LwMin(_cbLine, cb - ib);
|
|
_pbsf->FetchRgb(ib, cbT, rgb);
|
|
|
|
//first comes the address of the first byte of the line
|
|
stn.FFormatSz(PszLit("%08x "), ib);
|
|
|
|
//now add the line's bytes in hex, with a space after every
|
|
//four bytes
|
|
for (ibT = 0; ibT < cbT; ibT++)
|
|
{
|
|
bT = rgb[ibT];
|
|
if ((ibT & 0x03) == 0)
|
|
stn.FAppendCh(kchSpace);
|
|
stn.FAppendCh(vrgchHex[(bT >> 4) & 0x0F]);
|
|
stn.FAppendCh(vrgchHex[bT & 0x0F]);
|
|
}
|
|
//pad the line with spaces
|
|
if (ibT < _cbLine)
|
|
{
|
|
ibT = _cbLine - ibT;
|
|
ibT = 2 * ibT + ibT / 4;
|
|
while (ibT-- > 0)
|
|
stn.FAppendCh(kchSpace);
|
|
}
|
|
stn.FAppendSz(PszLit(" "));
|
|
|
|
//now comes the ascii characters.
|
|
for (ibT = 0; ibT < cbT; ibT++)
|
|
{
|
|
bT = rgb[ibT];
|
|
if (bT < 32 || bT == 0x7F)
|
|
bT = '?';
|
|
stn.FAppendCh((achar)bT);
|
|
}
|
|
//pad the line with spaces
|
|
while (ibT++ < _cbLine)
|
|
stn.FAppendCh(kchSpace);
|
|
|
|
pgnv->DrawStn(&stn, xp, yp, kacrBlack, kacrWhite);
|
|
yp += _dypLine;
|
|
}
|
|
if (yp < prcClip->ypBottom)
|
|
{
|
|
rc = rcSrc;
|
|
rc.ypTop = yp;
|
|
pgnv->FillRc(&rc, kacrWhite);
|
|
}
|
|
|
|
//draw the selection
|
|
if (_fSelOn)
|
|
_InvertSel(pgnv);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draw the header for the DCH.
|
|
***************************************************************************/
|
|
void DCH::_DrawHeader(PGNV pgnv)
|
|
{
|
|
STN stn;
|
|
RC rc, rcSrc;
|
|
|
|
pgnv->SetOnn(_onn);
|
|
pgnv->GetRcSrc(&rcSrc);
|
|
|
|
//erase the first part of the line
|
|
rc.xpLeft = 0;
|
|
rc.xpRight = _XpFromIch(0);
|
|
rc.ypTop = 0;
|
|
rc.ypBottom = _dypLine;
|
|
pgnv->FillRc(&rc, kacrWhite);
|
|
|
|
//draw the text
|
|
stn.FFormatSz(PszLit("%08x"), _pbsf->IbMac());
|
|
pgnv->DrawStn(&stn, rc.xpRight, 0, kacrBlack, kacrWhite);
|
|
|
|
//erase the rest of the line
|
|
rc.xpLeft = _XpFromIch(8);
|
|
rc.xpRight = rcSrc.xpRight;
|
|
pgnv->FillRc(&rc, kacrWhite);
|
|
|
|
//draw the _fHex Marker
|
|
rc.xpLeft = _XpFromCb(0, _fHexSel);
|
|
rc.xpRight = rc.xpLeft + rc.Dyp();
|
|
rc.Inset(rc.Dyp() / 6, rc.Dyp() / 6);
|
|
if (_fActive)
|
|
pgnv->FillRc(&rc, kacrBlack);
|
|
else
|
|
pgnv->FillRcApt(&rc, &vaptGray, kacrBlack, kacrWhite);
|
|
|
|
//draw the line seperating the header from the data
|
|
rc = rcSrc;
|
|
rc.ypTop = _dypHeader - 1;
|
|
rc.ypBottom = _dypHeader;
|
|
pgnv->FillRc(&rc, kacrBlack);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Handle key input.
|
|
***************************************************************************/
|
|
bool DCH::FCmdKey(PCMD_KEY pcmd)
|
|
{
|
|
AssertThis(0);
|
|
ulong grfcust;
|
|
long dibSel, dibDel, ibLim;
|
|
long cact;
|
|
CMD cmd;
|
|
byte rgb[64], bT;
|
|
bool fRight = fFalse;
|
|
|
|
// keep fetching characters until we get a cursor key, delete key or
|
|
// until the buffer is full.
|
|
dibSel = 0;
|
|
dibDel = 0;
|
|
ibLim = 0;
|
|
do
|
|
{
|
|
switch (pcmd->vk)
|
|
{
|
|
case kvkHome:
|
|
dibSel = -_pbsf->IbMac() - ibLim - 1;
|
|
break;
|
|
case kvkEnd:
|
|
dibSel = _pbsf->IbMac() + ibLim + 1;
|
|
break;
|
|
case kvkLeft:
|
|
dibSel = -1;
|
|
break;
|
|
case kvkRight:
|
|
dibSel = 1;
|
|
fRight = fTrue;
|
|
break;
|
|
case kvkUp:
|
|
dibSel = -_cbLine;
|
|
fRight = _fRightSel;
|
|
break;
|
|
case kvkDown:
|
|
dibSel = _cbLine;
|
|
fRight = _fRightSel;
|
|
break;
|
|
|
|
case kvkDelete:
|
|
if (!_fFixed)
|
|
dibDel = 1;
|
|
break;
|
|
case kvkBack:
|
|
if (!_fFixed)
|
|
dibDel = -1;
|
|
break;
|
|
|
|
default:
|
|
if (chNil == pcmd->ch)
|
|
break;
|
|
if (!_fHexSel)
|
|
bT = (byte)pcmd->ch;
|
|
else
|
|
{
|
|
//hex typing
|
|
if (FIn(pcmd->ch, '0', '9' + 1))
|
|
bT = pcmd->ch - '0';
|
|
else if (FIn(pcmd->ch, 'A', 'F' + 1))
|
|
bT = pcmd->ch - 'A' + 10;
|
|
else if (FIn(pcmd->ch, 'a', 'f' + 1))
|
|
bT = pcmd->ch - 'a' + 10;
|
|
else
|
|
break;
|
|
}
|
|
for (cact = 0; cact < pcmd->cact && ibLim < size(rgb); cact++)
|
|
rgb[ibLim++] = bT;
|
|
break;
|
|
}
|
|
|
|
grfcust = pcmd->grfcust;
|
|
pcmd = (PCMD_KEY)&cmd;
|
|
}
|
|
while (0 == dibSel && 0 == dibDel && ibLim < size(rgb) && vpcex->FGetNextKey(&cmd));
|
|
|
|
if (ibLim > 0)
|
|
{
|
|
//have some characters to insert
|
|
if (!_fHexSel)
|
|
{
|
|
//just straight characters to insert
|
|
_FReplace(rgb, ibLim, _ibAnchor, _ibOther);
|
|
}
|
|
else
|
|
{
|
|
//hex typing
|
|
byte bT;
|
|
long ibSrc, ibDst;
|
|
long ibAnchor = _ibAnchor;
|
|
|
|
if (_fHalfSel && ibAnchor > 0)
|
|
{
|
|
//complete the byte
|
|
_pbsf->FetchRgb(--ibAnchor, 1, &bT);
|
|
rgb[0] = (bT & 0xF0) | (rgb[0] & 0x0F);
|
|
ibSrc = 1;
|
|
}
|
|
else
|
|
ibSrc = 0;
|
|
|
|
for (ibDst = ibSrc; ibSrc + 1 < ibLim; ibSrc += 2)
|
|
rgb[ibDst++] = (rgb[ibSrc] << 4) | (rgb[ibSrc + 1] & 0x0F);
|
|
|
|
if (ibSrc < ibLim)
|
|
{
|
|
Assert(ibSrc + 1 == ibLim, 0);
|
|
rgb[ibDst++] = rgb[ibSrc] << 4;
|
|
}
|
|
|
|
_FReplace(rgb, ibDst, ibAnchor, _ibOther, ibSrc < ibLim);
|
|
}
|
|
}
|
|
|
|
if (dibSel != 0)
|
|
{
|
|
//move the selection
|
|
if (grfcust & fcustShift)
|
|
{
|
|
//extend selection
|
|
_SetSel(_ibAnchor, _ibOther + dibSel, fRight);
|
|
_ShowSel();
|
|
}
|
|
else
|
|
{
|
|
long ibOther = _ibOther;
|
|
long ibAnchor = _ibAnchor;
|
|
|
|
if (_fHalfSel)
|
|
{
|
|
if (dibSel < 0)
|
|
{
|
|
ibAnchor--;
|
|
fRight = fFalse;
|
|
}
|
|
else
|
|
fRight = fTrue;
|
|
}
|
|
else if (ibAnchor == ibOther)
|
|
ibAnchor += dibSel;
|
|
else if ((dibSel > 0) != (ibAnchor > ibOther))
|
|
{
|
|
ibAnchor = ibOther;
|
|
fRight = dibSel > 0;
|
|
}
|
|
_SetSel(ibAnchor, ibAnchor, fRight);
|
|
_ShowSel();
|
|
}
|
|
}
|
|
else if (dibDel != 0)
|
|
{
|
|
if (_ibAnchor != _ibOther)
|
|
dibDel = _ibOther - _ibAnchor;
|
|
else
|
|
dibDel = LwBound(_ibAnchor + dibDel, 0, _pbsf->IbMac() + 1) - _ibAnchor;
|
|
if (dibDel != 0)
|
|
_FReplace(pvNil, 0, _ibAnchor, _ibAnchor + dibDel);
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Replaces the bytes between ib1 and ib2 with the given bytes.
|
|
***************************************************************************/
|
|
bool DCH::_FReplace(byte *prgb, long cb, long ib1, long ib2, bool fHalfSel)
|
|
{
|
|
_SwitchSel(fFalse);
|
|
SortLw(&ib1, &ib2);
|
|
if (_fFixed)
|
|
{
|
|
cb = LwMin(cb, _pbsf->IbMac() - ib1);
|
|
ib2 = ib1 + cb;
|
|
}
|
|
if (!_pbsf->FReplace(prgb, cb, ib1, ib2 - ib1))
|
|
return fFalse;
|
|
|
|
_InvalAllDch(ib1, cb, ib2 - ib1);
|
|
ib1 += cb;
|
|
if (fHalfSel)
|
|
_SetHalfSel(ib1);
|
|
else
|
|
_SetSel(ib1, ib1, fFalse /*REVIEW shonk*/);
|
|
_ShowSel();
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Invalidate all DCHs on this byte stream. Also dirties the document.
|
|
Should be called by any code that edits the document.
|
|
***************************************************************************/
|
|
void DCH::_InvalAllDch(long ib, long cbIns, long cbDel)
|
|
{
|
|
AssertThis(0);
|
|
long ipddg;
|
|
PDDG pddg;
|
|
|
|
//mark the document dirty
|
|
_pdocb->SetDirty();
|
|
|
|
//inform the DCDs
|
|
for (ipddg = 0; pvNil != (pddg = _pdocb->PddgGet(ipddg)); ipddg++)
|
|
{
|
|
if (pddg->FIs(kclsDCH))
|
|
((PDCH)pddg)->_InvalIb(ib, cbIns, cbDel);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Invalidate the display from ib to the end of the display. If we're
|
|
the active DCH, also redraw.
|
|
***************************************************************************/
|
|
void DCH::_InvalIb(long ib, long cbIns, long cbDel)
|
|
{
|
|
AssertThis(0);
|
|
Assert(!_fSelOn, "why is the sel on during an invalidation?");
|
|
RC rc;
|
|
long ibAnchor, ibOther;
|
|
|
|
//adjust the sel
|
|
ibAnchor = _ibAnchor;
|
|
ibOther = _ibOther;
|
|
FAdjustIv(&ibAnchor, ib, cbIns, cbDel);
|
|
FAdjustIv(&ibOther, ib, cbIns, cbDel);
|
|
if (ibAnchor != _ibAnchor || ibOther != _ibOther)
|
|
_SetSel(ibAnchor, ibOther, _fRightSel);
|
|
|
|
//caclculate the invalid rectangle
|
|
GetRc(&rc, cooLocal);
|
|
rc.ypTop = _YpFromIb(ib);
|
|
if (cbIns == cbDel)
|
|
rc.ypBottom = rc.ypTop + _dypLine;
|
|
|
|
if (rc.FEmpty())
|
|
return;
|
|
|
|
if (_fActive)
|
|
{
|
|
ValidRc(&rc, kginDraw);
|
|
InvalRc(&rc, kginDraw);
|
|
}
|
|
else
|
|
InvalRc(&rc);
|
|
|
|
if (cbIns != cbDel)
|
|
{
|
|
//invalidate the length
|
|
GetRc(&rc, cooLocal);
|
|
rc.xpLeft = _XpFromIch(0);
|
|
rc.xpRight = _XpFromIch(8);
|
|
rc.ypTop = 0;
|
|
rc.ypBottom = _dypHeader;
|
|
InvalRc(&rc);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Turn the selection on or off.
|
|
***************************************************************************/
|
|
void DCH::_SwitchSel(bool fOn)
|
|
{
|
|
if (FPure(fOn) != FPure(_fSelOn))
|
|
{
|
|
GNV gnv(this);
|
|
_InvertSel(&gnv);
|
|
_fSelOn = FPure(fOn);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Make sure the ibOther of the selection is visible. If possible, show
|
|
both ends of the selection.
|
|
***************************************************************************/
|
|
void DCH::_ShowSel(void)
|
|
{
|
|
long ln, lnHope, cln, dscv;
|
|
RC rc;
|
|
|
|
//find the line we definitely need to show
|
|
ln = _ibOther / _cbLine;
|
|
if (_ibOther % _cbLine == 0 && ln > 0)
|
|
{
|
|
//may have to adjust ln down by one
|
|
if (_ibAnchor < _ibOther || _ibAnchor == _ibOther && _fRightSel)
|
|
ln--;
|
|
}
|
|
|
|
//find the other end of the selection - which we hope to be able to show
|
|
lnHope = _ibAnchor / _cbLine;
|
|
|
|
_GetContent(&rc);
|
|
cln = LwMax(1, rc.Dyp() / _dypLine);
|
|
if (LwAbs(ln - lnHope) >= cln)
|
|
lnHope = ln; //can't show both
|
|
|
|
if (FIn(ln, _scvVert, _scvVert + cln) &&
|
|
FIn(lnHope, _scvVert, _scvVert + cln))
|
|
{
|
|
//both are showing
|
|
return;
|
|
}
|
|
|
|
Assert(LwAbs(lnHope - ln) < cln, "ln and lnHope too far apart");
|
|
SortLw(&ln, &lnHope);
|
|
if (ln < _scvVert)
|
|
dscv = ln - _scvVert;
|
|
else
|
|
{
|
|
dscv = lnHope - _scvVert - cln + 1;
|
|
Assert(dscv > 0, "bad dscv (bad logic above)");
|
|
}
|
|
_Scroll(scaNil, scaToVal, 0, _scvVert + dscv);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Invert the selection. Doesn't touch _fSelOn.
|
|
***************************************************************************/
|
|
void DCH::_InvertSel(PGNV pgnv)
|
|
{
|
|
Assert(!_fFixed || _ibAnchor == _ibOther, "non-ins sel in fixed");
|
|
long cb;
|
|
RC rcClip;
|
|
RC rc, rcT;
|
|
|
|
_GetContent(&rcClip);
|
|
if (_fFixed && _pbsf->IbMac() > 0 && !_fHalfSel)
|
|
{
|
|
rc.xpLeft = _XpFromIb(_ibAnchor, fTrue);
|
|
rc.xpRight = rc.xpLeft + 2 * _dxpChar;
|
|
rc.ypTop = _YpFromIb(_ibAnchor);
|
|
rc.ypBottom = rc.ypTop + _dypLine;
|
|
if (rcT.FIntersect(&rc, &rcClip))
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
|
|
rc.xpLeft = _XpFromIb(_ibAnchor, fFalse);
|
|
rc.xpRight = rc.xpLeft + _dxpChar;
|
|
if (rcT.FIntersect(&rc, &rcClip))
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
}
|
|
else if (_ibAnchor == _ibOther)
|
|
{
|
|
//insertion or half sel
|
|
Assert(!_fHalfSel || _fRightSel, "_fHalfSel set but not _fRightSel");
|
|
cb = _ibAnchor % _cbLine;
|
|
if (_fRightSel && cb == 0 && _ibAnchor > 0)
|
|
{
|
|
rc.ypTop = _YpFromIb(_ibAnchor - 1);
|
|
cb = _cbLine;
|
|
}
|
|
else
|
|
rc.ypTop = _YpFromIb(_ibAnchor);
|
|
rc.ypBottom = rc.ypTop + _dypLine;
|
|
|
|
//do the hex sel
|
|
rc.xpLeft = _XpFromCb(cb, fTrue, _fRightSel);
|
|
if (_fHalfSel && _ibAnchor > 0)
|
|
{
|
|
rc.xpRight = rc.xpLeft;
|
|
rc.xpLeft -= _dxpChar;
|
|
if (rcT.FIntersect(&rc, &rcClip))
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
}
|
|
else
|
|
{
|
|
rc.xpRight = --rc.xpLeft + 2;
|
|
if (rcT.FIntersect(&rc, &rcClip))
|
|
pgnv->FillRc(&rcT, kacrInvert);
|
|
}
|
|
|
|
//do the ascii sel
|
|
rc.xpLeft = _XpFromCb(cb, fFalse) - 1;
|
|
rc.xpRight = rc.xpLeft + 2;
|
|
if (rcT.FIntersect(&rc, &rcClip))
|
|
pgnv->FillRc(&rcT, kacrInvert);
|
|
}
|
|
else
|
|
{
|
|
_InvertIbRange(pgnv, _ibAnchor, _ibOther, fTrue);
|
|
_InvertIbRange(pgnv, _ibAnchor, _ibOther, fFalse);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Inverts a range on screen. Does not mark insertion bars or half sels.
|
|
***************************************************************************/
|
|
void DCH::_InvertIbRange(PGNV pgnv, long ib1, long ib2, bool fHex)
|
|
{
|
|
long ibMin, ibMac;
|
|
long xp2, yp2;
|
|
RC rc, rcT, rcClip;
|
|
|
|
ibMin = _scvVert * _cbLine;
|
|
ibMac = _pbsf->IbMac();
|
|
ib1 = LwBound(ib1, ibMin, ibMac + 1);
|
|
ib2 = LwBound(ib2, ibMin, ibMac + 1);
|
|
|
|
if (ib1 == ib2)
|
|
return;
|
|
SortLw(&ib1, &ib2);
|
|
|
|
_GetContent(&rcClip);
|
|
rc.xpLeft = _XpFromIb(ib1, fHex);
|
|
rc.ypTop = _YpFromIb(ib1);
|
|
xp2 = _XpFromIb(ib2, fHex);
|
|
yp2 = _YpFromIb(ib2);
|
|
|
|
rc.ypBottom = rc.ypTop + _dypLine;
|
|
if (yp2 == rc.ypTop)
|
|
{
|
|
//only one line involved
|
|
rc.xpRight = xp2;
|
|
if (rcT.FIntersect(&rc, &rcClip))
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
return;
|
|
}
|
|
|
|
//invert the sel on the first line
|
|
rc.xpRight = _XpFromCb(_cbLine, fHex);
|
|
if (rcT.FIntersect(&rc, &rcClip))
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
|
|
//invert the main rectangular block
|
|
rc.xpLeft = _XpFromCb(0, fHex);
|
|
rc.ypTop += _dypLine;
|
|
rc.ypBottom = yp2;
|
|
if (rcT.FIntersect(&rc, &rcClip))
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
|
|
//invert the last line
|
|
rc.ypTop = yp2;
|
|
rc.ypBottom = yp2 + _dypLine;
|
|
rc.xpRight = xp2;
|
|
if (rcT.FIntersect(&rc, &rcClip))
|
|
pgnv->HiliteRc(&rcT, kacrWhite);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Select the second half of the byte before ib.
|
|
***************************************************************************/
|
|
void DCH::_SetHalfSel(long ib)
|
|
{
|
|
ib = LwBound(ib, 0, _pbsf->IbMac() + 1);
|
|
if (ib == 0)
|
|
{
|
|
_SetSel(ib, ib, fFalse);
|
|
return;
|
|
}
|
|
|
|
GNV gnv(this);
|
|
if (_fSelOn)
|
|
{
|
|
//turn off the sel
|
|
_InvertSel(&gnv);
|
|
_fSelOn = fFalse;
|
|
}
|
|
_ibAnchor = _ibOther = ib;
|
|
_fHalfSel = fTrue;
|
|
_fRightSel = fTrue;
|
|
if (_fActive)
|
|
{
|
|
_InvertSel(&gnv);
|
|
_fSelOn = fTrue;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the selection. fRight is ignored for non-insertion bar selections.
|
|
***************************************************************************/
|
|
void DCH::_SetSel(long ibAnchor, long ibOther, bool fRight)
|
|
{
|
|
long ibMac = _pbsf->IbMac();
|
|
GNV gnv(this);
|
|
|
|
if (_fFixed && ibMac > 0)
|
|
{
|
|
ibOther = ibAnchor = LwBound(ibOther, 0, ibMac);
|
|
fRight = fFalse;
|
|
}
|
|
else
|
|
{
|
|
ibAnchor = LwBound(ibAnchor, 0, ibMac + 1);
|
|
ibOther = LwBound(ibOther, 0, ibMac + 1);
|
|
if (ibAnchor == ibOther)
|
|
{
|
|
if (fRight && ibAnchor == 0)
|
|
fRight = fFalse;
|
|
else if (!fRight && ibAnchor == ibMac)
|
|
fRight = fTrue;
|
|
}
|
|
else
|
|
fRight = fFalse;
|
|
}
|
|
|
|
if (!_fHalfSel && ibAnchor == _ibAnchor && ibOther == _ibOther &&
|
|
FPure(fRight) == FPure(_fRightSel))
|
|
{
|
|
goto LDrawSel;
|
|
}
|
|
|
|
if (_fSelOn)
|
|
{
|
|
if (_ibAnchor != ibAnchor || _ibAnchor == _ibOther ||
|
|
ibAnchor == ibOther)
|
|
{
|
|
_InvertSel(&gnv);
|
|
_fSelOn = fFalse;
|
|
}
|
|
else
|
|
{
|
|
//they have the same anchor and neither is an insertion
|
|
_InvertIbRange(&gnv, _ibOther, ibOther, fTrue);
|
|
_InvertIbRange(&gnv, _ibOther, ibOther, fFalse);
|
|
}
|
|
}
|
|
|
|
_ibAnchor = ibAnchor;
|
|
_ibOther = ibOther;
|
|
_fRightSel = FPure(fRight);
|
|
_fHalfSel = fFalse;
|
|
|
|
LDrawSel:
|
|
if (!_fSelOn && _fActive)
|
|
{
|
|
_InvertSel(&gnv);
|
|
_fSelOn = fTrue;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Changes the selection type from hex to ascii or vice versa.
|
|
***************************************************************************/
|
|
void DCH::_SetHexSel(bool fHex)
|
|
{
|
|
if (FPure(fHex) == FPure(_fHexSel))
|
|
return;
|
|
_fHexSel = FPure(fHex);
|
|
|
|
GNV gnv(this);
|
|
_DrawHeader(&gnv);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the column for the given horizontal byte position. cb is the number
|
|
of bytes in from the left edge. fHex indicates whether we want the
|
|
position in the hex area or the ascii area. fNoTrailSpace is ignored if
|
|
fHex is false. If fHex is true, fNoTrailSpace indicates whether a
|
|
trailing space should be included (if the cb is divisible by 4).
|
|
***************************************************************************/
|
|
long DCH::_IchFromCb(long cb, bool fHex, bool fNoTrailSpace)
|
|
{
|
|
AssertIn(cb, 0, _cbLine + 1);
|
|
|
|
//skip over the address
|
|
long ich = 10;
|
|
|
|
if (fHex)
|
|
{
|
|
//account for the spaces every four hex digits
|
|
ich += 2 * cb + cb / 4;
|
|
if (fNoTrailSpace && (cb % 4) == 0 && cb > 0)
|
|
ich--;
|
|
}
|
|
else
|
|
{
|
|
//skip over the hex area
|
|
ich += 2 * _cbLine + _cbLine / 4 + 1 + cb;
|
|
}
|
|
return ich;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the xp for the given byte. fHex indicates whether we want the
|
|
postion in the hex area or the ascii area.
|
|
***************************************************************************/
|
|
long DCH::_XpFromIb(long ib, bool fHex)
|
|
{
|
|
return _XpFromIch(_IchFromCb(ib % _cbLine, fHex));
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the xp for the given horizontal byte position. cb is the number
|
|
of bytes in from the left edge. fHex indicates whether we want the
|
|
postion in the hex area or the ascii area.
|
|
***************************************************************************/
|
|
long DCH::_XpFromCb(long cb, bool fHex, bool fNoTrailSpace)
|
|
{
|
|
return _XpFromIch(_IchFromCb(cb, fHex, fNoTrailSpace));
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the yp for the given byte.
|
|
***************************************************************************/
|
|
long DCH::_YpFromIb(long ib)
|
|
{
|
|
AssertIn(ib, 0, kcbMax);
|
|
return LwMul((ib / _cbLine) - _scvVert, _dypLine) + _dypHeader;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Finds the byte that the given point is over. *ptHex is both input and
|
|
output. If *ptHex is tMaybe on input, it will be set to tYes or tNo on
|
|
output (unless the point is not in the edit area of the DCH). If *ptHex
|
|
is tYes or tNo on input, the ib is determined using *ptHex.
|
|
***************************************************************************/
|
|
long DCH::_IbFromPt(long xp, long yp, bool *ptHex, bool *pfRight)
|
|
{
|
|
AssertVarMem(ptHex);
|
|
AssertNilOrVarMem(pfRight);
|
|
|
|
RC rc;
|
|
long cbMin, cbLim, cb, ib;
|
|
long xpFind, xpT;
|
|
bool fHex;
|
|
|
|
_GetContent(&rc);
|
|
if (!rc.FPtIn(xp, yp))
|
|
return ivNil;
|
|
|
|
if (*ptHex == tMaybe)
|
|
{
|
|
xpT = (_XpFromCb(_cbLine, fTrue, fTrue) + _XpFromCb(0, fFalse)) / 2;
|
|
if (xp <= xpT)
|
|
*ptHex = tYes;
|
|
else
|
|
*ptHex = tNo;
|
|
}
|
|
|
|
xpFind = xp - _dxpChar;
|
|
if (*ptHex == tYes)
|
|
{
|
|
if (_fFixed)
|
|
xpFind -= _dxpChar;
|
|
fHex = fTrue;
|
|
}
|
|
else
|
|
{
|
|
if (!_fFixed)
|
|
xpFind = xp - _dxpChar / 2;
|
|
fHex = fFalse;
|
|
}
|
|
|
|
for (cbMin = 0, cbLim = _cbLine; cbMin < cbLim; )
|
|
{
|
|
cb = (cbMin + cbLim) / 2;
|
|
xpT = _XpFromCb(cb, fHex);
|
|
if (xpT < xpFind)
|
|
cbMin = cb + 1;
|
|
else
|
|
cbLim = cb;
|
|
}
|
|
if (_fFixed && cbMin == _cbLine)
|
|
cbMin--;
|
|
ib = cbMin + _cbLine * _LnFromYp(yp);
|
|
ib = LwMin(ib, _pbsf->IbMac());
|
|
if (pvNil != pfRight)
|
|
*pfRight = cbMin == _cbLine;
|
|
return ib;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Handle a mouse down in our content.
|
|
***************************************************************************/
|
|
void DCH::MouseDown(long xp, long yp, long cact, ulong grfcust)
|
|
{
|
|
AssertThis(0);
|
|
bool tHex;
|
|
bool fDown, fRight;
|
|
PT pt, ptT;
|
|
long ib;
|
|
RC rc;
|
|
|
|
//doing this before the activate avoids flashing the old selection
|
|
tHex = tMaybe;
|
|
ib = _IbFromPt(xp, yp, &tHex, &fRight);
|
|
if (ivNil != ib)
|
|
_SetSel((grfcust & fcustShift) ? _ibAnchor : ib, ib, fRight);
|
|
|
|
if (!_fActive)
|
|
Activate(fTrue);
|
|
|
|
if (ivNil == ib)
|
|
return;
|
|
|
|
_SetHexSel(tYes == tHex);
|
|
|
|
Clean();
|
|
_GetContent(&rc);
|
|
for (GetPtMouse(&pt, &fDown); fDown; GetPtMouse(&pt, &fDown))
|
|
{
|
|
if (!rc.FPtIn(pt.xp, pt.yp))
|
|
{
|
|
//do autoscroll
|
|
ptT = pt;
|
|
rc.PinPt(&pt);
|
|
_Scroll(scaToVal, scaToVal,
|
|
_scvHorz + LwDivAway(ptT.xp - pt.xp, _dxpChar),
|
|
_scvVert + LwDivAway(ptT.yp - pt.yp, _dypLine));
|
|
}
|
|
|
|
ib = _IbFromPt(pt.xp, pt.yp, &tHex, &fRight);
|
|
if (ivNil != ib)
|
|
_SetSel(_ibAnchor, ib, fRight);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the maximum for the indicated scroll bar.
|
|
***************************************************************************/
|
|
long DCH::_ScvMax(bool fVert)
|
|
{
|
|
RC rc;
|
|
|
|
_GetContent(&rc);
|
|
return LwMax(0, fVert ?
|
|
(_pbsf->IbMac() + _cbLine - 1) / _cbLine + 1 - rc.Dyp() / _dypLine :
|
|
_IchFromCb(_cbLine, fFalse) + 2 - rc.Dxp() / _dxpChar);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Copy the selection.
|
|
***************************************************************************/
|
|
bool DCH::_FCopySel(PDOCB *ppdocb)
|
|
{
|
|
PDHEX pdhex;
|
|
long ib1, ib2;
|
|
|
|
ib1 = _ibOther;
|
|
ib2 = _fFixed ? _pbsf->IbMac() : _ibAnchor;
|
|
if (_ibOther == ib2)
|
|
return fFalse;
|
|
|
|
if (pvNil == ppdocb)
|
|
return fTrue;
|
|
|
|
SortLw(&ib1, &ib2);
|
|
if (pvNil != (pdhex = DHEX::PdhexNew()))
|
|
{
|
|
if (!pdhex->Pbsf()->FReplaceBsf(_pbsf, ib1, ib2 - ib1, 0, 0))
|
|
ReleasePpo(&pdhex);
|
|
}
|
|
|
|
*ppdocb = pdhex;
|
|
return pvNil != *ppdocb;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Clear (delete) the selection.
|
|
***************************************************************************/
|
|
void DCH::_ClearSel(void)
|
|
{
|
|
_FReplace(pvNil, 0, _ibAnchor, _ibOther, fFalse);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Paste over the selection.
|
|
***************************************************************************/
|
|
bool DCH::_FPaste(PCLIP pclip, bool fDoIt, long cid)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pclip, 0);
|
|
long ib1, ib2, cb;
|
|
PDOCB pdocb;
|
|
PBSF pbsf;
|
|
|
|
if (cidPaste != cid)
|
|
return fFalse;
|
|
|
|
if (!pclip->FGetFormat(kclsDHEX) && !pclip->FGetFormat(kclsTXTB))
|
|
return fFalse;
|
|
|
|
if (!fDoIt)
|
|
return fTrue;
|
|
|
|
if (pclip->FGetFormat(kclsDHEX, &pdocb))
|
|
{
|
|
if (pvNil == (pbsf = ((PDHEX)pdocb)->Pbsf()) || 0 >= (cb = pbsf->IbMac()))
|
|
{
|
|
ReleasePpo(&pdocb);
|
|
return fFalse;
|
|
}
|
|
}
|
|
else if (pclip->FGetFormat(kclsTXTB, &pdocb))
|
|
{
|
|
if (pvNil == (pbsf = ((PTXTB)pdocb)->Pbsf()) ||
|
|
0 >= (cb = pbsf->IbMac() - size(achar)))
|
|
{
|
|
ReleasePpo(&pdocb);
|
|
return fFalse;
|
|
}
|
|
}
|
|
else
|
|
return fFalse;
|
|
|
|
ib1 = _ibAnchor;
|
|
ib2 = _ibOther;
|
|
_SwitchSel(fFalse);
|
|
SortLw(&ib1, &ib2);
|
|
if (_fFixed)
|
|
{
|
|
cb = LwMin(cb, _pbsf->IbMac() - ib1);
|
|
ib2 = ib1 + cb;
|
|
}
|
|
if (!_pbsf->FReplaceBsf(pbsf, 0, cb, ib1, ib2 - ib1))
|
|
{
|
|
ReleasePpo(&pdocb);
|
|
return fFalse;
|
|
}
|
|
|
|
_InvalAllDch(ib1, cb, ib2 - ib1);
|
|
ib1 += cb;
|
|
_SetSel(ib1, ib1, fFalse /*REVIEW shonk*/);
|
|
_ShowSel();
|
|
|
|
ReleasePpo(&pdocb);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of an object.
|
|
***************************************************************************/
|
|
void DCH::AssertValid(ulong grf)
|
|
{
|
|
DCH_PAR::AssertValid(0);
|
|
AssertPo(_pbsf, 0);
|
|
AssertIn(_cbLine, 1, kcbMaxLineDch + 1);
|
|
AssertIn(_ibAnchor, 0, kcbMax);
|
|
AssertIn(_ibOther, 0, kcbMax);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the DCH.
|
|
***************************************************************************/
|
|
void DCH::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
DCH_PAR::MarkMem();
|
|
MarkMemObj(_pbsf);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for a chunk hex editing doc.
|
|
***************************************************************************/
|
|
DOCH::DOCH(PDOCB pdocb, PCFL pcfl, CTG ctg, CNO cno)
|
|
: DOCE(pdocb, pcfl, ctg, cno)
|
|
{
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Creates a new hex editing doc based on the given chunk. Asserts that
|
|
there are no open editing docs based on the chunk.
|
|
***************************************************************************/
|
|
PDOCH DOCH::PdochNew(PDOCB pdocb, PCFL pcfl, CTG ctg, CNO cno)
|
|
{
|
|
AssertPo(pdocb, 0);
|
|
AssertPo(pcfl, 0);
|
|
|
|
Assert(pvNil == DOCE::PdoceFromChunk(pdocb, pcfl, ctg, cno),
|
|
"DOCE already exists for the chunk");
|
|
PDOCH pdoch;
|
|
|
|
if (pvNil == (pdoch = NewObj DOCH(pdocb, pcfl, ctg, cno)))
|
|
return pvNil;
|
|
if (!pdoch->_FInit())
|
|
{
|
|
ReleasePpo(&pdoch);
|
|
return pvNil;
|
|
}
|
|
AssertPo(pdoch, 0);
|
|
return pdoch;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize the stream from the given flo.
|
|
***************************************************************************/
|
|
bool DOCH::_FRead(PBLCK pblck)
|
|
{
|
|
FLO flo;
|
|
bool fRet;
|
|
|
|
if (!pblck->FUnpackData())
|
|
return fFalse;
|
|
|
|
if (pvNil == (flo.pfil = FIL::PfilCreateTemp()))
|
|
return fFalse;
|
|
flo.fp = 0;
|
|
flo.cb = pblck->Cb();
|
|
|
|
if (!pblck->FWriteToFlo(&flo))
|
|
{
|
|
ReleasePpo(&flo.pfil);
|
|
return fFalse;
|
|
}
|
|
fRet = _bsf.FReplaceFlo(&flo, fFalse, 0, _bsf.IbMac());
|
|
ReleasePpo(&flo.pfil);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Create a new DDG for the doc.
|
|
***************************************************************************/
|
|
PDDG DOCH::PddgNew(PGCB pgcb)
|
|
{
|
|
AssertThis(0);
|
|
return DCH::PdchNew(this, &_bsf, fFalse, pgcb);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Returns the length of the data on file
|
|
***************************************************************************/
|
|
long DOCH::_CbOnFile(void)
|
|
{
|
|
AssertThis(0);
|
|
return _bsf.IbMac();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Writes the data and returns success/failure.
|
|
***************************************************************************/
|
|
bool DOCH::_FWrite(PBLCK pblck, bool fRedirect)
|
|
{
|
|
AssertThis(0);
|
|
if (!_bsf.FWriteRgb(pblck))
|
|
return fFalse;
|
|
_FRead(pblck);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of an object.
|
|
***************************************************************************/
|
|
void DOCH::AssertValid(ulong grf)
|
|
{
|
|
DOCH_PAR::AssertValid(0);
|
|
AssertPo(&_bsf, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory used by the DOCH.
|
|
***************************************************************************/
|
|
void DOCH::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
DOCH_PAR::MarkMem();
|
|
MarkMemObj(&_bsf);
|
|
}
|
|
#endif //DEBUG
|
|
|