// memview.cpp : implementation file
//

#include "stdafx.h"
#include "sxbmem.h"
#include "sxbmedoc.h"
#include "memview.h"
#include <ctype.h>

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
extern CSxbmemApp theApp;

/////////////////////////////////////////////////////////////////////////////
// CMemView

IMPLEMENT_DYNCREATE(CMemView, CScrollView)

CMemView::CMemView()
{
	m_Size.SetRectEmpty();
	m_pFont = NULL;
	m_PrintText = NULL;
	m_TextLength = 0;
	m_Text = NULL;
	m_AddToSel = FALSE;
	m_DeactiavteRgn.CreateRectRgn(0, 0, 0, 0);
	m_SelRgn.CreateRectRgn(0, 0, 0, 0);
	m_SelStart = m_SelEnd = m_PinSel = 0;
#ifndef _MAC
	m_nTabStops = 8*4;  // default 8 character positions
#endif
	m_hPrinterFont = NULL;
	m_hMirrorFont = NULL;
}

CMemView::~CMemView()
{
}


BEGIN_MESSAGE_MAP(CMemView, CScrollView)
	//{{AFX_MSG_MAP(CMemView)
	ON_WM_DESTROY()
	ON_WM_ERASEBKGND()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_KEYDOWN()
	ON_WM_CHAR()
	ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
	ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, OnFilePrintPreview)
	//}}AFX_MSG_MAP
	// Standard printing commands
	ON_COMMAND(ID_FILE_PRINT, CScrollView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, CScrollView::OnFilePrintPreview)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMemView drawing

void CMemView::SizeText()
{
	BOOL once = m_Size.IsRectEmpty();

	GetClientRect(m_Size);

	CDC *pDC = GetDC();
	CFont *font = pDC->SelectObject(m_pFont);

	pDC->DrawText(m_Text, m_TextLength, m_Size, DT_NOPREFIX | DT_LEFT | DT_CALCRECT | DT_NOCLIP);

	pDC->SelectObject(font);
	ReleaseDC(pDC);

	m_Size.right = m_Size.left + VisibleCharPerLine() * m_Width;

	SetScrollSizes(MM_TEXT, m_Size.Size(), sizeDefault, CSize(m_Width, m_Height));
}

void SetColors(CDC *pDC, BOOL& flipflop, BOOL set)
{
	if (flipflop ^ set) {
		flipflop = !flipflop;

		pDC->SetBkColor(::GetSysColor(flipflop ? COLOR_WINDOW : COLOR_HIGHLIGHT));
		pDC->SetTextColor(flipflop ? ::GetSysColor(COLOR_WINDOWTEXT) : PALETTERGB(255, 255, 255));
		}
}

void CMemView::OnDraw(CDC* pDC)
{
	// TODO: add draw code here
	CFont *font = pDC->SelectObject(m_pFont);

	int height = 0;
	LPSTR text = m_Text;
	CRect clip(CPoint(0, 0), CSize(m_Size.Width(), m_Height));
	CRgn clipRgn;
	clipRgn.CreateRectRgnIndirect(clip);
	CRgn selIntersect;
	selIntersect.CreateRectRgn(0, 0, 0, 0);
	BOOL start = FALSE, isplain = TRUE;
	int Length = m_TextLength < CharPerLine() ? CharPerLine() : m_TextLength + 2;

	for (short i = 0; i < Length / CharPerLine(); i++) 
	{
		if (pDC->RectVisible(clip)) {
			start = TRUE;
			selIntersect.CombineRgn(&clipRgn, &m_SelRgn, RGN_AND);
			CRect intersect;
			selIntersect.GetRgnBox(intersect);

			if (intersect != clip) {
				CRect sub;

				sub.SubtractRect(clip, intersect);
				SetColors(pDC, isplain, TRUE);
				pDC->ExcludeClipRect(&intersect);
				pDC->ExtTextOut(0, height, ETO_CLIPPED, sub, text, VisibleCharPerLine(), NULL);
				pDC->SelectClipRgn(NULL);
				}

			if (!intersect.IsRectEmpty()) {
				SetColors(pDC, isplain, m_SelStart == m_SelEnd);
				pDC->ExtTextOut(0, height, ETO_OPAQUE | ETO_CLIPPED, intersect, text, VisibleCharPerLine(), NULL);
				}
			}
		else if (start)
			break;

		height += m_Height;
		text += CharPerLine();
		clip.OffsetRect(0, m_Height);
		clipRgn.OffsetRgn(0, m_Height);
	}
	SetColors(pDC, isplain, TRUE);
	pDC->SelectObject(font);
}

/////////////////////////////////////////////////////////////////////////////
// CMemView diagnostics

#ifdef _DEBUG
void CMemView::AssertValid() const
{
	CScrollView::AssertValid();
}

