www.pudn.com > HexEditOcx.rar > HexEditCtl.cpp
/*
* Softerra Hex Editor (c) Softerra, LLC 2005
*
* FILENAME: HexEditCtl.cpp
* SUBSYSTEM: Core
* DESCRIPTION: Implementation of the CHexEditCtrl ActiveX Control class.
*/
#include "stdafx.h"
#include "atlbase.h"
#include "LanguageListener.h"
#include "HexEdit.h"
#include "HexEditCtl.h"
#include "HexEditPpg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
enum EDIT_MODE // m_editMode flags
{
EDIT_BYTES = 1,
EDIT_INSERT = 2,
};
const int ID_TIMER = 1;
IMPLEMENT_DYNCREATE(CHexEditCtrl, COleControl)
/////////////////////////////////////////////////////////////////////////////
// Message map
BEGIN_MESSAGE_MAP(CHexEditCtrl, COleControl)
ON_WM_CONTEXTMENU()
//{{AFX_MSG_MAP(CHexEditCtrl)
ON_WM_CREATE()
ON_WM_DESTROY()
ON_WM_KILLFOCUS()
ON_WM_SETFOCUS()
ON_WM_KEYDOWN()
ON_WM_CHAR()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONDBLCLK()
ON_WM_GETDLGCODE()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_VSCROLL()
ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
ON_COMMAND(ID_EDIT_CUT, OnEditCut)
ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
ON_WM_HSCROLL()
ON_WM_TIMER()
ON_WM_SIZE()
//}}AFX_MSG_MAP
ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateEditSelectAll)
ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// Dispatch map
BEGIN_DISPATCH_MAP(CHexEditCtrl, COleControl)
//{{AFX_DISPATCH_MAP(CHexEditCtrl)
DISP_PROPERTY_NOTIFY(CHexEditCtrl, "ShowAddress", m_showAddress, OnShowAddressChanged, VT_BOOL)
DISP_PROPERTY_NOTIFY(CHexEditCtrl, "ShowAscii", m_showAscii, OnShowAsciiChanged, VT_BOOL)
DISP_PROPERTY_NOTIFY(CHexEditCtrl, "AllowChangeSize", m_allowChangeSize, OnAllowChangeSizeChanged, VT_BOOL)
DISP_PROPERTY_NOTIFY(CHexEditCtrl, "DataModified", m_dataModified, OnDataModifiedChanged, VT_BOOL)
DISP_PROPERTY_EX(CHexEditCtrl, "DigitsInAddress", GetDigitsInAddress, SetDigitsInAddress, VT_I2)
DISP_PROPERTY_EX(CHexEditCtrl, "DigitsInData", GetDigitsInData, SetDigitsInData, VT_I2)
DISP_PROPERTY_EX(CHexEditCtrl, "Columns", GetColumns, SetColumns, VT_I2)
DISP_PROPERTY_EX(CHexEditCtrl, "FontHeight", GetFontHeight, SetFontHeight, VT_I2)
DISP_FUNCTION(CHexEditCtrl, "SetData", SetData, VT_ERROR, VTS_PVARIANT VTS_I4)
DISP_FUNCTION(CHexEditCtrl, "GetData", GetData, VT_ERROR, VTS_PVARIANT)
DISP_STOCKPROP_FORECOLOR()
DISP_STOCKPROP_BACKCOLOR()
DISP_STOCKPROP_APPEARANCE()
//}}AFX_DISPATCH_MAP
DISP_FUNCTION_ID(CHexEditCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()
/////////////////////////////////////////////////////////////////////////////
// Event map
BEGIN_EVENT_MAP(CHexEditCtrl, COleControl)
//{{AFX_EVENT_MAP(CHexEditCtrl)
// NOTE - ClassWizard will add and remove event map entries
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_EVENT_MAP
END_EVENT_MAP()
/////////////////////////////////////////////////////////////////////////////
// Property pages
// TODO: Add more property pages as needed. Remember to increase the count!
BEGIN_PROPPAGEIDS(CHexEditCtrl, 1)
// PROPPAGEID(CHexEditPropPage::guid)
PROPPAGEID(CLSID_CColorPropPage)
END_PROPPAGEIDS(CHexEditCtrl)
/////////////////////////////////////////////////////////////////////////////
// Initialize class factory and guid
IMPLEMENT_OLECREATE_EX(CHexEditCtrl, "HEXEDIT.HexEditCtrl.1",
0x2e93307e, 0x777d, 0x49e4, 0x88, 0x6a, 0xd5, 0xb0, 0x44, 0x70, 0x79, 0x6a)
/////////////////////////////////////////////////////////////////////////////
// Type library ID and version
IMPLEMENT_OLETYPELIB(CHexEditCtrl, _tlid, _wVerMajor, _wVerMinor)
/////////////////////////////////////////////////////////////////////////////
// Interface IDs
const IID BASED_CODE IID_DHexEdit =
{ 0x8224766d, 0xd7e6, 0x40a2, { 0xad, 0x7e, 0x6a, 0x5b, 0x82, 0x37, 0x28, 0xbe } };
const IID BASED_CODE IID_DHexEditEvents =
{ 0xc9fdd5f5, 0x117, 0x4fab, { 0xb0, 0xec, 0xcc, 0xd5, 0x7a, 0x6d, 0x23, 0xc7 } };
/////////////////////////////////////////////////////////////////////////////
// Control type information
static const DWORD BASED_CODE _dwHexEditOleMisc =
OLEMISC_ACTIVATEWHENVISIBLE |
OLEMISC_SETCLIENTSITEFIRST |
OLEMISC_INSIDEOUT |
OLEMISC_CANTLINKINSIDE |
OLEMISC_RECOMPOSEONRESIZE;
IMPLEMENT_OLECTLTYPE(CHexEditCtrl, IDS_HEXEDIT, _dwHexEditOleMisc)
CString CHexEditCtrl::m_strWindowClass;
CCriticalSection CHexEditCtrl::_critSect;
/////////////////////////////////////////////////////////////////////////////
// CHexEditCtrl::CHexEditCtrlFactory::UpdateRegistry -
// Adds or removes system registry entries for CHexEditCtrl
BOOL CHexEditCtrl::CHexEditCtrlFactory::UpdateRegistry(BOOL bRegister)
{
// TODO: Verify that your control follows apartment-model threading rules.
// Refer to MFC TechNote 64 for more information.
// If your control does not conform to the apartment-model rules, then
// you must modify the code below, changing the 6th parameter from
// afxRegApartmentThreading to 0.
if (bRegister)
{
return AfxOleRegisterControlClass(
AfxGetInstanceHandle(),
m_clsid,
m_lpszProgID,
IDS_HEXEDIT,
IDB_HEXEDIT,
afxRegApartmentThreading,
_dwHexEditOleMisc,
_tlid,
_wVerMajor,
_wVerMinor);
}
else
return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
}
/////////////////////////////////////////////////////////////////////////////
// CHexEditCtrl::CHexEditCtrl - Constructor
CHexEditCtrl::CHexEditCtrl()
{
InitializeIIDs(&IID_DHexEdit, &IID_DHexEditEvents);
_critSect.Lock();
if ( m_strWindowClass.IsEmpty())
m_strWindowClass = AfxRegisterWndClass ( CS_DBLCLKS, ::LoadCursor(0, IDC_IBEAM) );
_critSect.Unlock();
m_selStart = m_selEnd = -1;
m_editPos = m_viewPos = 0;
m_editMode = EDIT_BYTES | EDIT_INSERT;
m_bMouseDown = m_bMouseMove = false;
m_bTimer = false;
m_dataModified = false;
m_dwStartAddr = 0;
m_pDestData = NULL;
m_nHorzScroll = 0;
}
/////////////////////////////////////////////////////////////////////////////
// CHexEditCtrl::~CHexEditCtrl - Destructor
CHexEditCtrl::~CHexEditCtrl()
{
if ( PermissibleType( LPCVARIANT(m_data)->vt ) )
{
if( m_pDestData )
{
ASSERT( m_pDestData->vt == VT_EMPTY );
*m_pDestData = m_data.Detach();
}
else
{
m_data.Detach();
}
}
}
inline bool CHexEditCtrl::PermissibleType(VARTYPE vt)
{
return vt == vtCharArray || vt == vtByteArray;
}
/////////////////////////////////////////////////////////////////////////////
// CHexEditCtrl::OnDraw - Drawing function
void CHexEditCtrl::OnDraw(
CDC* pdc, const CRect& /*rcBounds*/, const CRect& rcInvalid)
{
if ( rcInvalid.IsRectEmpty() )
return;
if ( !PermissibleType( LPCVARIANT(m_data)->vt ) )
{
// Paint the background using the BackColor property when no data set
CBrush bkBrush(TranslateColor(GetBackColor()));
pdc->FillRect(rcInvalid, &bkBrush);
return;
}
CRect rc;
GetClientRect(rc);
CDC dc;
dc.CreateCompatibleDC(pdc);
CBitmap bm;
bm.CreateCompatibleBitmap(pdc, rc.Width(), rc.Height());
dc.SelectObject(bm);
// paint half-letter-wide rect at the left of the text
CRect rc1 ( 0, rcInvalid.top, m_cellSize.cx/2, rcInvalid.bottom );
CBrush bkBrush(TranslateColor(GetBackColor()));
dc.FillRect(rc1, &bkBrush);
CFont font;
font.CreateFont ( m_fontHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, 0 );
CFont* oldFont = dc.SelectObject ( &font );
dc.SetBoundsRect(&rc, DCB_DISABLE);
CString strText;
int nAddr = m_viewPos*(m_digitsInData/2);
BYTE* buf;
m_data.AccessData((void**)&buf);
for ( int i = 0; i < m_charCountWindow.cy; i++ )
// draw line
{
if ( m_showAddress )
{
// draw address field
if (DWORD(nAddr) <= m_data.GetOneDimSize() /*+ m_columns*(m_digitsInData/2)*/)
strText.Format ( _T("%0*X "), m_digitsInAddress, nAddr+m_dwStartAddr );
else
strText.Format ( _T("%*c "), m_digitsInAddress, ' ' );
dc.SetTextColor(TranslateColor(GetForeColor()));
dc.SetBkColor(TranslateColor(GetBackColor()));
TextOutWithCheck ( &dc, rcInvalid, -m_nHorzScroll*m_cellSize.cx+m_cellSize.cx/2, i*m_cellSize.cy, strText );
}
for ( int j = 0; j < m_columns; j++ )
{
// draw hex data
int dwPos = j+i*m_columns+m_viewPos;
if ( dwPos >= m_selStart && dwPos <= m_selEnd)
{
// selected text colors
dc.SetBkColor(::GetSysColor ( COLOR_HIGHLIGHT ));
dc.SetTextColor(::GetSysColor ( COLOR_HIGHLIGHTTEXT ));
}
else
{
// normal text colors
dc.SetTextColor(TranslateColor(GetForeColor()));
dc.SetBkColor(TranslateColor(GetBackColor()));
}
if ( DWORD(dwPos) < m_data.GetOneDimSize()/(m_digitsInData/2))
{
switch (m_digitsInData)
{
case 2:
strText.Format ( _T("%02X "), buf[dwPos]);
break;
case 4:
strText.Format ( _T("%04X "), *(WORD*)(buf+dwPos*2));
break;
case 8:
strText.Format ( _T("%08X "), *(DWORD*)(buf+dwPos*4));
break;
default:
ASSERT(0);
}
TextOutWithCheck ( &dc, rcInvalid, (m_addrMargin+j*(m_digitsInData+1)-m_nHorzScroll)*m_cellSize.cx+m_cellSize.cx/2,
i*m_cellSize.cy, strText );
if ( m_bRealShowAscii )
{
// draw ascii data
ASSERT ( m_digitsInData == 2 );
WCHAR wChar;
char cChar = buf[dwPos];
if (cChar >= 0 && cChar < 32) cChar = '.';
MultiByteToWideChar(GetPreferedCodePage(), NULL, &cChar, 1, &wChar, 1);
strText = cChar;
//(char)buf[dwPos];
//char(isprint ( buf[dwPos] ) ? buf[dwPos] : '.');
CharOutWithCheck ( &dc, rcInvalid, (m_asciiMargin+j-m_nHorzScroll)*m_cellSize.cx+m_cellSize.cx/2, i*m_cellSize.cy, wChar );
}
}
else // if ( DWORD(dwPos)
{
// if the last line has less than m_columns numbers, fill it with spaces
dc.SetTextColor(TranslateColor(GetForeColor()));
dc.SetBkColor(TranslateColor(GetBackColor()));
strText.Format ( _T("%*c "), m_digitsInData, ' ' );
TextOutWithCheck ( &dc, rcInvalid, (m_addrMargin+j*(m_digitsInData+1)-m_nHorzScroll)*m_cellSize.cx+m_cellSize.cx/2, i*m_cellSize.cy, strText );
if ( m_bRealShowAscii )
{
ASSERT ( m_digitsInData == 2 );
strText = ' ';
TextOutWithCheck ( &dc, rcInvalid, (m_asciiMargin+j-m_nHorzScroll)*m_cellSize.cx+m_cellSize.cx/2, i*m_cellSize.cy, strText );
}
}
}
dc.SetTextColor(TranslateColor(GetForeColor()));
dc.SetBkColor(TranslateColor(GetBackColor()));
if ( m_bRealShowAscii )
{
// draw space between hex and ascii
strText = ' ';
TextOutWithCheck ( &dc, rcInvalid, (m_asciiMargin-m_nHorzScroll-1)*m_cellSize.cx+m_cellSize.cx/2, i*m_cellSize.cy, strText );
}
// draw extra spaces at the end of line
dc.SetTextColor(TranslateColor(GetForeColor()));
dc.SetBkColor(TranslateColor(GetBackColor()));
if ( m_charCountView.cx < m_charCountWindow.cx )
strText.Format(_T("%*c "), m_charCountWindow.cx-m_charCountView.cx+1, ' ' );
else
strText = _T(" ");
TextOutWithCheck ( &dc, rcInvalid, (m_asciiMargin + (m_bRealShowAscii?m_columns:0) -m_nHorzScroll)*m_cellSize.cx+m_cellSize.cx/2, i*m_cellSize.cy, strText );
nAddr += m_columns*(m_digitsInData/2);
/* if ( DWORD(nAddr) >= m_data.GetOneDimSize() )
break;*/
}
m_data.UnaccessData();
// fill the rest of the window with spaces
strText.Format ( _T("%*c "), m_charCountWindow.cx, ' ' );
TextOutWithCheck ( &dc, rcInvalid, m_cellSize.cx/2, m_charCountWindow.cy*m_cellSize.cy, strText );
dc.SelectObject ( oldFont );
pdc->BitBlt(0, 0, rc.Width(), rc.Height(), &dc, 0, 0, SRCCOPY);
}
/////////////////////////////////////////////////////////////////////////////
// CHexEditCtrl::DoPropExchange - Persistence support
void CHexEditCtrl::DoPropExchange(CPropExchange* pPX)
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
// Call PX_ functions for each persistent custom property.
PX_Bool ( pPX, _T("ShowAddress"), m_showAddress, TRUE );
PX_Bool ( pPX, _T("ShowAscii"), m_showAscii, TRUE );
PX_Short ( pPX, _T("DigitsInData"), m_digitsInData, 2 );
PX_Short ( pPX, _T("DigitsInAddress"), m_digitsInAddress, 4 );
PX_Short ( pPX, _T("Columns"), m_columns, 8 );
PX_Short ( pPX, _T("FontHeight"), m_fontHeight, 14 );
PX_Bool ( pPX, _T("AllowChangeSize"), m_allowChangeSize, FALSE );
}
/////////////////////////////////////////////////////////////////////////////
// CHexEditCtrl::OnResetState - Reset control to default state
void CHexEditCtrl::OnResetState()
{
COleControl::OnResetState(); // Resets defaults found in DoPropExchange
// TODO: Reset any other control state here.
}
/////////////////////////////////////////////////////////////////////////////
// CHexEditCtrl::AboutBox - Display an "About" box to the user
void CHexEditCtrl::AboutBox()
{
CDialog dlgAbout(IDD_ABOUTBOX_HEXEDIT);
dlgAbout.DoModal();
}
/////////////////////////////////////////////////////////////////////////////
// CHexEditCtrl properties and methods
void CHexEditCtrl::OnShowAddressChanged()
{
if ( PermissibleType( LPCVARIANT(m_data)->vt ) )
{
// do following only if m_data has been set
m_nHorzScroll = 0;
RecalcLayout();
InvalidateControl();
}
SetModifiedFlag();
}
void CHexEditCtrl::OnShowAsciiChanged()
{
if ( m_digitsInData == 2 )
{
m_nEditDigit = 0;
if ( !m_showAscii )
m_editMode |= EDIT_BYTES;
if ( PermissibleType( LPCVARIANT(m_data)->vt ) )
{
// do following only if m_data has been set
m_nHorzScroll = 0;
RecalcLayout();
ChangeEditPos(0,0);
InvalidateControl();
}
SetModifiedFlag();
}
}
short CHexEditCtrl::GetColumns()
{
return m_columns;
}
void CHexEditCtrl::SetColumns(short nNewValue)
{
if ( nNewValue < 0 )
{
ThrowError ( CTL_E_INVALIDPROPERTYVALUE, _T("Value must be non-negative"));
return;
}
m_columns = nNewValue;
if ( PermissibleType( LPCVARIANT(m_data)->vt ) )
{
// do following only if m_data has been set
m_nHorzScroll = 0;
RecalcLayout();
InvalidateControl();
}
SetModifiedFlag();
}
SCODE CHexEditCtrl::SetData(VARIANT FAR* pData, long dwStartAddr)
{
if ( ( pData->vt != (VT_ARRAY | VT_UI1) && pData->vt != (VT_ARRAY | VT_I1) ) ||
::SafeArrayGetDim(pData->parray) != 1)
{
// only 1-dimension array of bytes can be set
return E_INVALIDARG;
}
m_data.Attach ( *pData );
if ( m_data.GetOneDimSize() >= 0x80000000 )
{
m_data.Detach();
return E_OUTOFMEMORY;
}
m_pDestData = pData;
m_dwStartAddr = dwStartAddr;
m_nEditDigit = 0;
m_selStart = m_selEnd = -1;
m_editPos = m_viewPos = m_nHorzScroll = 0;
m_editMode |= EDIT_BYTES;
m_dataModified = false;
RecalcLayout();
InvalidateControl();
return S_OK;
}
SCODE CHexEditCtrl::GetData( VARIANT FAR* pData )
{
if( pData == NULL ) return E_POINTER;
pData->vt = LPCVARIANT(m_data)->vt/* VT_UI1 | VT_ARRAY */;
pData->parray = NULL;
m_data.Copy( &pData->parray );
return S_OK;
}
short CHexEditCtrl::GetFontHeight()
{
return m_fontHeight;
}
void CHexEditCtrl::SetFontHeight(short nNewValue)
{
if ( nNewValue < 8 )
{
ThrowError ( CTL_E_INVALIDPROPERTYVALUE, _T("Value must be greater than 7"));
return;
}
m_fontHeight = nNewValue;
if ( PermissibleType( LPCVARIANT(m_data)->vt ) )
{
// do following only if m_data has been set
m_nHorzScroll = 0;
RecalcLayout();
InvalidateControl();
}
SetModifiedFlag();
}
short CHexEditCtrl::GetDigitsInAddress()
{
return m_digitsInAddress;
}
void CHexEditCtrl::SetDigitsInAddress(short nNewValue)
{
if ( nNewValue != 4 && nNewValue != 8 )
{
ThrowError ( CTL_E_INVALIDPROPERTYVALUE, _T("Value must be 4 or 8"));
return;
}
m_digitsInAddress = nNewValue;
if ( PermissibleType( LPCVARIANT(m_data)->vt ) )
{
// do following only if m_data has been set
m_nHorzScroll = 0;
RecalcLayout();
InvalidateControl();
}
SetModifiedFlag();
}
short CHexEditCtrl::GetDigitsInData()
{
return m_digitsInData;
}
void CHexEditCtrl::SetDigitsInData(short nNewValue)
{
if ( nNewValue != 2 && nNewValue != 4 && nNewValue != 8 )
{
ThrowError ( CTL_E_INVALIDPROPERTYVALUE, _T("Value must be 2, 4 or 8"));
return;
}
m_digitsInData = nNewValue;
m_nEditDigit = 0;
m_selStart = m_selEnd = -1;
m_editPos = m_viewPos = m_nHorzScroll = 0;
m_editMode |= EDIT_BYTES;
if ( PermissibleType( LPCVARIANT(m_data)->vt ) )
{
// do following only if m_data has been set
RecalcLayout();
InvalidateControl();
}
SetModifiedFlag();
}
void CHexEditCtrl::OnAllowChangeSizeChanged()
{
SetModifiedFlag();
}
void CHexEditCtrl::OnDataModifiedChanged()
{
}
/////////////////////////////////////////////////////////////////////////////
// CHexEditCtrl message handlers
BOOL CHexEditCtrl::PreCreateWindow(CREATESTRUCT& cs)
{
cs.lpszClass = m_strWindowClass;
return COleControl::PreCreateWindow(cs);
}
void CHexEditCtrl::OnKillFocus(CWnd* pNewWnd)
{
COleControl::OnKillFocus(pNewWnd);
if ( !PermissibleType( LPCVARIANT(m_data)->vt ) )
return;
if (m_bMouseDown)
{
ReleaseCapture();
m_bMouseDown = false;
m_bMouseMove = false;
if ( m_bTimer )
{
KillTimer(ID_TIMER);
m_bTimer = false;
}
// place caret just after the selection
if ( !IsSelectionEmpty() )
{
if ( m_editPos == m_selEnd )
m_editPos++;
m_nEditDigit = 0;
}
}
PlaceCaret();
DestroyCaret ();
}
void CHexEditCtrl::OnSetFocus(CWnd* pOldWnd)
{
COleControl::OnSetFocus(pOldWnd);
if ( !PermissibleType( LPCVARIANT(m_data)->vt ) )
return;
CreateSolidCaret ( (m_editMode & EDIT_INSERT) ? 0:m_cellSize.cx,
m_cellSize.cy );
PlaceCaret();
if ( !m_bMouseDown )
ShowCaret();
}
void CHexEditCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (!AmbientUserMode()) return;
BOOL bShift = ::GetKeyState(VK_SHIFT) & 0x80000000;
BOOL bControl = ::GetKeyState(VK_CONTROL) & 0x80000000;
if ( nChar == VK_TAB )
{
CWnd* pParent = GetParentOwner();
if ( !pParent ) return;
if ( bShift && !bControl )
{
::SendMessage(pParent->m_hWnd, WM_NEXTDLGCTL, 1, 0);
return;
}
else if ( !bControl && !bShift )
{
::SendMessage(pParent->m_hWnd, WM_NEXTDLGCTL, 0, 0);
return;
}
}
if ( !PermissibleType( LPCVARIANT(m_data)->vt ) )
return;
if (m_bMouseDown) return;
if ( bShift && !bControl )
{
int prevEditPos = m_editPos;
switch ( nChar )
// SHIFT+arrows - modify selection
{
case VK_LEFT:
ChangeEditPos ( -1, 0 );
break;
case VK_RIGHT:
ChangeEditPos ( 1, 0 );
break;
case VK_UP:
ChangeEditPos ( 0, -1 );
break;
case VK_DOWN:
ChangeEditPos ( 0, 1 );
break;
case VK_PRIOR:
ChangeEditPos ( 0, -m_charCountWindow.cy );
break;
case VK_NEXT:
ChangeEditPos ( 0, m_charCountWindow.cy );
break;
case VK_HOME:
if ( m_editMode & EDIT_BYTES )
m_nHorzScroll = 0;
if ( bControl )
// select to the start of data
{
m_editPos = 0;
ChangeEditPos ( 0, 0 );
}
else // select to the beginning of the line
ChangeEditPos ( -(m_editPos % m_columns), 0 );
break;
case VK_END:
if ( bControl )
// select to the end of data
{
m_editPos = m_data.GetOneDimSize()/(m_digitsInData/2);
ChangeEditPos ( 0, 0 );
}
else // select to the end of the line
ChangeEditPos ( m_columns-(m_editPos % m_columns), 0 );
break;
case VK_INSERT:
OnEditPaste();
return;
case VK_DELETE:
OnEditCut();
return;
default:
COleControl::OnKeyDown(nChar, nRepCnt, nFlags);
return;
}
// recalc selection margins
if ( IsSelectionEmpty() )
{
if ( m_editPos < prevEditPos )
{
m_selStart = m_editPos;
m_selEnd = prevEditPos-1;
}
else if ( m_editPos > prevEditPos )
{
m_selStart = prevEditPos;
m_selEnd = m_editPos-1;
}
}
else
{
if ( prevEditPos-1 == m_selEnd )
m_selEnd = m_editPos-1;
else
m_selStart = m_editPos;
if ( m_selStart == m_selEnd+1 )
ClearSelection();
}
InvalidateControl();
}
else
{
if ( bControl && !bShift)
{
switch ( nChar )
{
case VK_HOME:
m_editPos = 0;
ChangeEditPos ( 0, 0 );
break;
case VK_END:
m_editPos = m_data.GetOneDimSize();
ChangeEditPos ( 0, 0 );
break;
case VK_INSERT:
OnEditCopy();
break;
case 'A':
OnEditSelectAll();
break;
case VK_TAB:
if ( m_digitsInData == 2 && m_bRealShowAscii )
{ // switch hex/ascii
m_editMode ^= EDIT_BYTES;
ChangeEditPos(0,0); // update scrollbars and caret pos
}
break;
default:
COleControl::OnKeyDown(nChar, nRepCnt, nFlags);
break;
}
}
else if ( !bControl && !bShift )
{
switch ( nChar )
{
case VK_LEFT:
ChangeEditPos ( -1, 0 );
break;
case VK_RIGHT:
ChangeEditPos ( 1, 0 );
break;
case VK_UP:
ChangeEditPos ( 0, -1 );
break;
case VK_DOWN:
ChangeEditPos ( 0, 1 );
break;
case VK_PRIOR:
ChangeEditPos ( 0, -m_charCountWindow.cy );
break;
case VK_NEXT:
ChangeEditPos ( 0, m_charCountWindow.cy );
break;
case VK_HOME:
if ( m_editMode & EDIT_BYTES )
m_nHorzScroll = 0;
ChangeEditPos ( -(m_editPos % m_columns), 0 );
break;
case VK_END: // todo???: place caret at the end
ChangeEditPos ( m_columns-(m_editPos % m_columns)-1, 0 );
break;
case VK_BACK:
if ( (m_editMode & EDIT_INSERT) || m_allowChangeSize )
{
if ( IsSelectionEmpty() )
{
if ( m_editPos > 0 )
{
ChangeEditPos ( -1, 0, true );
DeleteData ( m_editPos );
m_dataModified = true;
}
}
else
DeleteSelection();
InvalidateControl();
PlaceCaret();
}
break;
case VK_DELETE:
if ( (m_editMode & EDIT_INSERT) || m_allowChangeSize )
{
if ( IsSelectionEmpty() )
{
if ( (DWORD)m_editPos < m_data.GetOneDimSize() )
{
DeleteData ( m_editPos );
ChangeEditPos ( 0, 0, true );
m_dataModified = true;
}
}
else
{
DeleteSelection();
InvalidateControl();
PlaceCaret();
}
}
else // if !EDIT_INSERT
{
if ( !IsSelectionEmpty() )
{
// zero selected bytes
BYTE* buf;
m_data.AccessData((void**)&buf);
memset ( buf+m_selStart*(m_digitsInData/2), 0, (m_selEnd-m_selStart+1)*(m_digitsInData/2));
m_data.UnaccessData();
ClearSelection();
}
}
break;
case VK_INSERT:
m_editMode ^= EDIT_INSERT;
CreateSolidCaret ( (m_editMode & EDIT_INSERT) ? 0:m_cellSize.cx,
m_cellSize.cy );
ShowCaret();
PlaceCaret();
break;
default:
COleControl::OnKeyDown(nChar, nRepCnt, nFlags);
return;
}
if ( !IsSelectionEmpty() )
{
ClearSelection();
InvalidateControl();
}
}
}
}
void CHexEditCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if ( !PermissibleType( LPCVARIANT(m_data)->vt ) || !AmbientUserMode())
return;
if (m_bMouseDown) return;
if ( m_editMode & EDIT_BYTES )
{
if((nChar >= '0' && nChar <= '9') || (nChar >= 'a' && nChar <= 'f') || (nChar >= 'A' && nChar <= 'F'))
{
UINT b = nChar - '0';
if(b > 9)
{
if (b <= 'F' - '0')
b = 10 + nChar - 'A';
else
b = 10 + nChar - 'a';
}
UINT mask = 0xFUL << (( m_digitsInData - m_nEditDigit - 1 )*4);
if ( !EnterNumber())
return;
BYTE* buf;
DWORD bufAddr = m_editPos*(m_digitsInData/2);
m_data.AccessData((void**)&buf);
*(DWORD*)(buf+bufAddr) = *(DWORD*)(buf+bufAddr) & ~mask | (b << (( m_digitsInData - m_nEditDigit - 1 )*4));
m_data.UnaccessData();
m_nEditDigit++;
if (NormalizeEditPos())
InvalidateControl(); // cursor was outside visible area
InvalidateEditRect ( m_editPos );
if ( m_nEditDigit == m_digitsInData )
ChangeEditPos(1, 0, false);
else
{
// NormalizeEditPos();
// InvalidateControl();
UpdateScrollBars();
PlaceCaret();
}
}
else
COleControl::OnChar(nChar, nRepCnt, nFlags);
}
else // EDIT_ASCII
{
if ( nChar >= ' ' )
{
if ( !EnterNumber())
return;
BYTE* buf;
m_data.AccessData((void**)&buf);
// Convert unicode character to Locale character
#ifdef UNICODE
/*int nRes = */WideCharToMultiByte(GetPreferedCodePage(), 0,
LPCWSTR(&nChar), 1, LPSTR(buf+m_editPos), 1, NULL, NULL);
#else
buf[m_editPos] = BYTE(nChar);
#endif
m_data.UnaccessData();
if (NormalizeEditPos())
InvalidateControl(); // cursor was outside visible area
else
InvalidateEditRect ( m_editPos );
ChangeEditPos(1, 0, false);
}
}
}
UINT CHexEditCtrl::OnGetDlgCode()
{
// if ( LPCVARIANT(m_data)->vt == (VT_ARRAY | VT_UI1) )
return COleControl::OnGetDlgCode() | DLGC_WANTARROWS | DLGC_WANTCHARS | DLGC_WANTTAB;
// else
// return COleControl::OnGetDlgCode() | DLGC_WANTARROWS | DLGC_WANTCHARS;
}
void CHexEditCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
if ( !PermissibleType( LPCVARIANT(m_data)->vt ) || !AmbientUserMode() )
return;
m_bMouseDown = true;
if ( !IsSelectionEmpty() )
ClearSelection();
if ( nFlags & MK_SHIFT )
{
if ( DWORD(m_editPos*(m_digitsInData/2)) == m_data.GetOneDimSize())
m_posMouseDown = m_editPos - 1;
else
m_posMouseDown = m_editPos;
RecalcSelection(point);
m_bMouseMove = true;
}
else
{
ChangeEditPos ( point );
if ( DWORD(m_editPos*(m_digitsInData/2)) == m_data.GetOneDimSize())
m_posMouseDown = m_editPos - 1;
else
m_posMouseDown = m_editPos;
}
SetCapture();
HideCaret();
COleControl::OnLButtonDown(nFlags, point);
}
void CHexEditCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
{
if ( !PermissibleType( LPCVARIANT(m_data)->vt ) || !AmbientUserMode())
return;
ChangeEditPos ( point );
// select the number under cursor if it isn't at the end of data
if ( DWORD(m_editPos*(m_digitsInData/2)) != m_data.GetOneDimSize())
{
m_selStart = m_selEnd = m_editPos;
m_nEditDigit = 0;
InvalidateEditRect(m_editPos);
m_editPos++;
}
PlaceCaret();
m_bMouseDown = false;
ReleaseCapture();
COleControl::OnLButtonDblClk(nFlags, point);
}
void CHexEditCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
if ( !PermissibleType( LPCVARIANT(m_data)->vt ) || !AmbientUserMode() )
return;
if ( m_bMouseDown )
{
if ( m_bMouseMove )
RecalcSelection(point);
ReleaseCapture();
m_bMouseDown = false;
m_bMouseMove = false;
if ( m_bTimer )
{
KillTimer(ID_TIMER);
m_bTimer = false;
}
// place caret just after the selection
if ( !IsSelectionEmpty() )
{
if ( m_editPos == m_selEnd )
m_editPos++;
m_nEditDigit = 0;
}
}
PlaceCaret();
ShowCaret();
COleControl::OnLButtonUp(nFlags, point);
}
void CHexEditCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
if ( !PermissibleType( LPCVARIANT(m_data)->vt ) || !AmbientUserMode() )
return;
if ( m_bMouseDown )
{
m_bMouseMove = true;
if ( !m_bTimer && !m_rcClient.PtInRect(point))
{
// mouse went outside the window, so we need to scroll even if mouse doesn't
// moving
SetTimer ( ID_TIMER, 200, 0 );
m_bTimer = true;
}
else if ( m_bTimer && m_rcClient.PtInRect(point))
{
// mouse is inside window, so we don't need the timer
KillTimer ( ID_TIMER );
m_bTimer = false;
}
m_prevMousePoint = point;
RecalcSelection(point);
}
COleControl::OnMouseMove(nFlags, point);
}
void CHexEditCtrl::OnVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/)
{
int nPrevViewPos = m_viewPos;
switch(nSBCode)
{
case SB_LINEDOWN:
m_viewPos += m_columns;
break;
case SB_LINEUP:
m_viewPos -= m_columns;
break;
case SB_PAGEDOWN:
m_viewPos += m_columns*m_charCountWindow.cy;
break;
case SB_PAGEUP:
m_viewPos -= m_columns*m_charCountWindow.cy;
break;
case SB_THUMBTRACK:
SCROLLINFO si;
si.cbSize = sizeof(si);
GetScrollInfo ( SB_VERT, &si, SIF_TRACKPOS );
m_viewPos = si.nTrackPos*m_columns;
break;
}
if ( m_viewPos < 0 )
m_viewPos = 0;
DWORD dwDataLen = m_data.GetOneDimSize()/(m_digitsInData/2)/m_columns * m_columns;
if ( DWORD(m_viewPos) > dwDataLen - m_columns*(m_charCountWindow.cy-1) )
m_viewPos = dwDataLen - m_columns*(m_charCountWindow.cy-1);
if ( m_viewPos != nPrevViewPos )
{
InvalidateControl();
SetScrollPos ( SB_VERT, m_viewPos / m_columns );
PlaceCaret();
}
}
void CHexEditCtrl::OnEditCut()
{
if ( (m_editMode & EDIT_INSERT) || m_allowChangeSize )
{
if ( !IsSelectionEmpty() )
{
OnEditCopy();
DeleteSelection();
InvalidateControl();
}
}
}
void CHexEditCtrl::OnEditCopy()
{
if ( IsSelectionEmpty() ) return;
if ( !OpenClipboard() )
return;
VERIFY(::EmptyClipboard());
// size of selection in bytes
int dwLen = (m_selEnd-m_selStart+1)*(m_digitsInData/2);
// alloc memory for "BinaryData" clipboard format
HGLOBAL hMemb = ::GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT, dwLen+sizeof(DWORD));
// alloc memory for CF_TEXT clipboard format
HGLOBAL hMema = ::GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT, dwLen+1);
ASSERT ( hMemb );
ASSERT ( hMema );
// copy binary
BYTE* buf;
LPBYTE p = (BYTE*)::GlobalLock(hMemb);
// first DWORD in BinaryData contains the length of data in bytes
*(DWORD*) p = dwLen;
p += sizeof(DWORD);
m_data.AccessData ( (void**)&buf );
memcpy(p, buf+m_selStart*(m_digitsInData/2), dwLen);
::GlobalUnlock(hMemb);
// copy text
p = (BYTE*)::GlobalLock(hMema);
memcpy(p, buf+m_selStart*(m_digitsInData/2), dwLen);
p[dwLen] = 0;
::GlobalUnlock(hMema);
m_data.UnaccessData();
VERIFY(::SetClipboardData((USHORT)RegisterClipboardFormat(_T("BinaryData")), hMemb));
VERIFY(::SetClipboardData(CF_TEXT, hMema));
::CloseClipboard();
}
void CHexEditCtrl::OnEditPaste()
{
static UINT hFormat = ::RegisterClipboardFormat(_T("BinaryData"));
// we can paste CF_TEXT or BinaryData
if (!::IsClipboardFormatAvailable((USHORT)hFormat) && !::IsClipboardFormatAvailable(CF_TEXT))
return;
BOOL bIsBinaryData = FALSE;
HGLOBAL hMemory = NULL;
VERIFY ( OpenClipboard() );
if ( ::IsClipboardFormatAvailable(hFormat) )
{
hMemory = ::GetClipboardData(hFormat);
bIsBinaryData = TRUE;
}
else if (::IsClipboardFormatAvailable(CF_TEXT))
{
hMemory = ::GetClipboardData(CF_TEXT);
}
if( hMemory )
{
LPBYTE p = (LPBYTE) ::GlobalLock(hMemory);
SIZE_T cbData = 0;
if ( bIsBinaryData )
{
// first DWORD in BinaryData contains the length of data in bytes
cbData = *(SIZE_T*)p;
p += sizeof(DWORD);
}
else
{
// CR1544: some apps pass HGLOBAL blocks to clipboard with a size
// that exceeds actual size of text within, so in order not to copy
// extra trash we need adjusting data size here.
cbData = min( ::GlobalSize(hMemory)-1, strlen((char*)p) );
}
if ( cbData > 0 )
{
if ( m_editMode & EDIT_INSERT )
{
DeleteSelection();
InsertData ( m_editPos, (cbData+m_digitsInData/2-1)/(m_digitsInData/2) );
}
else
{
if ( !IsSelectionEmpty() )
{
m_editPos = m_selStart;
if ( cbData > ULONG(m_selEnd - m_selStart + 1) )
cbData = m_selEnd - m_selStart + 1;
ClearSelection();
}
DWORD dwDataLen = m_data.GetOneDimSize();
if ( m_editPos*(m_digitsInData/2)+cbData > dwDataLen )
{
// need to add bytes at the end
if (m_allowChangeSize)
InsertData ( dwDataLen/(m_digitsInData/2), (cbData - (dwDataLen-m_editPos*(m_digitsInData/2))+(m_digitsInData/2)-1)/(m_digitsInData/2) );
else
cbData = dwDataLen - m_editPos*(m_digitsInData/2);
}
}
if ( cbData > 0 )
{
BYTE* buf;
m_data.AccessData ( (void**)&buf );
memcpy ( buf+m_editPos*(m_digitsInData/2), p, cbData );
m_data.UnaccessData();
m_dataModified = true;
InvalidateControl();
}
}
::GlobalUnlock( hMemory );
}
::CloseClipboard();
}
void CHexEditCtrl::OnTimer(UINT nIDEvent)
{
// called when mouse went outside window during selection
ASSERT ( m_bTimer );
if ( nIDEvent == ID_TIMER )
{
RecalcSelection ( m_prevMousePoint );
}
else
COleControl::OnTimer(nIDEvent);
}
void CHexEditCtrl::OnContextMenu(CWnd*, CPoint point)
{
if ( !PermissibleType( LPCVARIANT(m_data)->vt ) || !AmbientUserMode() )
return;
{
if (point.x == -1 && point.y == -1){
//keystroke invocation
CRect rect;
GetClientRect(rect);
ClientToScreen(rect);
point = rect.TopLeft();
point.Offset(5, 5);
}
CMenu menu;
VERIFY(menu.LoadMenu(IDR_POPUP_HEX_EDIT_CTRL));
CMenu* pPopup = menu.GetSubMenu(0);
ASSERT(pPopup != NULL);
CWnd* pWndPopupOwner = this;
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
pWndPopupOwner);
}
}
void CHexEditCtrl::OnEditSelectAll()
{
if ( m_data.GetOneDimSize() == 0 )
return;
m_editPos = m_selStart = 0;
m_selEnd = m_data.GetOneDimSize()/(m_digitsInData/2)-1;
ChangeEditPos (0, 0, true ); // place caret & repaint
}
void CHexEditCtrl::OnUpdateEditSelectAll(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_data.GetOneDimSize() != 0);
}
void CHexEditCtrl::OnUpdateEditCopy(CCmdUI* pCmdUI)
{
pCmdUI->Enable(!IsSelectionEmpty());
}
void CHexEditCtrl::OnUpdateEditCut(CCmdUI* pCmdUI)
{
pCmdUI->Enable(!IsSelectionEmpty() && ((m_editMode & EDIT_INSERT) || m_allowChangeSize));
}
void CHexEditCtrl::OnUpdateEditPaste(CCmdUI* pCmdUI)
{
COleDataObject obj;
if (obj.AttachClipboard())
pCmdUI->Enable(obj.IsDataAvailable(CF_TEXT) || obj.IsDataAvailable((USHORT)RegisterClipboardFormat(_T("BinaryData"))));
else
pCmdUI->Enable(false);
}
void CHexEditCtrl::OnHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/)
{
int nPrevHorzScroll = m_nHorzScroll;
switch(nSBCode)
{
case SB_LINELEFT:
m_nHorzScroll--;
break;
case SB_PAGELEFT:
m_nHorzScroll -= m_charCountWindow.cx;
break;
case SB_LINERIGHT:
m_nHorzScroll++;
break;
case SB_PAGERIGHT:
m_nHorzScroll += m_charCountWindow.cx;
break;
case SB_THUMBTRACK:
SCROLLINFO si;
si.cbSize = sizeof(si);
GetScrollInfo ( SB_HORZ, &si, SIF_TRACKPOS );
m_nHorzScroll = si.nTrackPos;
break;
}
if ( m_nHorzScroll < 0 )
m_nHorzScroll = 0;
else if ( m_nHorzScroll > m_charCountView.cx - m_charCountWindow.cx )
m_nHorzScroll = m_charCountView.cx - m_charCountWindow.cx;
if ( nPrevHorzScroll != m_nHorzScroll )
{
InvalidateControl();
SetScrollPos ( SB_HORZ, m_nHorzScroll );
PlaceCaret();
}
}
void CHexEditCtrl::OnSize(UINT nType, int cx, int cy)
{
COleControl::OnSize(nType, cx, cy);
if ( PermissibleType( LPCVARIANT(m_data)->vt ) )
{
// do following only if m_data has been set
RecalcLayout();
InvalidateControl();
}
}
void CHexEditCtrl::InvalidateEditRect(long nEditPos)
{
CRect rcInvalid;
long nPos = nEditPos - m_viewPos;
ASSERT ( nPos >= 0 && nPos < m_columns*m_charCountWindow.cy );
// invalidate rect of the number
rcInvalid.left = (nPos % m_columns * (m_digitsInData+1) + m_addrMargin - m_nHorzScroll)*m_cellSize.cx+m_cellSize.cx/2;
rcInvalid.top = nPos / m_columns * m_cellSize.cy;
rcInvalid.right = rcInvalid.left + (m_digitsInData+1)*m_cellSize.cx;
rcInvalid.bottom = rcInvalid.top + m_cellSize.cy;
InvalidateControl(rcInvalid);
if ( m_bRealShowAscii )
{
// invalidate rect of corresponding ASCII char
rcInvalid.left = (nPos % m_columns + m_asciiMargin - m_nHorzScroll)*m_cellSize.cx+m_cellSize.cx/2;
rcInvalid.top = nPos / m_columns * m_cellSize.cy;
rcInvalid.right = rcInvalid.left + m_cellSize.cx;
rcInvalid.bottom = rcInvalid.top + m_cellSize.cy;
InvalidateControl(rcInvalid);
}
}
void CHexEditCtrl::DeleteSelection()
{
if ( !IsSelectionEmpty())
{
DeleteData ( m_selStart, m_selEnd-m_selStart+1 );
m_editPos = m_selStart;
m_dataModified = true;
// m_viewPos = m_editPos/m_columns * m_columns;
ClearSelection();
NormalizeEditPos();
PlaceCaret();
}
}
void CHexEditCtrl::RecalcLayout()
{
if ( m_hWnd == 0 ) return; // invisible window
if ( m_data.GetOneDimSize()+m_dwStartAddr > 0x10000 )
m_digitsInAddress = 8;
int nColumns = m_columns;
CFont font;
font.CreateFont ( m_fontHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, 0 );
CDC* pdc = GetDC();
ASSERT ( pdc );
CFont* oldFont = pdc->SelectObject ( &font );
// determine char size
m_cellSize = pdc->GetTextExtent(_T("0"));
pdc->LPtoDP ( &m_cellSize );
pdc->SelectObject ( oldFont );
ReleaseDC ( pdc );
GetClientRect ( m_rcClient );
// compute entire rect of the control, including scroll bars
if (GetStyle() & WS_VSCROLL)
m_rcClient.right += ::GetSystemMetrics(SM_CXVSCROLL);
if (GetStyle() & WS_HSCROLL)
m_rcClient.bottom += ::GetSystemMetrics(SM_CYHSCROLL);
// before calculations
bool bShowSbVert = false; // need to show vertical scroll bar?
bool bShowSbHorz = false; // need to show horizontal scroll bar?
// after calculations
bool bShowSbVert2 = false;
bool bShowSbHorz2 = false;
m_bRealShowAscii = (m_digitsInData == 2 && m_showAscii);
do
{
bShowSbVert = bShowSbVert2;
bShowSbHorz = bShowSbHorz2;
// first pass - compute params when scrollbars are hidden
// next passes (0, 1, or 2) - recalculate params if client rect was shrunk after showing scrollbars
m_charCountWindow.cx = (m_rcClient.Width() - m_cellSize.cx/2) / m_cellSize.cx;
m_charCountWindow.cy = m_rcClient.Height() / m_cellSize.cy;
// if ( m_digitsInData != 2 )
// m_bRealShowAscii = false;
if ( m_showAddress )
m_addrMargin = m_digitsInAddress+2;
else
m_addrMargin = 0;
if ( nColumns == 0 )
{
// auto columns
if ( m_bRealShowAscii )
m_columns = short((m_charCountWindow.cx - m_addrMargin - 1)/(m_digitsInData+2));
else
m_columns = short((m_charCountWindow.cx - m_addrMargin+1)/(m_digitsInData+1));
if ( m_columns <= 0 )
m_columns = 1;
}
m_asciiMargin = m_charCountView.cx = m_columns * (m_digitsInData+1) + m_addrMargin;
if ( m_bRealShowAscii )
{
m_charCountView.cx += m_columns * (m_digitsInData/2) + 1;
m_asciiMargin++;
}
else
m_charCountView.cx--;
m_charCountView.cy = m_data.GetOneDimSize() / (m_columns*(m_digitsInData/2)) + 1;
if ( m_charCountView.cx > m_charCountWindow.cx && !bShowSbHorz )
{
m_rcClient.bottom -= ::GetSystemMetrics(SM_CYHSCROLL);
bShowSbHorz2 = true;
}
if ( m_charCountView.cy > m_charCountWindow.cy && !bShowSbVert )
{
m_rcClient.right -= ::GetSystemMetrics(SM_CXVSCROLL);
bShowSbVert2 = true;
}
} while ( bShowSbHorz != bShowSbHorz2 || bShowSbVert != bShowSbVert2 );
if ( m_digitsInData != 2 )
// rounding data length on (m_digitsInData/2)
m_data.ResizeOneDim ( (m_data.GetOneDimSize()+(m_digitsInData/2)-1)/(m_digitsInData/2)*(m_digitsInData/2));
// m_nHorzScroll = 0;
m_viewPos = m_viewPos / m_columns * m_columns;
NormalizeEditPos();
UpdateScrollBars();
if (!(GetStyle() & WS_VSCROLL))
m_viewPos = 0;
}
void CHexEditCtrl::PlaceCaret()
{
CWnd *pFocus = GetFocus();
if( pFocus != NULL && GetFocus()->m_hWnd == m_hWnd )
{
POINT pt;
if ( !IsSelectionEmpty() && m_editPos == m_selEnd+1 )
{
// place caret just after the selection
if ( m_editMode & EDIT_BYTES )
{
pt.x = m_selEnd % m_columns * (m_digitsInData+1) + m_digitsInData + 1;
if ( m_showAddress )
pt.x += m_addrMargin;
}
else
pt.x = m_asciiMargin + m_selEnd % m_columns + 1;
pt.y = (m_selEnd - m_viewPos)/m_columns;
}
else
{
if ( m_editMode & EDIT_BYTES )
{
pt.x = m_editPos % m_columns * (m_digitsInData+1) + m_nEditDigit;
if ( m_showAddress )
pt.x += m_addrMargin;
}
else
pt.x = m_asciiMargin + m_editPos % m_columns;
pt.y = (m_editPos - m_viewPos)/m_columns;
}
pt.x = (pt.x - m_nHorzScroll)*m_cellSize.cx+m_cellSize.cx/2-1;
pt.y *= m_cellSize.cy;
SetCaretPos ( pt );
}
}
void CHexEditCtrl::ChangeEditPos(long dx, long dy, bool bRepaint /* = false */)
{
m_nEditDigit = 0;
m_editPos += dy*m_columns + dx;
if ( NormalizeEditPos())
bRepaint = true;
if ( UpdateScrollBars ())
bRepaint = true;
if ( bRepaint )
InvalidateControl();
PlaceCaret();
}
void CHexEditCtrl::ChangeEditPos(CPoint pt)
{
pt.x = (pt.x - m_cellSize.cx/2) / m_cellSize.cx;
pt.y /= m_cellSize.cy;
pt.x += m_nHorzScroll;
if ( pt.x >= m_asciiMargin && m_bRealShowAscii && !m_bMouseMove ||
m_bMouseMove && !(m_editMode & EDIT_BYTES))
// click in the ascii field or extending selection in ascii field
/* if selection was started in ascii, we never shouldn't switch
to hex field until selection is finished
The same, when selection started in hex field
*/
{
if ( pt.x < m_asciiMargin )
pt.x = m_asciiMargin;
else
if ( pt.x >= m_asciiMargin + m_columns*m_digitsInData/2 )
pt.x = m_asciiMargin + m_columns*m_digitsInData/2 - 1;
m_editPos = m_viewPos + (pt.x-m_asciiMargin) + pt.y * m_columns;
m_editMode &= ~EDIT_BYTES;
}
else
{
m_editMode |= EDIT_BYTES;
if ( pt.x >= m_asciiMargin )
pt.x = m_asciiMargin - 1;
pt.x -= m_addrMargin;
if ( pt.x < 0 ) // clicked in address area
pt.x = 0;
if ( pt.x >= m_columns*(m_digitsInData+1))
pt.x = (m_columns-1)*(m_digitsInData+1);
m_nEditDigit = pt.x % (m_digitsInData+1);
if ( m_nEditDigit == m_digitsInData )
{
m_nEditDigit = m_digitsInData-1;
}
m_editPos = m_viewPos + pt.x / (m_digitsInData+1) + pt.y * m_columns;
}
NormalizeEditPos();
UpdateScrollBars();
}
bool CHexEditCtrl::NormalizeEditPos()
{
bool bRepaint = false;
DWORD dwDataSize = m_data.GetOneDimSize()/(m_digitsInData/2);
if ( m_editPos < 0 )
m_editPos = 0;
else if ( DWORD(m_editPos) >= dwDataSize )
{
m_editPos = dwDataSize;
m_nEditDigit = 0;
}
// change m_viewPos if m_editPos is moved out of the visible area
if ( m_viewPos > m_editPos )
{
m_viewPos = m_editPos / m_columns * m_columns;
bRepaint = true;
}
else if ( m_viewPos+m_columns*m_charCountWindow.cy <= m_editPos )
{
m_viewPos = (m_editPos / m_columns - m_charCountWindow.cy + 1) * m_columns;
bRepaint = true;
}
// if m_viewPos is too near to the end of data, decrease m_viewPos so entire page is displayed
// (this may happen after deleting large selection or changing properties)
if ( DWORD(m_viewPos) > m_data.GetOneDimSize() / (m_digitsInData/2) - m_columns*(m_charCountWindow.cy-1))
{
m_viewPos = m_data.GetOneDimSize() / (m_digitsInData/2) / m_columns * m_columns - m_columns*(m_charCountWindow.cy-1);
if ( m_viewPos < 0 ) m_viewPos = 0;
}
int nPosX; // x coordinate of caret in view
if ( m_editMode & EDIT_BYTES )
nPosX = m_addrMargin + m_editPos % m_columns * (m_digitsInData+1);
else // EDIT_ASCII
nPosX = m_asciiMargin + m_editPos % m_columns;
if ( nPosX < m_nHorzScroll )
{
m_nHorzScroll = nPosX;
bRepaint = true;
}
else
{
if ( m_editMode & EDIT_BYTES )
nPosX += m_digitsInData; // to make entire number visible
else
nPosX++;
if ( nPosX-m_nHorzScroll >= m_charCountWindow.cx )
{
m_nHorzScroll = nPosX - m_charCountWindow.cx;
bRepaint = true;
}
}
return bRepaint;
}
void CHexEditCtrl::InsertData(DWORD dwPos, DWORD dwCount/*=1*/)
{
// convert to bytes
dwPos *= (m_digitsInData/2);
dwCount *= (m_digitsInData/2);
DWORD dwArrSize = m_data.GetOneDimSize();
ASSERT ( dwCount > 0 );
ASSERT ( dwPos <= dwArrSize );
if ( dwArrSize+dwCount <= dwArrSize || dwArrSize+dwCount >= 0x80000000 )
{
// overflow
AfxThrowMemoryException ();
return;
}
m_data.ResizeOneDim ( dwArrSize + dwCount );
if ( dwPos != dwArrSize )
{
BYTE* buf;
m_data.AccessData((void**)&buf);
memmove ( buf+dwPos+dwCount, buf+dwPos, dwArrSize-dwPos );
memset ( buf+dwPos, 0, dwCount );
m_data.UnaccessData();
}
RecalcLayout();
}
void CHexEditCtrl::DeleteData(DWORD dwPos, DWORD dwCount /*=1*/)
{
// convert to bytes
dwPos *= (m_digitsInData/2);
dwCount *= (m_digitsInData/2);
DWORD dwArrSize = m_data.GetOneDimSize();
ASSERT ( dwCount > 0 );
ASSERT ( dwPos < dwArrSize );
if ( dwPos != dwArrSize-1 )
{
BYTE* buf;
m_data.AccessData((void**)&buf);
memmove ( buf+dwPos, buf+dwPos+dwCount, dwArrSize-dwPos-dwCount );
m_data.UnaccessData();
}
m_data.ResizeOneDim ( dwArrSize - dwCount );
RecalcLayout();
}
bool CHexEditCtrl::UpdateScrollBars()
{
bool bRepaint = false;
SCROLLINFO si, prevSI;
si.cbSize = prevSI.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = m_charCountView.cy - 1;
si.nPage = m_charCountWindow.cy;
si.nPos = (m_viewPos+m_columns-1) / m_columns;
if ( !bRepaint && ( !GetScrollInfo(SB_VERT, &prevSI, SIF_POS ) || si.nPos != prevSI.nPos ))
// if scrollbar is about to show or its position changed, the control needs repainting
bRepaint = true;
if(si.nMax >= int(si.nPage))
{
ShowScrollBar(SB_VERT);
}
else
{
ShowScrollBar(SB_VERT, FALSE);
}
VERIFY(SetScrollInfo(SB_VERT, &si, TRUE));
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = m_charCountView.cx-1;
si.nPage = m_charCountWindow.cx;
si.nPos = m_nHorzScroll;
if ( si.nMax > (int)si.nPage && si.nPos+int(si.nPage) > si.nMax )
si.nPos = m_nHorzScroll = si.nMax - si.nPage + 1;
if ( !bRepaint && ( !GetScrollInfo(SB_HORZ, &prevSI, SIF_POS ) || si.nPos != prevSI.nPos ))
// if scrollbar is about to show or its position changed, the control needs repainting
bRepaint = true;
if(si.nMax >= (int)si.nPage )
{
ShowScrollBar(SB_HORZ);
}
else
{
ShowScrollBar(SB_HORZ, FALSE);
}
VERIFY(SetScrollInfo(SB_HORZ, &si, TRUE));
return bRepaint;
}
inline void CHexEditCtrl::ClearSelection()
{
m_selStart = m_selEnd = -1;
InvalidateControl();
}
inline bool CHexEditCtrl::IsSelectionEmpty()
{
return ( m_selStart == -1 );
}
void CHexEditCtrl::RecalcSelection(CPoint point)
{
if ( m_data.GetOneDimSize() == 0 )
return;
if ( m_bMouseDown )
{
int nPrevSelStart = m_selStart, nPrevSelEnd = m_selEnd;
ChangeEditPos ( point );
if ( m_posMouseDown < m_editPos )
{
if ( DWORD(m_editPos*(m_digitsInData/2)) == m_data.GetOneDimSize())
m_editPos--;
m_selStart = m_posMouseDown;
m_selEnd = m_editPos;
m_editPos = m_selEnd;
m_nEditDigit = 0;
}
else
{
m_selStart = m_editPos;
m_selEnd = m_posMouseDown;
m_editPos = m_selStart;
m_nEditDigit = 0;
}
if ( nPrevSelStart != m_selStart || nPrevSelEnd != m_selEnd )
InvalidateControl();
}
}
void CHexEditCtrl::TextOutWithCheck(CDC *pdc, const CRect &rcInvalid, int x, int y, CString &strText)
{
CRect rcText ( x, y, x+strText.GetLength()*m_cellSize.cx, y+m_cellSize.cy );
if ( !((rcInvalid & rcText).IsRectEmpty()) )
pdc->TextOut ( x, y, strText );
}
void CHexEditCtrl::CharOutWithCheck(CDC *pdc, const CRect &rcInvalid, int x, int y, WCHAR wChar)
{
CRect rcChar ( x, y, x+m_cellSize.cx, y+m_cellSize.cy );
if ( !((rcInvalid & rcChar).IsRectEmpty()) )
TextOutW(pdc->m_hDC, x, y, &wChar, 1);
}
bool CHexEditCtrl::EnterNumber()
{
if ( !IsSelectionEmpty())
{
if ( (m_editMode & EDIT_INSERT) || m_allowChangeSize )
{
// delete current selection and insert 1 number
m_editPos = m_selStart;
DeleteSelection();
InsertData ( m_editPos );
InvalidateControl();
}
else
{
ClearSelection();
}
}
else // is m_editPos at the end?
if ( DWORD(m_editPos)*(m_digitsInData/2) == m_data.GetOneDimSize())
{
if ( (m_editMode & EDIT_INSERT) || m_allowChangeSize )
{
InsertData ( m_editPos );
if ( m_editPos % m_columns != m_columns-1)
InvalidateEditRect (m_editPos);
else
InvalidateControl(); // we need to add new line
}
else
return false;
}
else if ((m_editMode & EDIT_INSERT) && m_nEditDigit == 0 )
// insert 1 number
{
InsertData ( m_editPos );
InvalidateControl();
}
m_dataModified = true;
return true;
}
UINT CHexEditCtrl::GetPreferedCodePage()
{
HKL hkl = GetKeyboardLayout(0);
DWORD dwLangID = 0xFFFF & (DWORD)hkl;
CHARSETINFO csi = { 0 };
BOOL bResult = TranslateCharsetInfo((DWORD*)dwLangID, &csi, /*TCI_SRCLOCALE*/0x1000);
return bResult ? csi.ciACP : 0;
}
void CHexEditCtrl::OnLangEvent(int/* nCode*/, WPARAM/* wParam*/, LPARAM/* lParam*/)
{
Invalidate(FALSE);
UpdateWindow();
}
int CHexEditCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
int iRes = COleControl::OnCreate(lpCreateStruct);
ActivateListener();
if (!AmbientUserMode())
m_data.CreateOneDim(VT_UI1, 50);
return iRes;
}
void CHexEditCtrl::OnDestroy()
{
DeactivateListener();
COleControl::OnDestroy();
}