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
1874 lines
50 KiB
/*
|
|
* 2022.1.29 by СÐÂ
|
|
*/
|
|
|
|
#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 : '.';
|
|
// Ö§³Öºº×ÖµÄÏÔʾ, »áµ¼ÖÂAsciiÇø¶ÔÆë¼ÆËã²»ÕýÈ·
|
|
//*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;
|
|
}
|
|
} |