void CMemView::Dump(CDumpContext& dc) const
{
	CScrollView::Dump(dc);
}

CSxbmemDoc* CMemView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSxbmemDoc)));
	return (CSxbmemDoc*)m_pDocument;
}
#endif //_DEBUG

// CSxbmemView printing
/////////////////////////////////////////////////////////////////////////////

static UINT AFXAPI EndOfLine(LPCTSTR lpszText, UINT nLen, UINT nIndex)
{
	ASSERT(AfxIsValidAddress(lpszText, nLen, FALSE));
	LPCTSTR lpsz = lpszText + nIndex;
	LPCTSTR lpszStop = lpszText + nLen;
#ifndef _MAC
	while (lpsz < lpszStop && *lpsz != '\r')
#else
	while (lpsz < lpszStop && *lpsz != '\n')
#endif
		++lpsz;
	return (UINT)(lpsz - lpszText);
}

static UINT AFXAPI NextLine(LPCTSTR lpszText, UINT nLen, UINT nIndex)
{
	ASSERT(AfxIsValidAddress(lpszText, nLen, FALSE));
	LPCTSTR lpsz = lpszText + nIndex;
	LPCTSTR lpszStop = lpszText + nLen;
#ifndef _MAC
	while (lpsz < lpszStop && *lpsz == '\r')
		++lpsz;
#endif
	if (lpsz < lpszStop && *lpsz == '\n')
		++lpsz;
	return (UINT)(lpsz - lpszText);
}

static UINT AFXAPI
ClipLine(CDC* pDC, int aCharWidths[256], int cxLine, int nTabStop,
	LPCTSTR lpszText, UINT nIndex, UINT nIndexEnd)
{
	ASSERT_VALID(pDC);
	ASSERT(nIndex < nIndexEnd);
	ASSERT(AfxIsValidAddress(lpszText, nIndexEnd, FALSE));
	UNUSED_ALWAYS(nTabStop);    // unused in Mac build

	TEXTMETRIC tm;
	::GetTextMetrics(pDC->m_hDC, &tm);

	// make an initial guess on the number of characters that will fit
	int cx = 0;
	LPCTSTR lpszStart = lpszText + nIndex;
	LPCTSTR lpszStop = lpszText + nIndexEnd;
	LPCTSTR lpsz = lpszStart;
	while (lpsz < lpszStop)
	{
#ifndef _MAC
		if (*lpsz == '\t')
			cx += nTabStop - (cx % nTabStop);
		else
#endif
		{
#ifdef _UNICODE
			if (*lpsz <= 0xFF)
				cx += aCharWidths[(BYTE)*lpsz];
			else
				cx += tm.tmAveCharWidth;
#else //_UNICODE
			if (TRUE /*_afxDBCS*/ && _istlead(*lpsz))
			{
				++lpsz;
				cx += tm.tmAveCharWidth;
			}
			else
				cx += aCharWidths[(BYTE)*lpsz];
#endif //!_UNICODE
		}
		++lpsz;
		if (cx > cxLine)
			break;
	}

	// adjust for errors in the guess
#ifndef _MAC
	cx = (int)(pDC->GetTabbedTextExtent(lpszStart, (int)(lpsz-lpszStart), 1, &nTabStop).cx);
#else
	cx = pDC->GetTextExtent(lpszStart, lpsz-lpszStart).cx;
#endif
	if (cx > cxLine)
	{
		// remove characters until it fits
		do
		{
			ASSERT(lpsz != lpszStart);
			if (TRUE /*_afxDBCS*/)
				lpsz = _tcsdec(lpszStart, lpsz);
			else
				--lpsz;
#ifndef _MAC
			cx = (int)(pDC->GetTabbedTextExtent(lpszStart, (int)(lpsz-lpszStart), 1, &nTabStop).cx);
#else
			cx = pDC->GetTextExtent(lpszStart, lpsz-lpszStart).cx;
#endif
		} while (cx > cxLine);
	}
	else if (cx < cxLine)
	{
		// add characters until it doesn't fit
		while (lpsz < lpszStop)
		{
			lpsz = _tcsinc(lpsz);
			ASSERT(lpsz <= lpszStop);
#ifndef _MAC
			cx = (int)(pDC->GetTabbedTextExtent(lpszStart, (int)(lpsz-lpszStart), 1, &nTabStop).cx);
#else
			cx = pDC->GetTextExtent(lpszStart, lpsz-lpszStart).cx;
#endif
			if (cx > cxLine)
			{
				if (TRUE /*_afxDBCS*/)
					lpsz = _tcsdec(lpszStart, lpsz);
				else
					--lpsz;
				break;
			}
		}
	}

	// return index of character just past the last that would fit
	return (UINT)(lpsz - lpszText);
}

BOOL CMemView::OnPreparePrinting(CPrintInfo* pInfo)
{
	// default preparation
	return DoPreparePrinting(pInfo);
}

