Microsoft-3D-Movie-Maker/kauai/TOOLS/CHHEX.CPP
2022-05-03 16:31:19 -07:00

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