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(); 
}