void CMemView::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo)
{
	// TODO: add extra initialization before printing
	ASSERT_VALID(this);
	ASSERT_VALID(pDC);
	// initialize page start vector
	ASSERT(m_aPageStart.GetSize() == 0);
	m_aPageStart.Add(0);
	ASSERT(m_aPageStart.GetSize() > 0);

	m_PrintText = m_Text;

	if (m_hPrinterFont == NULL)
	{
		// get current screen font object metrics
		CFont* pFont = GetFont();
		LOGFONT lf;
		LOGFONT lfSys;
		if (pFont == NULL)
			return;
		VERIFY(pFont->GetObject(sizeof(LOGFONT), &lf));
		VERIFY(::GetObject(::GetStockObject(SYSTEM_FONT), sizeof(LOGFONT),
			&lfSys));
		if (lstrcmpi((LPCTSTR)lf.lfFaceName, (LPCTSTR)lfSys.lfFaceName) == 0)
			return;

		// map to printer font metrics
		HDC hDCFrom = ::GetDC(NULL);
		lf.lfHeight = ::MulDiv(lf.lfHeight, pDC->GetDeviceCaps(LOGPIXELSY),
			::GetDeviceCaps(hDCFrom, LOGPIXELSY));
		lf.lfWidth = ::MulDiv(lf.lfWidth, pDC->GetDeviceCaps(LOGPIXELSX),
			::GetDeviceCaps(hDCFrom, LOGPIXELSX));
		::ReleaseDC(NULL, hDCFrom);

		// create it, if it fails we just use the printer's default.
		m_hMirrorFont = ::CreateFontIndirect(&lf);
		m_hPrinterFont = m_hMirrorFont;
	}
	ASSERT_VALID(this);
}

void CMemView::OnEndPrinting(CDC* pDC, CPrintInfo* pInfo)
{
	// TODO: add cleanup after printing
	ASSERT_VALID(this);

	m_aPageStart.RemoveAll();
	if (m_hMirrorFont != NULL && m_hPrinterFont == m_hMirrorFont)
	{
		::DeleteObject((HGDIOBJ*)&m_hMirrorFont);
		m_hPrinterFont = NULL;
	}

	m_PrintText = NULL;
}

void CMemView::OnPrint(CDC* pDC, CPrintInfo *pInfo)
{
	// TODO: add code to print the controls
	ASSERT_VALID(this);
	ASSERT_VALID(pDC);
	ASSERT(pInfo != NULL);
	ASSERT(pInfo->m_bContinuePrinting);

	CFont* pOldFont = NULL;
	if (m_hPrinterFont != NULL)
		pOldFont = pDC->SelectObject(CFont::FromHandle(m_hPrinterFont));
	pDC->SetBkMode(TRANSPARENT);

	UINT nPage = pInfo->m_nCurPage;
	ASSERT(nPage <= (UINT)m_aPageStart.GetSize());
	UINT nIndex = m_aPageStart[nPage-1];

	// print as much as possible in the current page.
	nIndex = PrintInsideRect(pDC, pInfo->m_rectDraw, nIndex, m_TextLength);

	if (pOldFont != NULL)
		pDC->SelectObject(pOldFont);

	// update pagination information for page just printed
	if (nPage == (UINT)m_aPageStart.GetSize())
	{
		if (nIndex < (UINT)m_TextLength)
			m_aPageStart.Add(nIndex);
	}
	else
	{
		ASSERT(nPage+1 <= (UINT)m_aPageStart.GetSize());
		ASSERT(nIndex == m_aPageStart[nPage+1-1]);
	}
}

BOOL CMemView::PaginateTo(CDC* pDC, CPrintInfo* pInfo)
	// attempts pagination to pInfo->m_nCurPage, TRUE == success
{
	ASSERT_VALID(this);
	ASSERT_VALID(pDC);

	CRect rectSave = pInfo->m_rectDraw;
	UINT nPageSave = pInfo->m_nCurPage;
	ASSERT(nPageSave > 1);
	ASSERT(nPageSave >= (UINT)m_aPageStart.GetSize());
	VERIFY(pDC->SaveDC() != 0);
	pDC->IntersectClipRect(0, 0, 0, 0);
	pInfo->m_nCurPage = (UINT)(m_aPageStart.GetSize());
	while (pInfo->m_nCurPage < nPageSave)
	{
		ASSERT(pInfo->m_nCurPage == (UINT)m_aPageStart.GetSize());
		OnPrepareDC(pDC, pInfo);
		ASSERT(pInfo->m_bContinuePrinting);
		pInfo->m_rectDraw.SetRect(0, 0,
			pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES));
		pDC->DPtoLP(&pInfo->m_rectDraw);
		OnPrint(pDC, pInfo);
		if (pInfo->m_nCurPage == (UINT)m_aPageStart.GetSize())
			break;
		++pInfo->m_nCurPage;
	}
	BOOL bResult = pInfo->m_nCurPage == nPageSave;
	pDC->RestoreDC(-1);
	pInfo->m_nCurPage = nPageSave;
	pInfo->m_rectDraw = rectSave;
	ASSERT_VALID(this);
	return bResult;
}

