用于EagleEye3.0 规则集漏报和误报测试的示例项目,项目收集于github和gitee
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1874 lines
50 KiB

5 months ago
/*
* 2022.1.29 by С<EFBFBD><EFBFBD>
*/
#include "stdafx.h"
#include "SHexEdit.h"
namespace SOUI
{
class SHexEditAction {
public:
enum ActionType { unknown, input, cut, paste } type;
protected:
UINT position;
SByteArray replaceData;
SByteArray insertData;
SHexEditAction* next;
public:
SHexEditAction();
~SHexEditAction();
BOOL set(ActionType type, UINT position, const SByteArray& replaceData, const SByteArray& insertData, SHexEditAction* next = 0);
void setNext(SHexEditAction* next) { this->next = next; }
BOOL append(const SByteArray& replaceData, const SByteArray& insertData);
ActionType getType() const { return type; }
UINT getPosition() const { return position; }
const SByteArray& getReplaceData() const { return replaceData; }
UINT getReplaceLen() const { return replaceData.Size(); }
const SByteArray& getInsertData() const { return insertData; }
UINT getInsertLen() const { return insertData.Size(); }
SHexEditAction* getNext() const { return next; }
};
SHexEditAction::SHexEditAction()
: type(unknown), position(0), next(0)
{
}
SHexEditAction::~SHexEditAction()
{
delete next;
}
BOOL SHexEditAction::set(ActionType type, UINT position, const SByteArray& replaceData, const SByteArray& insertData, SHexEditAction* next)
{
this->type = type;
this->position = position;
this->replaceData = replaceData;
this->insertData = insertData;
this->next = next;
return TRUE;
}
BOOL SHexEditAction::append(const SByteArray& replaceData, const SByteArray& insertData)
{
this->replaceData.Append(replaceData);
this->insertData.Append(insertData);
return TRUE;
}
//=============================================================================
#define NOSECTION_VAL 0xffffffff
// data length that can be handled
#define MAXHEXEDITLENGTH 0x7ffff000
// increase capacity by 1 + m_nCapacity/CAPACICTYINCDIVISOR when a character is appended/inserted
#define CAPACICTYINCDIVISOR 8
// control-layout customization (low-level)
#define ADR_DATA_SPACE 15
#define DATA_ASCII_SPACE 15
#define CONTROL_BORDER_SPACEH 0
#define CONTROL_BORDER_SPACEV 0
// boundaries and special values
#define MOUSEREP_TIMER_TID 0x400
#define MOUSEREP_TIMER_ELAPSE 0x5
// macros
#define NORMALIZE_SELECTION(beg, end) if(beg>end){UINT tmp = end; end=beg; beg=tmp; }
const TCHAR tabHexCharacters[16] = {
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
SHexEdit::SHexEdit() :
m_nAdrSize(8),
m_nBytesPerRow(16),
m_bHasCaret(false),
m_bHighBits(true),
m_bCaretAscii(false),
m_bReadOnly(false),
m_overwriteMode(false),
m_nHighlightedBegin(NOSECTION_VAL),
m_nHighlightedEnd(NOSECTION_VAL),
m_nSelectionBegin(NOSECTION_VAL),
m_nSelectionEnd(NOSECTION_VAL),
m_nCurrentAddress(0),
m_bAutoBytesPerRow(false),
m_bRecalc(true),
m_nScrollPostionX(0),
m_nScrollRangeX(0),
m_nScrollPostionY(0),
m_nScrollRangeY(0),
m_bShowAscii(true),
m_bShowAddress(true),
m_nMouseRepSpeed(0),
m_iMouseRepDelta(0),
m_nMouseRepCounter(0),
m_bIsMouseRepActive(false),
m_cDragRect(0, 0, 0, 0),
m_undo(0),
m_redo(0),
m_dwZeroAddr(0)
{
m_bFocusable = TRUE;
m_tPaintDetails.nCharacterWidth = 0;
m_bCaretAtLineEnd = false;
m_tAdrTxtCol = RGBA(0x00, 0x00, 0xBF, 0xFF);
m_tAdrBkgCol = RGBA(0xFF, 0xF8, 0xF0, 0xFF);
m_tHexBkgCol = m_tAdrBkgCol;
m_tAsciiBkgCol = m_tAdrBkgCol;
m_tHexTxtCol = RGBA(0x00, 0x00, 0x00, 0xFF);
m_tAsciiTxtCol = RGBA(0x00, 0x00, 0x00, 0xFF);
m_tSelectedNoFocusTxtCol = RGBA(114, 114, 114, 255);
m_tSelectedNoFocusBkgCol = RGBA(194, 222, 244, 255);
m_tSelectedFousTxtCol = GetSysColor(COLOR_HIGHLIGHTTEXT) | 0xff000000;
m_tSelectedFousBkgCol = GetSysColor(COLOR_HIGHLIGHT) | 0xff000000;
m_pFont = SFontPool::getSingleton().GetFont(L"face:consolas", 1);
GetEventSet()->addEvent(EVENTID(EventHexEditDataChanged));
GetEventSet()->addEvent(EVENTID(EventHexEditSelChanged));
}
SHexEdit::~SHexEdit()
{
delete m_undo;
delete m_redo;
}
void SHexEdit::OnDestroy()
{
__super::OnDestroy();
}
void SHexEdit::OnSize(UINT nType, CSize size)
{
__super::OnSize(nType, size);
m_bRecalc = true;
SetScrollbarRanges();
}
void SHexEdit::ReInitialize()
{
m_nSelectingBeg = NOSECTION_VAL;
m_nSelectingEnd = NOSECTION_VAL;
m_nSelectionBegin = NOSECTION_VAL;
m_nSelectionEnd = NOSECTION_VAL;
m_nHighlightedBegin = NOSECTION_VAL;
m_nHighlightedEnd = NOSECTION_VAL;
m_xData.Clear();
delete m_undo;
delete m_redo;
m_undo = 0;
m_redo = 0;
m_bRecalc = true;
}
void SHexEdit::SetData(const SByteArray& data)
{
ReInitialize();
m_xData = data;
m_bRecalc = true;
SetEditCaretPos(0, true);
Invalidate();
EventHexEditDataChanged evt(this);
FireEvent(evt);
}
void SHexEdit::SetData(const BYTE* data, UINT len)
{
ReInitialize();
if (data && len > 0)
m_xData.Append(data, len);
m_bRecalc = true;
SetEditCaretPos(0, true);
Invalidate();
EventHexEditDataChanged evt(this);
FireEvent(evt);
}
bool SHexEdit::IsSelection() const
{
return (m_nSelectionEnd != NOSECTION_VAL) && (m_nSelectionBegin != NOSECTION_VAL);
}
bool SHexEdit::IsInsert() const
{
return !m_overwriteMode;
}
bool SHexEdit::GetSelection(UINT& nBegin, UINT& nEnd) const
{
if (IsSelection()) {
nBegin = m_nSelectionBegin;
nEnd = m_nSelectionEnd;
return true;
}
nBegin = NOSECTION_VAL;
nEnd = NOSECTION_VAL;
return false;
}
void SHexEdit::SetBytesPerRow(UINT nBytesPerRow, bool bAuto, bool bUpdate)
{
if ((m_bAutoBytesPerRow != bAuto) || (m_nBytesPerRow != nBytesPerRow)) {
m_bAutoBytesPerRow = bAuto;
m_nBytesPerRow = nBytesPerRow;
m_bRecalc = true;
SetEditCaretPos(m_nCurrentAddress, m_bHighBits);
if (bUpdate) {
Invalidate();
}
}
}
void SHexEdit::SetShowAddress(bool bShow, bool bUpdate)
{
if (m_bShowAddress != bShow) {
m_bShowAddress = bShow;
m_bRecalc = true;
SetEditCaretPos(m_nCurrentAddress, m_bHighBits);
if (bUpdate) {
Invalidate();
}
}
}
void SHexEdit::SetShowAscii(bool bShow, bool bUpdate)
{
if (m_bShowAscii != bShow) {
m_bShowAscii = bShow;
m_bRecalc = true;
SetEditCaretPos(m_nCurrentAddress, m_bHighBits);
if (bUpdate) {
Invalidate();
}
}
}
void SHexEdit::SetSelection(UINT nBegin, UINT nEnd, bool bMakeVisible, bool bUpdate)
{
if ((m_nSelectionEnd != nEnd) || (m_nSelectionBegin != nBegin)) {
if ((nEnd >= GetDataSize()) && (nEnd != NOSECTION_VAL)) {
nEnd = GetDataSize() - 1;
}
if ((nBegin >= GetDataSize()) && (nBegin != NOSECTION_VAL)) {
nBegin = GetDataSize() - 1;
}
m_nSelectionEnd = nEnd;
m_nSelectionBegin = nBegin;
if (bMakeVisible && nEnd != NOSECTION_VAL && nBegin != NOSECTION_VAL) {
MakeVisible(m_nSelectionBegin, m_nSelectionEnd, false);
}
if (bUpdate) {
Invalidate();
}
}
}
void SHexEdit::SetReadonly(bool bReadOnly, bool bUpdate)
{
m_bReadOnly = bReadOnly;
if (bUpdate) {
Invalidate();
}
}
void SHexEdit::SetAddressSize(BYTE nAdrSize, bool bUpdate)
{
m_nAdrSize = nAdrSize;
m_bRecalc = true;
SetEditCaretPos(m_nCurrentAddress, m_bHighBits);
if (bUpdate) {
Invalidate();
}
}
void SHexEdit::SetAdrCol(COLORREF tAdrBkgCol, COLORREF tAdrTxtCol, bool bUpdate)
{
m_tAdrBkgCol = tAdrBkgCol;
m_tAdrTxtCol = tAdrTxtCol;
if (bUpdate) {
Invalidate();
}
}
void SHexEdit::SetAsciiCol(COLORREF tAsciiBkgCol, COLORREF tAsciiTxtCol, bool bUpdate)
{
m_tAsciiBkgCol = tAsciiBkgCol;
m_tAsciiTxtCol = tAsciiTxtCol;
if (bUpdate) {
Invalidate();
}
}
void SHexEdit::SetHexCol(COLORREF tHexBkgCol, COLORREF tHexTxtCol, bool bUpdate)
{
m_tHexBkgCol = tHexBkgCol;
m_tHexTxtCol = tHexTxtCol;
if (bUpdate) {
Invalidate();
}
}
void SHexEdit::SetSelectedNoFocusCol(COLORREF tSelectedNoFocusBkgCol, COLORREF tSelectedNoFocusTxtCol, bool bUpdate)
{
m_tSelectedNoFocusBkgCol = tSelectedNoFocusBkgCol;
m_tSelectedNoFocusTxtCol = tSelectedNoFocusTxtCol;
if (bUpdate) {
Invalidate();
}
}
void SHexEdit::SetSelectedFocusCol(COLORREF tSelectedFousTxtCol, COLORREF tSelectedFousBkgCol, bool bUpdate)
{
m_tSelectedFousTxtCol = tSelectedFousTxtCol;
m_tSelectedFousBkgCol = tSelectedFousBkgCol;
if (bUpdate) {
Invalidate();
}
}
void SHexEdit::OnSetFocus(SWND wndOld)
{
SetEditCaretPos(m_nCurrentAddress, m_bHighBits);
__super::OnSetFocus(wndOld);
}
void SHexEdit::OnKillFocus(SWND wndFocus)
{
DestoyEditCaret();
__super::OnKillFocus(wndFocus);
}
void SHexEdit::OnPaint(IRenderTarget* pRT)
{
SPainter painter;
BeforePaint(pRT, painter);
pRT->SelectObject(m_pFont);
if (m_bRecalc)
CalculatePaintingDetails(pRT);
pRT->FillSolidRect(m_tPaintDetails.cPaintingRect, m_tAdrBkgCol);
if (m_bShowAddress)
PaintAddresses(pRT);
PaintHexData(pRT);
if (m_bShowAscii)
PaintAsciiData(pRT);
AfterPaint(pRT, painter);
}
void SHexEdit::SetScrollbarRanges()
{
SCROLLINFO tScrollInfo;
memset(&tScrollInfo, 0, sizeof(SCROLLINFO));
tScrollInfo.cbSize = sizeof(SCROLLINFO);
if (m_nScrollRangeY > 0) {
ShowScrollBar(SSB_VERT, TRUE);
EnableScrollBar(SSB_VERT, TRUE);
tScrollInfo.fMask = SIF_ALL;
tScrollInfo.nPage = m_tPaintDetails.nFullVisibleLines;
tScrollInfo.nMax = m_nScrollRangeY + tScrollInfo.nPage - 1;
if (m_nScrollPostionY > m_nScrollRangeY) {
m_nScrollPostionY = m_nScrollRangeY;
}
tScrollInfo.nPos = m_nScrollPostionY;
SetScrollInfo(tScrollInfo, TRUE);
SetScrollPos(SSB_VERT, m_nScrollPostionY, TRUE);
}
else {
ShowScrollBar(SSB_VERT, FALSE);
}
if (m_nScrollRangeX > 0) {
EnableScrollBar(SSB_HORZ, TRUE);
ShowScrollBar(SSB_HORZ, TRUE);
tScrollInfo.fMask = SIF_ALL;
tScrollInfo.nPage = m_tPaintDetails.cPaintingRect.Width();
tScrollInfo.nMax = m_nScrollRangeX + tScrollInfo.nPage - 1;
if (m_nScrollPostionX > m_nScrollRangeX) {
m_nScrollPostionX = m_nScrollRangeX;
}
tScrollInfo.nPos = m_nScrollPostionX;
SetScrollInfo(tScrollInfo, FALSE);
SetScrollPos(SSB_HORZ, m_nScrollPostionX, TRUE);
}
else {
ShowScrollBar(SSB_HORZ, FALSE);
}
}
void SHexEdit::CalculatePaintingDetails(IRenderTarget* pRT)
{
m_bRecalc = false;
// Get size information
if (m_tPaintDetails.nCharacterWidth == 0)
{
SIZE sz;
pRT->MeasureText(L"D", 1, &sz);
m_tPaintDetails.nCharacterWidth = sz.cx;
m_tPaintDetails.nLineHeight = sz.cy;
}
// count of visible lines
GetClientRect(m_tPaintDetails.cPaintingRect);
if (true) {
m_tPaintDetails.cPaintingRect.InflateRect(-CONTROL_BORDER_SPACEH, -CONTROL_BORDER_SPACEV,
-CONTROL_BORDER_SPACEH, -CONTROL_BORDER_SPACEV);
if (m_tPaintDetails.cPaintingRect.right < m_tPaintDetails.cPaintingRect.left) {
m_tPaintDetails.cPaintingRect.right = m_tPaintDetails.cPaintingRect.left;
}
if (m_tPaintDetails.cPaintingRect.bottom < m_tPaintDetails.cPaintingRect.top) {
m_tPaintDetails.cPaintingRect.bottom = m_tPaintDetails.cPaintingRect.top;
}
}
m_tPaintDetails.nVisibleLines = m_tPaintDetails.cPaintingRect.Height() / m_tPaintDetails.nLineHeight;
m_tPaintDetails.nLastLineHeight = m_tPaintDetails.cPaintingRect.Height() % m_tPaintDetails.nLineHeight;
if (m_tPaintDetails.nLastLineHeight > 0) {
m_tPaintDetails.nFullVisibleLines = m_tPaintDetails.nVisibleLines;
m_tPaintDetails.nVisibleLines++;
}
else {
m_tPaintDetails.nFullVisibleLines = m_tPaintDetails.nVisibleLines;
m_tPaintDetails.nLastLineHeight = m_tPaintDetails.nLineHeight;
}
// position & size of the address
if (m_bShowAddress) {
m_tPaintDetails.nAddressPos = 0;
m_tPaintDetails.nAddressLen = ADR_DATA_SPACE + m_tPaintDetails.nCharacterWidth * m_nAdrSize;
}
else {
m_tPaintDetails.nAddressPos = 0;
m_tPaintDetails.nAddressLen = 0;
}
// Calculate how many bytes per line we can display, when this is automatically calculated
if (m_bAutoBytesPerRow) {
int iFreeSpace = m_tPaintDetails.cPaintingRect.Width() - m_tPaintDetails.nAddressLen;
if (m_bShowAscii) {
iFreeSpace -= DATA_ASCII_SPACE;
if (iFreeSpace < 0) {
m_tPaintDetails.nBytesPerRow = 1;
}
else {
m_tPaintDetails.nBytesPerRow = iFreeSpace / (4 * m_tPaintDetails.nCharacterWidth); // 2(HEXDATA)+1(Space)+1(Ascii) = 4
if ((iFreeSpace % (4 * m_tPaintDetails.nCharacterWidth)) >= (3 * m_tPaintDetails.nCharacterWidth)) {
m_tPaintDetails.nBytesPerRow++; // we actually only need n-1 spaces not n (n = nBytesPerRow)
}
}
}
else {
if (iFreeSpace < 0) {
m_tPaintDetails.nBytesPerRow = 1;
}
else {
m_tPaintDetails.nBytesPerRow = iFreeSpace / (3 * m_tPaintDetails.nCharacterWidth); // 2(HEXDATA)+1(Space) = 3
if ((iFreeSpace % (3 * m_tPaintDetails.nCharacterWidth)) >= (2 * m_tPaintDetails.nCharacterWidth)) {
m_tPaintDetails.nBytesPerRow++; // we actually only need n-1 spaces not n (n = nBytesPerRow)
}
}
}
//remark: m_nBytesPerRow=0 is a valid thing... (not very lucky thing, but valid)
}
else {
m_tPaintDetails.nBytesPerRow = m_nBytesPerRow;
}
if (m_tPaintDetails.nBytesPerRow == 0) {
m_tPaintDetails.nBytesPerRow = 1;
}
// position & size of the hex-data
m_tPaintDetails.nHexPos = m_tPaintDetails.nAddressPos + m_tPaintDetails.nAddressLen;
m_tPaintDetails.nHexLen = (m_tPaintDetails.nBytesPerRow * 2 + m_tPaintDetails.nBytesPerRow - 1) * m_tPaintDetails.nCharacterWidth;
//2(HEXData) + 1(Space) (only n-1 spaces needed)
int iWidth = m_tPaintDetails.nHexPos + m_tPaintDetails.nHexLen;
m_tPaintDetails.nHexLen += DATA_ASCII_SPACE;
// position & size of the ascii-data
if (m_bShowAscii) {
m_tPaintDetails.nAsciiPos = m_tPaintDetails.nHexPos + m_tPaintDetails.nHexLen;
m_tPaintDetails.nAsciiLen = m_tPaintDetails.nBytesPerRow * m_tPaintDetails.nCharacterWidth;
iWidth = m_tPaintDetails.nAsciiPos + m_tPaintDetails.nAsciiLen;
iWidth++;
}
else {
m_tPaintDetails.nAsciiPos = 0;
m_tPaintDetails.nAsciiLen = 0;
}
// calculate scrollranges
// Y-Bar
UINT nTotalLines;
nTotalLines = (GetDataSize() + m_tPaintDetails.nBytesPerRow - 1) / m_tPaintDetails.nBytesPerRow;
if (nTotalLines > m_tPaintDetails.nFullVisibleLines) {
m_nScrollRangeY = nTotalLines - m_tPaintDetails.nFullVisibleLines;
}
else {
m_nScrollRangeY = 0;
}
if (m_nScrollPostionY > m_nScrollRangeY) {
m_nScrollPostionY = m_nScrollRangeY;
}
// X-Bar
if (iWidth > m_tPaintDetails.cPaintingRect.Width()) {
m_nScrollRangeX = iWidth - m_tPaintDetails.cPaintingRect.Width();
}
else {
m_nScrollRangeX = 0;
}
if (m_nScrollPostionX > m_nScrollRangeX) {
m_nScrollPostionX = m_nScrollRangeX;
}
SetScrollbarRanges();
}
void SHexEdit::PaintAddresses(IRenderTarget* pRT)
{
UINT nAdr;
UINT nEndAdr;
SStringT cAdrFormatString;
CRect cAdrRect(m_tPaintDetails.cPaintingRect);
_TCHAR pBuf[32];
// create the format string
cAdrFormatString.Format(_T("%%0%uX"), m_nAdrSize);
// the Rect for painting & background
cAdrRect.left += m_tPaintDetails.nAddressPos - m_nScrollPostionX;
cAdrRect.right = cAdrRect.left + m_tPaintDetails.nAddressLen - ADR_DATA_SPACE; // without border
pRT->FillSolidRect(cAdrRect, m_tAdrBkgCol);
cAdrRect.bottom = cAdrRect.top + m_tPaintDetails.nLineHeight;
// start & end-address
nAdr = m_nScrollPostionY * m_tPaintDetails.nBytesPerRow;
nEndAdr = nAdr + m_tPaintDetails.nVisibleLines * m_tPaintDetails.nBytesPerRow;
if (nEndAdr >= GetDataSize()) {
nEndAdr = GetDataSize();
}
// paint
pRT->SetTextColor(m_tAdrTxtCol);
for (; nAdr < nEndAdr; nAdr += m_tPaintDetails.nBytesPerRow) {
_sntprintf(pBuf, 32, (LPCTSTR)cAdrFormatString, nAdr + m_dwZeroAddr); // slightly faster then CString::Format
pRT->DrawText(pBuf, -1, (LPRECT)cAdrRect, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX);
cAdrRect.OffsetRect(0, m_tPaintDetails.nLineHeight);
}
}
void SHexEdit::PaintHexData(IRenderTarget* pRT)
{
if (GetDataSize() == 0) {
return;
}
UINT nAdr;
UINT nEndAdr;
UINT nSelectionCount = 0;
TCHAR pBuf[1024];
TCHAR* pBufPtr;
TCHAR* pSelectionBufPtrBegin;
TCHAR* pSelectionBufPtrEnd;
BYTE* pDataPtr;
BYTE* pEndDataPtr;
BYTE* pEndLineDataPtr;
BYTE* pSelectionPtrBegin;
BYTE* pSelectionPtrEnd;
CRect cHexRect(m_tPaintDetails.cPaintingRect);
// prepare the buffer for the formated hex-data
memset(pBuf, 0, m_tPaintDetails.nBytesPerRow * 3); // fill with spaces
pBuf[m_tPaintDetails.nBytesPerRow * 3 - 1] = '\0'; // zero-terminate
// the Rect for painting & background
cHexRect.left += m_tPaintDetails.nHexPos - m_nScrollPostionX;
cHexRect.right = cHexRect.left + m_tPaintDetails.nHexLen - DATA_ASCII_SPACE;
pRT->FillSolidRect(cHexRect, m_tHexBkgCol);
cHexRect.bottom = cHexRect.top + m_tPaintDetails.nLineHeight;
// selection (pointers)
if ((m_nSelectionBegin != NOSECTION_VAL) && (m_nSelectionEnd != NOSECTION_VAL)) {
pSelectionPtrBegin = m_xData.GetData(m_nSelectionBegin);
pSelectionPtrEnd = m_xData.GetData(m_nSelectionEnd);
}
else {
pSelectionPtrBegin = NULL;
pSelectionPtrEnd = NULL;
}
// start & end-address (& pointers)
nAdr = m_nScrollPostionY * m_tPaintDetails.nBytesPerRow;
nEndAdr = nAdr + m_tPaintDetails.nVisibleLines * m_tPaintDetails.nBytesPerRow;
if (nEndAdr >= GetDataSize()) {
nEndAdr = GetDataSize() - 1;
}
pDataPtr = m_xData.GetData(nAdr);
pEndDataPtr = m_xData.GetData(nEndAdr);
// paint
while (pDataPtr < pEndDataPtr + 1) {
pEndLineDataPtr = pDataPtr + m_tPaintDetails.nBytesPerRow;
if (pEndLineDataPtr > pEndDataPtr) {
pEndLineDataPtr = pEndDataPtr + 1;
}
pSelectionBufPtrBegin = NULL;
pSelectionBufPtrEnd = NULL;
if ((pDataPtr >= pSelectionPtrBegin) && (pDataPtr <= pSelectionPtrEnd)) {
pSelectionBufPtrBegin = pBuf;
}
for (pBufPtr = pBuf; pDataPtr < pEndLineDataPtr; ++pDataPtr) {
if (pDataPtr == pSelectionPtrBegin) {
pSelectionBufPtrBegin = pBufPtr;
}
if (pDataPtr == pSelectionPtrEnd) {
if (pSelectionBufPtrBegin == NULL) {
pSelectionBufPtrBegin = pBuf;
}
pSelectionBufPtrEnd = pBufPtr + 2;
}
*pBufPtr++ = tabHexCharacters[*pDataPtr >> 4];
*pBufPtr++ = tabHexCharacters[*pDataPtr & 0xf];
*pBufPtr++ = ' ';
}
*--pBufPtr = '\0';
// set end-pointers
if (pSelectionBufPtrEnd == NULL) {
pSelectionBufPtrEnd = pBufPtr;
}
// first draw all normal
pRT->SetTextColor(m_tHexTxtCol);
pRT->DrawText(pBuf, -1, (LPRECT)cHexRect, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX);
// selection
if (pSelectionBufPtrBegin != NULL) {
BOOL bHasFocus = IsFocused();
CRect cRect(cHexRect);
cRect.left += (pSelectionBufPtrBegin - pBuf) * m_tPaintDetails.nCharacterWidth;
cRect.right -= (pBuf - 1 + m_tPaintDetails.nBytesPerRow * 3 - pSelectionBufPtrEnd) * m_tPaintDetails.nCharacterWidth;
CRect cSelectionRect(cRect);
cSelectionRect.InflateRect(0, -1, +1, +1);
*pSelectionBufPtrEnd = '\0'; // set "end-mark"
pRT->FillSolidRect(cSelectionRect, !m_bCaretAscii ? m_tSelectedFousBkgCol : m_tSelectedNoFocusBkgCol);
pRT->SetTextColor(!m_bCaretAscii ? m_tSelectedFousTxtCol : m_tSelectedNoFocusTxtCol);
pRT->DrawText(pSelectionBufPtrBegin, -1, (LPRECT)cRect, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX);
*pSelectionBufPtrEnd = ' '; // restore the buffer
}
cHexRect.OffsetRect(0, m_tPaintDetails.nLineHeight);
}
}
void SHexEdit::PaintAsciiData(IRenderTarget* pRT)
{
if (GetDataSize() == 0) {
return;
}
UINT nAdr;
UINT nEndAdr;
CRect cAsciiRect(m_tPaintDetails.cPaintingRect);
char pBuf[512];
char* pBufPtr;
BYTE* pDataPtr;
BYTE* pDataPtrEnd;
BYTE* pSelectionPtrBegin;
BYTE* pSelectionPtrEnd;
BYTE* pEndDataPtr;
char* pSelectionBufPtrBegin;
char* pSelectionBufPtrEnd;
memset(pBuf, 0, m_tPaintDetails.nBytesPerRow + 1);
// the Rect for painting & background
cAsciiRect.left += m_tPaintDetails.nAsciiPos - m_nScrollPostionX;
cAsciiRect.right = cAsciiRect.left + m_tPaintDetails.nAsciiLen;
pRT->FillSolidRect(cAsciiRect, m_tAsciiBkgCol);
cAsciiRect.bottom = cAsciiRect.top + m_tPaintDetails.nLineHeight;
//highlighting section
// selection (pointers)
if ((m_nSelectionBegin != NOSECTION_VAL) && (m_nSelectionEnd != NOSECTION_VAL)) {
pSelectionPtrBegin = m_xData.GetData(m_nSelectionBegin);
pSelectionPtrEnd = m_xData.GetData(m_nSelectionEnd);
}
else {
pSelectionPtrBegin = NULL;
pSelectionPtrEnd = NULL;
}
//highlighting section
// start & end-address
nAdr = m_nScrollPostionY * m_tPaintDetails.nBytesPerRow;
nEndAdr = nAdr + m_tPaintDetails.nVisibleLines * m_tPaintDetails.nBytesPerRow;
if (nEndAdr >= GetDataSize()) {
nEndAdr = GetDataSize() - 1;
}
pDataPtr = m_xData.GetData(nAdr);
pEndDataPtr = m_xData.GetData(nEndAdr);
// paint
for (; nAdr <= nEndAdr; nAdr += m_tPaintDetails.nBytesPerRow) {
pDataPtrEnd = pDataPtr + m_tPaintDetails.nBytesPerRow;
if (pDataPtrEnd > pEndDataPtr) {
pDataPtrEnd = pEndDataPtr + 1;
}
pSelectionBufPtrBegin = NULL;
pSelectionBufPtrEnd = NULL;
if ((pDataPtr >= pSelectionPtrBegin) && (pDataPtr <= pSelectionPtrEnd)) {
pSelectionBufPtrBegin = pBuf;
}
for (pBufPtr = pBuf; pDataPtr < pDataPtrEnd; ++pDataPtr, ++pBufPtr) {
if (pDataPtr == pSelectionPtrBegin) {
pSelectionBufPtrBegin = pBufPtr;
}
if (pDataPtr == pSelectionPtrEnd) {
if (pSelectionBufPtrBegin == NULL) {
pSelectionBufPtrBegin = pBuf;
}
pSelectionBufPtrEnd = pBufPtr + 1;
}
*pBufPtr = isprint(*pDataPtr) ? (char)*pDataPtr : '.';
// ֧<EFBFBD>ֺ<EFBFBD><EFBFBD>ֵ<EFBFBD><EFBFBD><EFBFBD>ʾ, <EFBFBD><EFBFBD><EFBFBD>Ascii<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȷ
//*pBufPtr = (*pDataPtr >= 0x20) ? (char)*pDataPtr : '.';
}
*pBufPtr = '\0';
// set end-pointers
if (pSelectionBufPtrEnd == NULL) {
pSelectionBufPtrEnd = pBufPtr;
}
pRT->SetTextColor(m_tAsciiTxtCol);
SStringT outText = S_CA2T(pBuf);
pRT->DrawText(outText, -1, (LPRECT)cAsciiRect, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX);
//highlighted section now
//// selection
if (pSelectionBufPtrBegin != NULL) {
BOOL bHasFocus = IsFocused();
CRect cRect(cAsciiRect);
cRect.left += (pSelectionBufPtrBegin - pBuf) * m_tPaintDetails.nCharacterWidth;
cRect.right -= (pBuf + m_tPaintDetails.nBytesPerRow - pSelectionBufPtrEnd) * m_tPaintDetails.nCharacterWidth;
CRect cSelectionRect(cRect);
cSelectionRect.InflateRect(0, -1, +1, +1);
*pSelectionBufPtrEnd = '\0'; // set "end-mark"
pRT->FillSolidRect(cSelectionRect, m_bCaretAscii ? m_tSelectedFousBkgCol : m_tSelectedNoFocusBkgCol);
pRT->SetTextColor(m_bCaretAscii ? m_tSelectedFousTxtCol : m_tSelectedNoFocusTxtCol);
SStringT outText = S_CA2T(pSelectionBufPtrBegin);
pRT->DrawText(outText, -1, (LPRECT)cRect, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX);
*pSelectionBufPtrEnd = ' '; // restore the buffer
}
cAsciiRect.OffsetRect(0, m_tPaintDetails.nLineHeight);
}
}
void SHexEdit::SetEditCaretPos(UINT nOffset, bool bHighBits)
{
m_nCurrentAddress = nOffset;
m_bHighBits = bHighBits;
if (GetDataSize() == 0) {
return;
}
if (m_nCurrentAddress >= GetDataSize())
m_nCurrentAddress--;
if (m_nCurrentAddress < m_nScrollPostionY * m_tPaintDetails.nBytesPerRow
|| (m_nCurrentAddress >= (m_nScrollPostionY + m_tPaintDetails.nVisibleLines) * m_tPaintDetails.nBytesPerRow)) {
// not in the visible range
DestoyEditCaret();
return;
}
UINT nRelAdr = m_nCurrentAddress - m_nScrollPostionY * m_tPaintDetails.nBytesPerRow;
UINT nRow = nRelAdr / m_tPaintDetails.nBytesPerRow;
UINT nColumn = nRelAdr % m_tPaintDetails.nBytesPerRow;
UINT nCarretHeight;
UINT nCarretWidth = m_tPaintDetails.nCharacterWidth;
if (nRow == m_tPaintDetails.nVisibleLines - 1) {
// last row can be only half visible
nCarretHeight = m_tPaintDetails.nLastLineHeight;
}
else {
nCarretHeight = m_tPaintDetails.nLineHeight;
}
UINT xpos = m_tPaintDetails.cPaintingRect.left - m_nScrollPostionX;
if (m_bCaretAscii)
xpos += m_tPaintDetails.nAsciiPos + nColumn * m_tPaintDetails.nCharacterWidth;
else
xpos += m_tPaintDetails.nHexPos + (nColumn * 3 + (bHighBits ? 0 : 1)) * m_tPaintDetails.nCharacterWidth;
if (m_bCaretAtLineEnd)
xpos += (bHighBits && !m_bCaretAscii ? 2 : 1) * m_tPaintDetails.nCharacterWidth;
UINT ypos = m_tPaintDetails.cPaintingRect.top + 1 + nRow * m_tPaintDetails.nLineHeight;
CPoint cCarretPoint(xpos, ypos);
if ((cCarretPoint.x + (short)m_tPaintDetails.nCharacterWidth <= m_tPaintDetails.cPaintingRect.left - 2)
|| (cCarretPoint.x > m_tPaintDetails.cPaintingRect.right)) {
// we can't see it
DestoyEditCaret();
return;
}
if (cCarretPoint.x < m_tPaintDetails.cPaintingRect.left - 2) {
nCarretWidth -= m_tPaintDetails.cPaintingRect.left - 2 - cCarretPoint.x;
cCarretPoint.x = m_tPaintDetails.cPaintingRect.left - 2;
}
if (cCarretPoint.x + (int)nCarretWidth > (int)m_tPaintDetails.cPaintingRect.right + 2) {
nCarretWidth = m_tPaintDetails.cPaintingRect.right + 2 - cCarretPoint.x;
}
m_CaretX = cCarretPoint.x;
m_CaretY = cCarretPoint.y;
CreateEditCaret(m_overwriteMode ? 2 : nCarretHeight - 1, m_overwriteMode ? nCarretWidth : 1); // FIXME: hardcoded insert caret width 2
SetCaretPos(m_CaretX, m_overwriteMode ? m_CaretY + nCarretHeight - 3 : m_CaretY);
ShowCaret(TRUE);
}
void SHexEdit::CreateEditCaret(UINT nCaretHeight, UINT nCaretWidth)
{
if (!m_bHasCaret || (nCaretHeight != m_nCurCaretHeight)
|| (nCaretWidth != m_nCurCaretWidth)) {
m_bHasCaret = true;
m_nCurCaretHeight = nCaretHeight;
m_nCurCaretWidth = nCaretWidth;
CreateCaret(NULL, m_nCurCaretWidth, m_nCurCaretHeight);
}
}
void SHexEdit::DestoyEditCaret() {
m_bHasCaret = false;
ShowCaret(FALSE);
}
void SHexEdit::OnTimer(UINT nTimerID)
{
if (m_bIsMouseRepActive && (nTimerID == MOUSEREP_TIMER_TID)) {
if (m_nMouseRepCounter > 0) {
m_nMouseRepCounter--;
}
else {
m_nMouseRepCounter = m_nMouseRepSpeed;
MoveScrollPostionY(m_iMouseRepDelta, false);
GetAddressFromPoint(m_cMouseRepPoint, m_nCurrentAddress, m_bHighBits);
SetEditCaretPos(m_nCurrentAddress, m_bHighBits);
m_nSelectingEnd = m_nCurrentAddress;
m_nSelectionBegin = m_nSelectingBeg;
m_nSelectionEnd = m_nSelectingEnd;
NORMALIZE_SELECTION(m_nSelectionBegin, m_nSelectionEnd);
Invalidate();
}
}
}
void SHexEdit::StartMouseRepeat(const CPoint& cPoint, int iDelta, WORD nSpeed)
{
m_cMouseRepPoint = cPoint;
m_nMouseRepSpeed = nSpeed;
m_iMouseRepDelta = iDelta;
if (!m_bIsMouseRepActive) {
m_bIsMouseRepActive = true;
m_nMouseRepCounter = nSpeed;
SetTimer2(MOUSEREP_TIMER_TID, MOUSEREP_TIMER_ELAPSE);
}
}
void SHexEdit::StopMouseRepeat()
{
if (m_bIsMouseRepActive) {
m_bIsMouseRepActive = false;
KillTimer2(MOUSEREP_TIMER_TID);
}
}
void SHexEdit::SetScrollPositionY(UINT nPosition, bool bUpdate)
{
if (nPosition > m_nScrollRangeY) {
nPosition = m_nScrollRangeY;
}
SetScrollPos(SSB_VERT, (int)nPosition, TRUE);
if ((nPosition != m_nScrollPostionY) && bUpdate) {
m_nScrollPostionY = nPosition;
Invalidate();
}
SetEditCaretPos(m_nCurrentAddress, m_bHighBits);
m_nScrollPostionY = nPosition;
}
void SHexEdit::SetScrollPositionX(UINT nPosition, bool bUpdate)
{
if (nPosition > m_nScrollRangeX) {
nPosition = m_nScrollRangeX;
}
SetScrollPos(SSB_HORZ, (int)nPosition, TRUE);
if ((nPosition != m_nScrollPostionX) && bUpdate) {
m_nScrollPostionX = nPosition;
Invalidate();
SetEditCaretPos(m_nCurrentAddress, m_bHighBits);
}
m_nScrollPostionX = nPosition;
}
void SHexEdit::MoveScrollPostionY(int iDelta, bool bUpdate)
{
if (iDelta > 0) {
SetScrollPositionY(m_nScrollPostionY + iDelta, bUpdate);
}
else {
int iPositon = (int)m_nScrollPostionY;
iPositon -= (-iDelta);
if (iPositon < 0) {
iPositon = 0;
}
SetScrollPositionY((UINT)iPositon, bUpdate);
}
}
void SHexEdit::MoveScrollPostionX(int iDelta, bool bUpdate)
{
if (iDelta > 0) {
SetScrollPositionX(m_nScrollPostionX + iDelta, bUpdate);
}
else {
int iPositon = (int)m_nScrollPostionX;
iPositon -= (-iDelta);
if (iPositon < 0) {
iPositon = 0;
}
SetScrollPositionX((UINT)iPositon, bUpdate);
}
}
void SHexEdit::MakeVisible(UINT nBegin, UINT nEnd, bool bUpdate)
{
UINT nAdrBeg = m_nScrollPostionY * m_tPaintDetails.nBytesPerRow;
UINT nFullBytesPerScreen = m_tPaintDetails.nFullVisibleLines * m_tPaintDetails.nBytesPerRow;
UINT nAdrEnd = nAdrBeg + nFullBytesPerScreen;
UINT nLength = nEnd - nBegin;
if ((nBegin > nAdrBeg) || (nEnd < nAdrEnd)) {
// don't do anything when it's simply not possible to see everything and
// we already see one ful page.
if (nLength > nFullBytesPerScreen) {
if (nAdrBeg < nBegin) {
SetScrollPositionY(nBegin / m_tPaintDetails.nBytesPerRow, false);
}
else if (nAdrEnd > nEnd) {
SetScrollPositionY((nEnd - nFullBytesPerScreen + m_tPaintDetails.nBytesPerRow) / m_tPaintDetails.nBytesPerRow, false);
}
}
else {
if (nAdrBeg > nBegin) {
SetScrollPositionY(nBegin / m_tPaintDetails.nBytesPerRow, false);
}
else if (nAdrEnd < nEnd) {
SetScrollPositionY((nEnd - nFullBytesPerScreen + m_tPaintDetails.nBytesPerRow) / m_tPaintDetails.nBytesPerRow, false);
}
}
}
int iLineX = (int)((nBegin % m_tPaintDetails.nBytesPerRow) * 3 * m_tPaintDetails.nCharacterWidth + m_tPaintDetails.nHexPos + m_tPaintDetails.cPaintingRect.left) - (int)m_nScrollPostionX;
int iLineX2 = (int)((2 + (nEnd % m_tPaintDetails.nBytesPerRow) * 3) * m_tPaintDetails.nCharacterWidth + m_tPaintDetails.nHexPos + m_tPaintDetails.cPaintingRect.left) - (int)m_nScrollPostionX;
if (iLineX > iLineX2) {
int iTemp = iLineX;
iLineX = iLineX2;
iLineX2 = iTemp;
}
if ((iLineX <= m_tPaintDetails.cPaintingRect.left) && (iLineX2 >= m_tPaintDetails.cPaintingRect.right)) {
// nothing to do here...
}
else if (iLineX < m_tPaintDetails.cPaintingRect.left) {
SetScrollPositionX(m_nScrollPostionX + iLineX - m_tPaintDetails.cPaintingRect.left, false);
}
else if (iLineX2 >= m_tPaintDetails.cPaintingRect.right) {
SetScrollPositionX(m_nScrollPostionX + iLineX2 - m_tPaintDetails.cPaintingRect.Width(), false);
}
if (bUpdate) {
Invalidate();
}
}
void SHexEdit::MoveCurrentAddress(int iDeltaAdr, bool bHighBits, bool bIgnoreShift)
{
bool bExtendSelection = !bIgnoreShift && (GetKeyState(VK_SHIFT) & 0x80000000) == 0x80000000;
if (GetDataSize() == 0) {
return;
}
UINT nAddress = m_nCurrentAddress;
if (!bExtendSelection) {
m_nSelectingBeg = NOSECTION_VAL;
m_nSelectionBegin = NOSECTION_VAL;
m_nSelectingEnd = NOSECTION_VAL;
m_nSelectionEnd = NOSECTION_VAL;
}
if (iDeltaAdr >= 0) {
// go down
if (nAddress + iDeltaAdr >= GetDataSize()) {
// we reached the end
nAddress = GetDataSize() - 1;
bHighBits = false;
m_bCaretAtLineEnd = true;
}
else {
nAddress += iDeltaAdr;
}
}
else if (iDeltaAdr < 0) {
if ((UINT)(-iDeltaAdr) <= nAddress) {
if (m_bCaretAtLineEnd && iDeltaAdr == -1)
{
m_bCaretAtLineEnd = false;
}else
nAddress -= (UINT)(-iDeltaAdr);
if (m_bCaretAtLineEnd && ((nAddress + 1) % m_tPaintDetails.nBytesPerRow))
{
m_bCaretAtLineEnd = false;
if ((UINT)(-iDeltaAdr) == m_tPaintDetails.nBytesPerRow)
{
nAddress++;
bHighBits = true;
}
}
}
else {
nAddress = 0;
bHighBits = true;
m_bCaretAtLineEnd = false;
}
}
if (bExtendSelection && (m_nSelectingBeg != NOSECTION_VAL)) {
m_nSelectingEnd = nAddress;
m_nSelectionBegin = m_nSelectingBeg;
m_nSelectionEnd = m_nSelectingEnd;
NORMALIZE_SELECTION(m_nSelectionBegin, m_nSelectionEnd);
}
MakeVisible(nAddress, nAddress, true);
SetEditCaretPos(nAddress, bHighBits);
}
void SHexEdit::GetAddressFromPoint(const CPoint& cPt, UINT& nAddress, bool& bHighBits)
{
m_bCaretAtLineEnd = false;
bool bAscii = false;
CPoint cPoint(cPt);
cPoint.x += m_nScrollPostionX;
cPoint.x += (m_tPaintDetails.nCharacterWidth >> 1);
cPoint.y -= m_tPaintDetails.cPaintingRect.top;
cPoint.x -= m_tPaintDetails.cPaintingRect.left + CONTROL_BORDER_SPACEH + 3; // 3 determined experimentally :-(
if (cPoint.y < 0) {
cPoint.y = 0;
}
else if (cPoint.y > (int)(m_tPaintDetails.nVisibleLines * m_tPaintDetails.nLineHeight)) {
cPoint.y = m_tPaintDetails.nVisibleLines * m_tPaintDetails.nLineHeight;
}
if ((int)cPoint.x < (int)m_tPaintDetails.nHexPos) {
cPoint.x = 0;
}
else if ((int)cPoint.x > (int)(m_tPaintDetails.nAsciiPos)) {
cPoint.x -= m_tPaintDetails.nAsciiPos;
if (cPoint.x >= (LONG)m_tPaintDetails.nAsciiLen)
{
cPoint.x = (LONG)m_tPaintDetails.nAsciiLen - 1;
m_bCaretAtLineEnd = true;
}
bAscii = true;
}
else {
cPoint.x -= m_tPaintDetails.nHexPos;
if (cPoint.x >= (LONG)m_tPaintDetails.nHexLen - DATA_ASCII_SPACE)
{
cPoint.x = (LONG)m_tPaintDetails.nHexLen - DATA_ASCII_SPACE - 1;
m_bCaretAtLineEnd = true;
}
}
UINT nRow = cPoint.y / m_tPaintDetails.nLineHeight;
UINT nCharColumn = cPoint.x / m_tPaintDetails.nCharacterWidth;
UINT nColumn;
if (bAscii)
nColumn = nCharColumn;
else
nColumn = nCharColumn / 3;
bHighBits = nCharColumn % 3 == 0;
nAddress = nColumn + (nRow + m_nScrollPostionY) * m_tPaintDetails.nBytesPerRow;
if (nAddress >= GetDataSize()) {
nAddress = GetDataSize() - 1;
m_bCaretAtLineEnd = true;
if ((int)nAddress < 0)
nAddress = 0;
bHighBits = false;
}
m_bCaretAscii = bAscii;
}
BOOL SHexEdit::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
MoveScrollPostionY(-(zDelta / WHEEL_DELTA), true);
return TRUE;
}
BOOL SHexEdit::OnScroll(BOOL bVertical, UINT uCode, int nPos)
{
if (m_dragSb != SSB_NULL)
{
if (bVertical)
OnVScroll(uCode, nPos, NULL);
else
OnHScroll(uCode, nPos, NULL);
}
else
__super::OnScroll(bVertical, uCode, nPos);
return TRUE;
}
void SHexEdit::OnVScroll(UINT nSBCode, UINT nPos, HWND)
{
if (GetDataSize() == 0) {
return;
}
switch (nSBCode) {
case SB_LINEDOWN:
MoveScrollPostionY(1, true);
break;
case SB_LINEUP:
MoveScrollPostionY(-1, true);
break;
case SB_PAGEDOWN:
MoveScrollPostionY(m_tPaintDetails.nFullVisibleLines, true);
break;
case SB_PAGEUP:
MoveScrollPostionY(-(int)m_tPaintDetails.nFullVisibleLines, true);
break;
case SB_THUMBTRACK:
// Windows only allows 16Bit track-positions in the callback message.
// MFC hides this by providing a 32-bit value (nobody expects to
// be an invalid value) which is unfortunately casted from a 16Bit value.
// -- MSDN gives a hint (in the API-documentation) about this problem
// -- and a solution as well. We should use GetScrollInfo here to receive
// -- the correct 32-Bit value when our scrollrange exceeds the 16bit range
// -- to keep it simple, I decided to always do it like this
SetScrollPositionY(nPos, true);
break;
}
}
void SHexEdit::OnHScroll(UINT nSBCode, UINT nPos, HWND)
{
if (GetDataSize() == 0) {
return;
}
switch (nSBCode) {
case SB_LINEDOWN:
MoveScrollPostionX(m_tPaintDetails.nCharacterWidth, true);
break;
case SB_LINEUP:
MoveScrollPostionX(-(int)m_tPaintDetails.nCharacterWidth, true);
break;
case SB_PAGEDOWN:
MoveScrollPostionX(m_tPaintDetails.cPaintingRect.Width(), true);
break;
case SB_PAGEUP:
MoveScrollPostionX(-(int)m_tPaintDetails.cPaintingRect.Width(), true);
break;
case SB_THUMBTRACK:
SetScrollPositionX(nPos, true);
break;
}
}
void SHexEdit::OnMouseMove(UINT nFlags, CPoint point)
{
if (GetDataSize() == 0) {
return;
}
if ((nFlags & MK_LBUTTON) && (m_nSelectingBeg != NOSECTION_VAL)) {
// first make a self built drag-detect (one that doesn't block)
if (!m_cDragRect.PtInRect(point)) {
m_cDragRect = CRect(-1, -1, -1, -1); // when once, out, kill it...
}
else {
return; // okay, still not draging
}
if (!m_tPaintDetails.cPaintingRect.PtInRect(point)) {
int iRepSpeed = 0;
int iDelta = 0;
if (point.y < m_tPaintDetails.cPaintingRect.top) {
iDelta = -1;
iRepSpeed = (int)m_tPaintDetails.cPaintingRect.top + 1 - (int)point.y;
}
else if (point.y > m_tPaintDetails.cPaintingRect.bottom) {
iDelta = 1;
iRepSpeed = (int)point.y - (int)m_tPaintDetails.cPaintingRect.bottom + 1;
}
if (iDelta != 0) {
iRepSpeed /= 5;
if (iRepSpeed > 5) {
iRepSpeed = 6;
}
StartMouseRepeat(point, iDelta, (short)(7 - iRepSpeed));
}
m_cMouseRepPoint = point; // make sure we always have the latest point
}
else {
StopMouseRepeat();
}
GetAddressFromPoint(point, m_nCurrentAddress, m_bHighBits);
SetEditCaretPos(m_nCurrentAddress, m_bHighBits);
m_nSelectingEnd = m_nCurrentAddress;
m_nSelectionBegin = m_nSelectingBeg;
m_nSelectionEnd = m_nSelectingEnd;
NORMALIZE_SELECTION(m_nSelectionBegin, m_nSelectionEnd);
Invalidate();
}
}
void SHexEdit::OnLButtonUp(UINT nFlags, CPoint pt)
{
if (GetCapture() == m_swnd) {
ReleaseCapture();
EventHexEditSelChanged evt(this);
evt.m_nSelBegin = m_nSelectionBegin;
evt.m_nSelEnd = m_nSelectionEnd;
FireEvent(evt);
}
StopMouseRepeat(); // in case it's started, otherwise it doesn't matter either
Invalidate();
}
void SHexEdit::OnLButtonDown(UINT nFlags, CPoint pt)
{
__super::OnLButtonDown(nFlags, pt);
GetContainer()->OnSetSwndFocus(m_swnd);
GetAddressFromPoint(pt, m_nCurrentAddress, m_bHighBits);
SetEditCaretPos(m_nCurrentAddress, m_bHighBits);
int iDragCX = GetSystemMetrics(SM_CXDRAG);
int iDragCY = GetSystemMetrics(SM_CYDRAG);
m_cDragRect = CRect(pt.x - (iDragCX >> 1), pt.y - (iDragCY >> 1),
pt.x + (iDragCX >> 1) + (iDragCX & 1), //(we don't want to loose a pixel, when it's so small)
pt.y + (iDragCY >> 1) + (iDragCY & 1));
m_nSelectingEnd = NOSECTION_VAL;
m_nSelectionBegin = NOSECTION_VAL;
m_nSelectingEnd = NOSECTION_VAL;
m_nSelectingBeg = m_nCurrentAddress;
SetCapture();
}
void SHexEdit::OnLButtonDbClick(UINT nFlags, CPoint pt)
{
}
std::string SHexEdit::GetClipboardText()
{
std::string text;
HGLOBAL hGlobal; // Global memory handle
LPSTR lpszData; // Pointer to clipboard data
unsigned long nSize; // Size of clipboard data
OpenClipboard(NULL);
if (IsClipboardFormatAvailable(CF_TEXT))
hGlobal = GetClipboardData(CF_TEXT);
else
{
CloseClipboard();
return text;
}
if (hGlobal == NULL) {
CloseClipboard();
return text;
}
lpszData = (LPSTR)GlobalLock(hGlobal);
nSize = GlobalSize(hGlobal);
text = lpszData;
GlobalUnlock(hGlobal);
CloseClipboard();
return text;
}
int SHexEdit::SetClipboard(LPCSTR lpszBuffer)
{
HGLOBAL hGlobal; // Global memory handle
LPSTR lpszData; // Pointer to clipboard data
unsigned long nSize; // Size of clipboard data
// First, open the clipboard. OpenClipboard() takes one
// parameter, the handle of the window that will temporarily
// be it's owner. If NULL is passed, the current process
// is assumed. After opening, empty the clipboard so we
// can put our text on it.
OpenClipboard(NULL);
EmptyClipboard();
// Get the size of the string in the buffer that was
// passed into the function, so we know how much global
// memory to allocate for the string.
nSize = strlen(lpszBuffer);
// Allocate the memory for the string.
hGlobal = GlobalAlloc(GMEM_ZEROINIT, nSize + 1);
// If we got any error during the memory allocation,
// we have been returned a NULL handle.
if (hGlobal == NULL) {
CloseClipboard();
return FALSE;
}
// Now we have a global memory handle to the text
// stored on the clipboard. We have to lock this global
// handle so that we have access to it.
lpszData = (LPSTR)GlobalLock(hGlobal);
// Now, copy the text from the buffer into the allocated
// global memory pointer.
for (UINT i = 0; i < nSize + 1; ++i)
*(lpszData + i) = *(lpszBuffer + i);
// Now, simply unlock the global memory pointer,
// set the clipboard data type and pointer,
// and close the clipboard.
GlobalUnlock(hGlobal);
SetClipboardData(CF_TEXT, hGlobal);
CloseClipboard();
return TRUE;
}
void SHexEdit::OnEditCopy()
{
if (!IsSelection())
return;
SByteArray selData = m_xData.Mid(m_nSelectionBegin, m_nSelectionEnd - m_nSelectionBegin + 1);
if (m_bCaretAscii)
SetClipboard(selData.ToString().c_str());
else
SetClipboard(selData.ToHex(' ').ToString().c_str());
}
static bool isHexSeparator(BYTE b)
{
switch (b) {
case '\t':
case '\r':
case '\n':
case ':':
case ' ':
return true;
default:
return false;
}
}
static UINT countHexDigits(const char* data, UINT ndata)
{
UINT n = 0;
while (ndata > 0) {
if (isxdigit(*data))
n++;
else if ((n & 1) == 0 && isHexSeparator(*data))
; // skip white space etc between bytes
else
return -1;
data++;
ndata--;
}
return n;
}
void SHexEdit::OnEditPaste()
{
if (m_bReadOnly)
return;
std::string clipText = GetClipboardText();
if (clipText.empty())
return;
UINT nPasteAdr = m_nCurrentAddress;
UINT nReplaceLength = 0;
if (IsSelection()) {
nPasteAdr = m_nSelectionBegin;
nReplaceLength = m_nSelectionEnd - m_nSelectionBegin + 1;
if (m_nSelectionEnd >= GetDataSize())
nReplaceLength--;
}
bool doHexdecode = false;
int nHexDigits = countHexDigits(clipText.c_str(), clipText.length()); // < 0 or odd if non valid hex encoding
if (nHexDigits > 0 && nHexDigits % 2 == 0) {
doHexdecode = true;
}
SByteArray pasteData;
if (doHexdecode)
pasteData.FromHexString(clipText);
else
pasteData.Append((BYTE*)clipText.c_str(), clipText.length());
SaveUndoAction(SHexEditAction::paste, nPasteAdr, m_xData.Mid(nPasteAdr, nReplaceLength), pasteData);
if (nReplaceLength > 0)
m_xData.Remove(nPasteAdr, nReplaceLength);
m_xData.Insert(nPasteAdr, pasteData);
m_bRecalc = true;
SetEditCaretPos(nPasteAdr + pasteData.Size(), true);
m_nSelectingBeg = NOSECTION_VAL;
m_nSelectionBegin = NOSECTION_VAL;
m_nSelectingEnd = NOSECTION_VAL;
m_nSelectionEnd = NOSECTION_VAL;
Invalidate();
EventHexEditDataChanged evt(this);
FireEvent(evt);
}
void SHexEdit::OnEditCut()
{
if (m_bReadOnly)
return;
if (IsSelection()) {
UINT nCutLength = m_nSelectionEnd - m_nSelectionBegin + 1;
if (m_nSelectionEnd >= GetDataSize())
nCutLength--;
if (nCutLength > 0)
{
SaveUndoAction(SHexEditAction::cut, m_nSelectionBegin, m_xData.Mid(m_nSelectionBegin, nCutLength), SByteArray());
m_xData.Remove(m_nSelectionBegin, nCutLength);
m_bRecalc = true;
SetEditCaretPos(m_nSelectionBegin, true);
m_nSelectingBeg = NOSECTION_VAL;
m_nSelectionBegin = NOSECTION_VAL;
m_nSelectingEnd = NOSECTION_VAL;
m_nSelectionEnd = NOSECTION_VAL;
Invalidate();
EventHexEditDataChanged evt(this);
FireEvent(evt);
}
}
}
bool SHexEdit::PrepareReplace(UINT pos, const SByteArray& oldData, const SByteArray& newData)
{
if (oldData.Size() > 0)
m_xData.Remove(pos, oldData.Size());
if (newData.Size() > 0)
m_xData.Insert(pos, newData);
if (oldData.Size() != newData.Size()) {
m_bRecalc = true;
}
//m_bRecalc = true;
MakeVisible(pos + newData.Size(), pos + newData.Size(), true);
SetSelection(NOSECTION_VAL, NOSECTION_VAL, true, false);
SetEditCaretPos(pos + newData.Size(), true);
Invalidate();
EventHexEditDataChanged evt(this);
FireEvent(evt);
return true;
}
void SHexEdit::OnEditClear()
{
OnDelete(VK_DELETE);
}
void SHexEdit::OnEditSelectAll()
{
if (GetDataSize() > 0) {
SetSelection(0, GetDataSize() - 1, false, true);
}
}
void SHexEdit::OnDelete(WPARAM wParam)
{
if (m_bReadOnly || GetDataSize() == 0)
return;
if (IsSelection()) {
OnEditCut();
}
else {
if (wParam == VK_BACK) {
if (m_nCurrentAddress == 0)
return;
m_nCurrentAddress--;
}
UINT delAddress = m_nCurrentAddress;
if (m_bCaretAtLineEnd)
delAddress++;
if (delAddress == GetDataSize())
return;
SaveUndoAction(SHexEditAction::cut, delAddress, m_xData.Mid(delAddress, 1), SByteArray());
m_xData.Remove(delAddress, 1);
MoveCurrentAddress(0, true);
Invalidate();
EventHexEditDataChanged evt(this);
FireEvent(evt);
}
}
bool SHexEdit::OnEditInput(WORD nInput)
{
if ((nInput > 255) || m_bReadOnly) {
return false;
}
BYTE nValue = 255;
char nKey = (char)tolower((char)nInput);
if (m_bCaretAscii) {
if (nInput <= 256)
nValue = (BYTE)nInput;
}
else if ((nKey >= 'a') && (nKey <= 'f')) {
nValue = nKey - (BYTE)'a' + (BYTE)0xa;
}
else if ((nKey >= '0') && (nKey <= '9')) {
nValue = nKey - (BYTE)'0';
}
else
return false;
UINT replaceLen = 0;
if (m_nCurrentAddress >= GetDataSize() || (!m_overwriteMode && (m_bCaretAscii || m_bHighBits))) {
if (m_nCurrentAddress >= GetDataSize())
m_bHighBits = true;
m_xData.Insert(m_nCurrentAddress, 0);
m_bRecalc = true;
}
else if (m_nCurrentAddress < GetDataSize()) { // we are overwriting 1 char
replaceLen = 1;
}
if (!m_bCaretAscii)
if (m_bHighBits)
nValue = (nValue << 4) | (m_xData[m_nCurrentAddress] & 0x0f);
else
nValue = (m_xData[m_nCurrentAddress] & 0xf0) | nValue;
SaveUndoAction(SHexEditAction::input, m_nCurrentAddress, m_xData.Mid(m_nCurrentAddress, replaceLen), SByteArray(&nValue, 1));
m_xData[m_nCurrentAddress] = nValue;
if (m_bCaretAscii)
MoveCurrentAddress(1, true, true);
else if (m_bHighBits)
MoveCurrentAddress(0, false, true);
else
MoveCurrentAddress(1, true, true);
Invalidate();
EventHexEditDataChanged evt(this);
FireEvent(evt);
return true;
}
void SHexEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
bool bIsControl = (GetKeyState(VK_CONTROL) & 0x80000000) == 0x80000000;
if (bIsControl)
return;
if (nChar != VK_TAB)
OnEditInput((WORD)nChar);
}
BOOL SHexEdit::PressKeyDown(UINT nChar)
{
bool bIsShift = (GetKeyState(VK_SHIFT) & 0x80000000) == 0x80000000;
bool bIsControl = (GetKeyState(VK_CONTROL) & 0x80000000) == 0x80000000;
if (bIsControl)
{
switch (nChar) {
case 'X': // ctrl + x: cut
OnEditCut();
return TRUE;
case 'C': // ctrl + c: copy
OnEditCopy();
return TRUE;
case 'V': // ctrl + v: paste
OnEditPaste();
return TRUE;
case 'A': // ctrl + a: select all
OnEditSelectAll();
return TRUE;
case 'Z':
Undo();
return TRUE;
case 'Y':
Redo();
return TRUE;
case VK_HOME:
MoveCurrentAddress(-0x8000000, true);
return TRUE;
case VK_END:
MoveCurrentAddress(0x7ffffff, true);
return TRUE;
}
}
else
{
if (bIsShift && (m_nSelectingBeg == NOSECTION_VAL)) {
// start with selecting
m_nSelectingBeg = m_nCurrentAddress;
}
switch (nChar) {
case VK_DOWN:
MoveCurrentAddress(m_tPaintDetails.nBytesPerRow, m_bHighBits);
return TRUE;
case VK_UP:
if (m_nCurrentAddress > m_tPaintDetails.nBytesPerRow)
MoveCurrentAddress(-(int)m_tPaintDetails.nBytesPerRow, m_bHighBits);
return TRUE;
case VK_RIGHT:
if (IsSelection()) {
MoveCurrentAddress(m_nSelectionEnd - m_nCurrentAddress + 1, 1);
}
else if (m_bCaretAscii) {
MoveCurrentAddress(1, true);
}
else if (m_bHighBits) {
// offset stays the same, caret moves to low-byte
MoveCurrentAddress(0, false);
}
else {
MoveCurrentAddress(1, true);
}
return TRUE;
case VK_LEFT:
if (IsSelection()) {
MoveCurrentAddress(m_nSelectionBegin - m_nCurrentAddress - 1, m_nSelectionBegin == 0);
}
else if (m_bCaretAscii) {
MoveCurrentAddress(-1, true);
}
else if (!m_bHighBits && !m_bCaretAtLineEnd) {
// offset stays the same, caret moves to high-byte
MoveCurrentAddress(0, true);
}
else {
MoveCurrentAddress(-1, false);
}
return TRUE;
case VK_PRIOR:
MoveCurrentAddress(-(int)(m_tPaintDetails.nBytesPerRow * (m_tPaintDetails.nVisibleLines - 1)), m_bHighBits);
return TRUE;
case VK_NEXT:
MoveCurrentAddress(m_tPaintDetails.nBytesPerRow * (m_tPaintDetails.nVisibleLines - 1), m_bHighBits);
return TRUE;
case VK_HOME:
if (m_nCurrentAddress == GetDataSize())
m_nCurrentAddress--;
MoveCurrentAddress(-(int)((m_nCurrentAddress) % m_tPaintDetails.nBytesPerRow), true);
return TRUE;
case VK_END:
m_bCaretAtLineEnd = true;
MoveCurrentAddress(m_tPaintDetails.nBytesPerRow - 1 - (m_nCurrentAddress % m_tPaintDetails.nBytesPerRow), false);
return TRUE;
case VK_INSERT:
m_overwriteMode = !m_overwriteMode;
SetEditCaretPos(m_nCurrentAddress, m_bHighBits);
Invalidate();
return TRUE;
case VK_TAB:
if (m_bShowAscii && !bIsShift) {
m_bCaretAscii = !m_bCaretAscii;
SetEditCaretPos(m_nCurrentAddress, m_bHighBits);
Invalidate();
}
return TRUE;
case VK_DELETE:
case VK_BACK:
OnDelete(nChar);
return TRUE;
}
}
return FALSE;
}
void SHexEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
SetMsgHandled(PressKeyDown(nChar));
}
void SHexEdit::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (GetKeyState(VK_MENU) & 0x8000) OnKeyDown(nChar, nRepCnt, nFlags);
else SetMsgHandled(FALSE);
}
HRESULT SHexEdit::OnAttrAddrBkColor(const SStringW& strValue, BOOL bLoading)
{
m_tAdrBkgCol = GETCOLOR(strValue);
if (!bLoading)
{
}
return S_OK;
}
HRESULT SHexEdit::OnAttrHexBkColor(const SStringW& strValue, BOOL bLoading)
{
m_tHexBkgCol = GETCOLOR(strValue);
if (!bLoading)
{
}
return S_OK;
}
HRESULT SHexEdit::OnAttrAsciiBkColor(const SStringW& strValue, BOOL bLoading)
{
m_tAsciiBkgCol = GETCOLOR(strValue);
if (!bLoading)
{
}
return S_OK;
}
BOOL SHexEdit::SaveUndoAction(UINT type, UINT position, const SByteArray& replaceData, const SByteArray& insertData)
{
delete m_redo; // m_pData will change invalidating m_redo
m_redo = NULL;
if (type == SHexEditAction::input && m_undo != 0 && m_undo->getType() == SHexEditAction::input &&
m_undo->getPosition() + m_undo->getInsertLen() == position) {
// merge input actions
if (!m_undo->append(replaceData, insertData)) {
//cant save undo
return FALSE;
}
}
else {
// new action can't be merged with previous
SHexEditAction* action = new SHexEditAction();
if (action == NULL ||
!action->set(SHexEditAction::input, position, replaceData, insertData, m_undo)) {
delete action;
//cant save undo
return FALSE;
}
m_undo = action;
}
return TRUE;
}
void SHexEdit::Undo()
{
if (!CanUndo())
return;
SHexEditAction* action = m_undo;
// replace insertData/insertLen at position with replaceData/replaceLen
if (!PrepareReplace(action->getPosition(), action->getInsertData(), action->getReplaceData())) {
return;
}
// move head of m_undo to m_redo
m_undo = action->getNext();
action->setNext(m_redo);
m_redo = action;
}
void SHexEdit::Redo()
{
if (!CanRedo())
return;
SHexEditAction* action = m_redo;
// replace replaceData/replaceLen at position with insertData/insertLen
if (!PrepareReplace(action->getPosition(), action->getReplaceData(), action->getInsertData())) {
// cant redo
return;
}
// move head of m_redo to m_undo
m_redo = action->getNext();
action->setNext(m_undo);
m_undo = action;
}
}