void CMemView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
	ASSERT_VALID(this);
	ASSERT_VALID(pDC);
	if (pInfo == NULL)  // overriding OnPaint -- never get this.
		CScrollView::OnPrepareDC(pDC, pInfo);
	else {
		if (pInfo->m_nCurPage > (UINT)m_aPageStart.GetSize() &&
			!PaginateTo(pDC, pInfo))
		{
			// can't paginate to that page, thus cannot print it.
			pInfo->m_bContinuePrinting = FALSE;
		}
		ASSERT_VALID(this);
		}
}

UINT CMemView::PrintInsideRect(CDC* pDC, RECT& rectLayout,
	UINT nIndexStart, UINT nIndexStop)
	// worker function for laying out text in a rectangle.
{
	ASSERT_VALID(this);
	ASSERT_VALID(pDC);
	BOOL bWordWrap = FALSE;

	// get buffer and real starting and ending postions
	UINT nLen = m_TextLength;
	if (nIndexStart >= nLen)
		return nLen;
	LPCTSTR lpszText = m_PrintText;
	if (nIndexStop > nLen)
		nIndexStop = nLen;
	ASSERT(nIndexStart < nLen);

	// calculate text & tab metrics
	TEXTMETRIC tm;
	pDC->GetTextMetrics(&tm);
	int cyChar = tm.tmHeight + tm.tmExternalLeading;
#ifndef _MAC
	int nTabStop = m_nTabStops *
		pDC->GetTabbedTextExtent(_T("\t"), 1, 0, NULL).cx / 8 / 4;
#else
	int nTabStop = pDC->GetTextExtent(_T("\t"), 1).cx;
#endif
	int aCharWidths[256];
	pDC->GetCharWidth(0, 255, aCharWidths);

	int y = rectLayout.top;
	UINT cx = rectLayout.right - rectLayout.left;
	UINT nIndex = nIndexStart;

	VERIFY(pDC->SaveDC() != 0);
	BOOL bLayoutOnly = pDC->IntersectClipRect(&rectLayout) == NULLREGION;

	do
	{
		UINT nIndexEnd = EndOfLine(lpszText, nIndexStop, nIndex);
		if (nIndex == nIndexEnd)
		{
			y += cyChar;
		}
		else if (bWordWrap)
		{
			// word-wrap printing
			do
			{
				UINT nIndexWrap = ClipLine(pDC, aCharWidths,
					cx, nTabStop, lpszText, nIndex, nIndexEnd);
				UINT nIndexWord = nIndexWrap;
				if (nIndexWord != nIndexEnd)
				{
					while (nIndexWord > nIndex &&
					  !isspace(lpszText[nIndexWord]))
					{
						nIndexWord--;
					}
					if (nIndexWord == nIndex)
						nIndexWord = nIndexWrap;
				}
				CRect rect(rectLayout.left, y, rectLayout.right, y+cyChar);
				if (!bLayoutOnly && pDC->RectVisible(rect))
				{
#ifndef _MAC
					pDC->TabbedTextOut(rect.left, y,
						(LPCTSTR)(lpszText+nIndex), nIndexWord-nIndex, 1,
						&nTabStop, rect.left);
#else
					pDC->TextOut(rect.left, y,
						(LPCTSTR)(lpszText+nIndex), nIndexWord-nIndex);
#endif
				}
				y += cyChar;
				nIndex = nIndexWord;
				while (nIndex < nIndexEnd && isspace(lpszText[nIndex]))
					nIndex++;
			} while (nIndex < nIndexEnd && y+cyChar <= rectLayout.bottom);

			nIndexEnd = nIndex;
		}
		else
		{
			// non-word wrap printing (much easier and faster)
			CRect rect(rectLayout.left, y, rectLayout.right, y+cyChar);
			if (!bLayoutOnly && pDC->RectVisible(rect))
			{
				UINT nIndexClip = ClipLine(pDC, aCharWidths, cx, nTabStop,
					lpszText, nIndex, nIndexEnd);
				if (nIndexClip < nIndexEnd)
				{
					if (_istlead(*(lpszText+nIndexClip)))
						nIndexClip++;
					nIndexClip++;
				}
#ifndef _MAC
				pDC->TabbedTextOut(rect.left, y,
					(LPCTSTR)(lpszText+nIndex), nIndexClip-nIndex, 1,
					&nTabStop, rect.left);
#else
				pDC->TextOut(rect.left, y,
					(LPCTSTR)(lpszText+nIndex), nIndexClip-nIndex);
#endif
			}
			y += cyChar;
		}
		nIndex = NextLine(lpszText, nIndexStop, nIndexEnd);
	}
	while (nIndex < nIndexStop && y+cyChar <= rectLayout.bottom);

	pDC->RestoreDC(-1);
	ASSERT_VALID(this);

	rectLayout.bottom = y;
	return nIndex;
}

void CMemView::RefreshMember()
{
	static const char *hex = "0123456789ABCDEF";

	CSxbmemDoc *doc = GetDocument();
	LPSTR NewText = NULL;
	LPSTR OldText = m_Text;
	int   OldLength = m_TextLength;
	DWORD size = doc->m_Size;

	if ( size != 0 ) 
	{
		DWORD Base = doc->m_MemAddress;
		int format = doc->m_Format;
		int numLines = (size - 1 + Base % 16) / 16 + 1;
		LPSTR Line = NewText = (LPSTR)malloc(numLines * CharPerLine() + 1);
		LPBYTE buffer = doc->m_Buffer, bpos = buffer - Base % 16;

		for (DWORD i = Base - Base % 16; i < Base + size; i += 16) {// for each line
			for (int j = 28; j >= 0; j -= 4)
				*Line++ = hex[(i >> j) & 0xF];
			*Line++ = ' ';
			*Line++ = ' ';

			char *asc = Line + 32 + (16 >> format) + 2;

			for (j = 0; j < 16; j += 1 << format) {
				for (short k = (1 << format) - 1; k >= 0; k--)
					if (bpos + k >= buffer && bpos + k < buffer + size) {
						*Line++ = hex[bpos[k] >> 4];
						*Line++ = hex[bpos[k] & 0xF];

						asc[k] = isprint(bpos[k]) ? bpos[k] : '.';
						}
					else {
						*Line++ = ' ';
						*Line++ = ' ';
						asc[k] = ' ';
						}
				bpos += (1 << format);
				asc += (1 << format);
				*Line++ = ' ';
				}
			*Line++ = ' ';
			*Line++ = ' ';
			Line += 16;
			*Line++ = '\r';
			*Line++ = '\n';
			}
		Line[-2] = 0;
	}
	else
	{
		static const char *msg = "No data to display";
		NewText = (LPSTR)malloc( CharPerLine() + 1);
		NewText[CharPerLine()] = '\0';
		strcpy( NewText, msg );
		FillMemory( NewText + strlen( NewText ), CharPerLine() - strlen( NewText ), ' ' );
	}

	m_Text = NewText;
	m_TextLength = (int)(strlen(NewText));

	if (OldText != NULL)
		free(OldText);

	InvalidateRect(m_Size);
	if (OldLength != m_TextLength)
		SizeText();

	RedrawWindow();
	
	if (m_SelEnd > m_TextLength)
		OnLButtonDown(MK_LBUTTON, GetScrollPosition());
}

void CMemView::Refresh() 
{
	if (m_PrintText != NULL)
		return;

	RefreshMember();
}
/////////////////////////////////////////////////////////////////////////////
// CMemView message handlers

void CMemView::OnInitialUpdate() 
{
	// TODO: Add your specialized code here and/or call the base class
	
	CScrollView::OnInitialUpdate();
	if (m_pFont == NULL) {
		CDC *pDC = GetDC();
		m_pFont = new CFont;
		m_pFont->CreatePointFont(100, "Courier", pDC);

		TEXTMETRIC tm;
		pDC->GetTextMetrics(&tm);
		ReleaseDC(pDC);

		m_Width = tm.tmAveCharWidth + 1;
		m_Height = tm.tmAscent;

		((CSplitterWnd *)GetParent())->RecalcLayout();
		RefreshMember();

		CRect rect;
		GetClientRect(rect);

		CRect rectFrame;
		CFrameWnd* pFrame = GetParentFrame();
		ASSERT_VALID(pFrame);
		pFrame->GetWindowRect(rectFrame);
		CSize size = rectFrame.Size(), SBSize;
		GetScrollBarSizes(SBSize);
		size.cx += m_Size.Width() - rect.Width() + SBSize.cx;
		pFrame->SetWindowPos(NULL, 0, 0, size.cx, size.cy, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
		}
}

void CMemView::OnDestroy() 
{
	CScrollView::OnDestroy();
	
	// TODO: Add your message handler code here
	delete m_pFont;	
	m_pFont = NULL;
	free(m_Text);
}

void CMemView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{
	// TODO: Add your specialized code here and/or call the base class
	if (lHint == uavDisplayMemory)
		Refresh();
}

BOOL CMemView::OnEraseBkgnd(CDC* pDC) 
{
	// TODO: Add your message handler code here and/or call default
	CBrush brush;
	brush.CreateSolidBrush(GetSysColor(COLOR_WINDOW));
	FillOutsideRect(pDC, &brush);
	return TRUE;
}


void CMemView::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	m_AddToSel = TRUE;
	if ((nFlags & MK_SHIFT) > 0) {
		OnMouseMove(MK_LBUTTON, point);
		return;
		}

	point += GetScrollPosition();
	point.y = __min(m_Size.bottom - 1, point.y);

	CRect rect;
	GetClientRect(rect);

	int Row = __max(0, point.y - rect.top) / m_Height;
	int Column = __min(VisibleCharPerLine(), __max(0, point.x - rect.left) / m_Width);

	m_SelStart = m_SelEnd = m_PinSel = Row * CharPerLine() + Column;

	InvalidateRgn(&m_SelRgn);

	m_SelRgn.DeleteObject();
	CPoint pt = CPoint(Column * m_Width, Row * m_Height);
	
	CRect caretRect(pt, CSize(::GetSystemMetrics(SM_CXBORDER), m_Height));
	m_SelRgn.CreateRectRgnIndirect(&caretRect);

	pt -= GetScrollPosition();
	SetCaretPos(pt);
	ShowCaret();
	InvalidateRgn(&m_SelRgn);
	RedrawWindow();
}

CPoint CMemView::CharPos(BOOL start)
{
	CPoint pt;
	if (start)
		pt = CPoint(m_SelStart % CharPerLine(), m_SelStart / CharPerLine());
	else
		pt = CPoint(m_SelEnd % CharPerLine(), m_SelEnd / CharPerLine() + 1);
	
	pt.x *= m_Width;
	pt.y *= m_Height;
	return pt;
}

void CMemView::CreateSelectionRegion()
{
	if (m_AddToSel)
		HideCaret();
	m_AddToSel = FALSE;

	CPoint start = CharPos(TRUE);
	CPoint end = CharPos(FALSE);

	InvalidateRgn(&m_SelRgn);

	m_SelRgn.DeleteObject();
	m_SelRgn.CreateRectRgnIndirect(CRect(start, CPoint((VisibleCharPerLine()) * m_Width, end.y)));

	CRect more(CPoint(0, start.y + m_Height), CPoint(start.x, end.y));
	CRgn moreRgn;
	moreRgn.CreateRectRgnIndirect(more);
	m_SelRgn.CombineRgn(&m_SelRgn, &moreRgn, RGN_OR);

	CRect less(CPoint(end.x, end.y - m_Height), CPoint((VisibleCharPerLine()) * m_Width, end.y));
	CRgn lessRgn;
	lessRgn.CreateRectRgnIndirect(less);
	m_SelRgn.CombineRgn(&m_SelRgn, &lessRgn, RGN_DIFF);

	InvalidateRgn(&m_SelRgn);
	RedrawWindow();
}

void CMemView::OnMouseMove(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	if ((nFlags & MK_LBUTTON) > 0) {
		point += GetScrollPosition();

		CRect rect;
		GetClientRect(rect);

		int Row = (point.y - rect.top) / m_Height;
		int Column = __min(VisibleCharPerLine(), (point.x - rect.left) / m_Width);

		int sel = Row * CharPerLine() + Column;
		if (sel < m_PinSel) {
			m_SelStart = sel;
			m_SelEnd = m_PinSel;
			}
		else if (sel > m_PinSel) {
			m_SelStart = m_PinSel;
			m_SelEnd = sel;
			}
		else
			m_SelStart = m_SelEnd = m_PinSel;
		ASSERT(m_SelEnd >= m_SelStart);

		if (m_SelEnd != m_SelStart)
			CreateSelectionRegion();
		else 
			OnLButtonDown(MK_LBUTTON, CPoint(point - GetScrollPosition()));
		}
}

void CMemView::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	for (; m_SelStart >= 0 && !isspace(m_Text[m_SelStart]); m_SelStart--);
	m_SelStart++;
	for (; m_SelEnd < m_TextLength && !isspace(m_Text[m_SelEnd]); m_SelEnd++);
	
	if (m_SelStart != m_SelEnd)
		CreateSelectionRegion();
}

void CMemView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// TODO: Add your message handler code here and/or call default
	CPoint pos = GetCaretPos(), original = pos;

	switch (nChar) {
		case VK_BACK : case VK_LEFT :
			pos.x -= m_Width;
			break;
		case VK_RIGHT : case VK_DELETE :
			pos.x += m_Width;
			break;
		case VK_UP :
			pos.y -= m_Height;
			break;
		case VK_DOWN :
			pos.y += m_Height;
			break;
		default :
			return;
		}

	OnLButtonDown(MK_LBUTTON, pos);
	
	CRect rect, temp, selRect;
	
	GetClientRect(rect);
	rect += GetScrollPosition();
	m_SelRgn.GetRgnBox(selRect);
	if (!temp.IntersectRect(rect, selRect) || temp != selRect)
		ScrollToPosition(CPoint(GetScrollPosition() - original + pos));
}

void CMemView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// TODO: Add your message handler code here and/or call default
	CSxbmemDoc *pDoc = GetDocument();
	int Column = m_SelStart % CharPerLine();
	int RowOffset = m_SelStart / CharPerLine() * 16 - pDoc->m_MemAddress % 16;
	
	const int hexMark = CharPerLine() - 20;
	BYTE OutChar = (BYTE)nChar;
	DWORD Offset = RowOffset;

	if (Column >= 10 && Column < hexMark) {
		nChar = toupper(nChar);
		if (!isxdigit(nChar) || isspace(m_Text[m_SelStart]))
			return;
		Column -= 10;
		int ColumnSize = (2 << pDoc->m_Format) + 1; 
		int nibble = Column - Column / ColumnSize;
		ColumnSize--;
		Offset += (1 + nibble / ColumnSize) * (1 << pDoc->m_Format) - ((nibble/2) % (1 << pDoc->m_Format)) - 1;
						
		OutChar = pDoc->m_Buffer[Offset];
		int newHex = strtoul((LPSTR)&nChar, NULL, 16);
		if ((nibble & 1) == 0)
			OutChar = (OutChar & 0x0F) + (newHex << 4);
		else
			OutChar = (OutChar & 0xF0) + newHex;
		}
	else if (Column >= hexMark + 2 && Column < VisibleCharPerLine())
		Offset += Column - (hexMark + 2);
	else
		return;
	
	DWORD Size = 1;
	DWORD Address = pDoc->m_MemAddress;;
	Address += Offset;
	if (Address >= pDoc->m_MemAddress && Address < pDoc->m_MemAddress + pDoc->m_Size) {
		theApp.SetMemory(Address, &OutChar, &Size);
		pDoc->m_Buffer[Offset] = OutChar;
		RefreshMember();
		OnKeyDown(VK_RIGHT, nRepCnt, nFlags);
		}
}

void CMemView::OnEditCopy() 
{
	// TODO: Add your command handler code here
	if (m_SelEnd > m_SelStart && m_SelEnd < m_TextLength) {
		HGLOBAL glob = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, m_SelEnd - m_SelStart + 1);
		LPSTR text = (LPSTR)GlobalLock(glob);
		strncpy(text, m_Text + m_SelStart, m_SelEnd - m_SelStart);
		GlobalUnlock(glob);

		OpenClipboard();
		SetClipboardData(CF_TEXT, text);
		CloseClipboard();
		}
}

void CMemView::OnUpdateEditCopy(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here
	pCmdUI->Enable(m_SelEnd > m_SelStart && m_SelEnd < m_TextLength);
}

void CMemView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView) 
{
	// TODO: Add your specialized code here and/or call the base class
	
	CScrollView::OnActivateView(bActivate, pActivateView, pDeactiveView);
	if (bActivate) {
		CreateSolidCaret(::GetSystemMetrics(SM_CXBORDER), m_Height);
		m_SelRgn.CopyRgn(&m_DeactiavteRgn);
		if (m_SelStart == m_SelEnd)
			OnLButtonDown(MK_LBUTTON, CharPos(TRUE));
		InvalidateRgn(&m_SelRgn);
		RedrawWindow();
		}
	else {
		CRect rect;

		m_SelRgn.GetRgnBox(rect);
		if (!rect.IsRectEmpty()) {
			::DestroyCaret();
			m_DeactiavteRgn.CopyRgn(&m_SelRgn);
			m_SelRgn.DeleteObject();
			m_SelRgn.CreateRectRgn(0, 0, 0, 0);
			InvalidateRgn(&m_DeactiavteRgn);
			RedrawWindow();
			}
		}
}

void CMemView::OnFilePrintPreview() 
{
	// TODO: Add your command handler code here
	CPrintPreviewState* pState = new CPrintPreviewState;
	if (!DoPrintPreview(AFX_IDD_PREVIEW_TOOLBAR, this, RUNTIME_CLASS(CMyPreviewView), pState))
	{
		TRACE0("Error: DoPrintPreview failed.\n");
		AfxMessageBox(AFX_IDP_COMMAND_FAILURE);
		delete pState;      // preview failed to initialize, delete State now
	}
}

/////////////////////////////////////////////////////////////////////////////
// CMemTree

IMPLEMENT_DYNCREATE(CMemTree, CTreeView)

CMemTree::CMemTree()
{
	m_pFont = NULL;
}

CMemTree::~CMemTree()
{
	delete m_pFont;
}


BEGIN_MESSAGE_MAP(CMemTree, CTreeView)
	//{{AFX_MSG_MAP(CMemTree)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMemTree drawing

void CMemTree::OnDraw(CDC* pDC)
{
	CDocument* pDoc = GetDocument();
	// TODO: add draw code here
}

/////////////////////////////////////////////////////////////////////////////
// CMemTree diagnostics

#ifdef _DEBUG
void CMemTree::AssertValid() const
{
	CTreeView::AssertValid();
}

void CMemTree::Dump(CDumpContext& dc) const
{
	CTreeView::Dump(dc);
}

CSxbmemDoc* CMemTree::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSxbmemDoc)));
	return (CSxbmemDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CMemTree message handlers

BOOL CMemTree::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) 
{
	// TODO: Add your specialized code here and/or call the base class
	dwStyle |= TVS_HASLINES | TVS_HASBUTTONS | TVS_EDITLABELS | TVS_DISABLEDRAGDROP;

	return CWnd::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext);
}

CString Format(DWORD flag, BYTE *buffer, DWORD size)
{
	BOOL sign = (dfSigned & flag) > 0;
	flag &= ~dfSigned;

	char result[256];

	switch (flag) {
		case dfByte : sprintf(result, " %1d", *buffer); break;
		case dfByteHex : sprintf(result, " 0x%1X", *buffer); break;
		case dfChar : sprintf(result, " %c", *buffer); break;
		case dfMaskChar : buffer[0] &= 0x7F; sprintf(result, " %c", *buffer); break;

		case dfShort : sprintf(result, " %d", *((WORD *)buffer)); break;
		case dfShortHex : sprintf(result, " 0x%04X", *((WORD *)buffer)); break;

		case dfLong : sprintf(result, " %ld", *((DWORD *)buffer)); break;
		case dfLongHex : sprintf(result, " 0x%08X", *((DWORD *)buffer)); break;

		case dfString : sprintf(result, " %s", buffer); break;

		case dfPadding : sprintf(result, " 0x%*X", size, *buffer); break;
		}

	return result;
}

void CMemTree::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{
	// TODO: Add your specialized code here and/or call the base class
	if (lHint == uavDisplayMemory) {
		CSxbmemDoc *doc = GetDocument();

		const CDef *def = doc->m_Def;
		CTreeCtrl& tree = GetTreeCtrl();

		HTREEITEM item = tree.GetRootItem();
		item = tree.GetChildItem(item);
		for (short i = 0; i < def->m_Members.GetSize(); i++) {
			CElement *elem = def->m_Members[i];

			LONG lp = (LONG)(tree.GetItemData(item));
			DWORD size = HIWORD(lp);
			int offset = LOWORD(lp);

			CString txt = elem->m_Name + Format(elem->m_Flags, doc->m_Buffer + offset, size);

			tree.SetItemText(item, txt);
			item = tree.GetNextSiblingItem(item);
			}
		}
}

void CMemTree::OnInitialUpdate() 
{
	CTreeView::OnInitialUpdate();
	
	// TODO: Add your specialized code here and/or call the base class
	if (m_pFont == NULL) {
		CDC *pDC = GetDC();
		m_pFont = new CFont;
		m_pFont->CreatePointFont(100, "Courier", pDC);
		ReleaseDC(pDC);
		}
	CTreeCtrl& tree = GetTreeCtrl();
	tree.SetFont(m_pFont);

	CSxbmemDoc *doc = GetDocument();

	const CDef *def = doc->m_Def;

	tree.DeleteAllItems();
	HTREEITEM parent = tree.InsertItem(doc->m_DefName);
	
	for (short i = 0; i < def->m_Members.GetSize(); i++) {
		CElement *elem = def->m_Members[i];

		DWORD lp = (elem->m_Size << 16) + elem->m_Offset;
		CString txt = elem->m_Name + Format(elem->m_Flags, doc->m_Buffer + elem->m_Offset, elem->m_Size);

		HTREEITEM item = tree.InsertItem(txt, parent);
		tree.SetItemData(item, lp);
		}

	tree.Expand(parent, TVE_EXPAND);
}

/////////////////////////////////////////////////////////////////////////////
// CMyPreviewView

IMPLEMENT_DYNCREATE(CMyPreviewView, CPreviewView)

CMyPreviewView::CMyPreviewView()
{
}

CMyPreviewView::~CMyPreviewView()
{
}


BEGIN_MESSAGE_MAP(CMyPreviewView, CPreviewView)
	//{{AFX_MSG_MAP(CMyPreviewView)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMyPreviewView diagnostics

#ifdef _DEBUG
void CMyPreviewView::AssertValid() const
{
	CPreviewView::AssertValid();
}

void CMyPreviewView::Dump(CDumpContext& dc) const
{
	CPreviewView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CMyPreviewView message handlers

void CMyPreviewView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{
	// TODO: Add your specialized code here and/or call the base class
	if (lHint == 0)
		CPreviewView::OnUpdate(pSender, lHint, pHint);
}
