/***************************************************************************
**
**    $Header:   D:/EP196/SRC/LOG/SRCDLL/SRCVIEW.CPP   1.0   15 Aug 1997 14:03:38   ZJRD  $
**
**    $Log:   D:/EP196/SRC/LOG/SRCDLL/SRCVIEW.CPP  $
** 
**    Rev 1.0   15 Aug 1997 14:03:38   ZJRD
** Initial revision.
** 
**    Rev 1.1.7.0   19 Jun 1997 14:49:00   ZJRD
** 1.0c
** 
****************************************************************************/

// srcview.cpp : implementation file
//

#include "stdafx.h"
#include "srcdll.h"

#include "srcgbl.h"
#include "srcaddr.h"
#include "srcdata.h"
#include "srctext.h"
#include "srcbar.h"
#include "srcfrm.h"
#include "brwfrm.h"
#include "srcdoc.h"
#include "srcview.h"
#include "srcsht.h"
#include "srcload.h"
#include "srcsrch.h"
#include "srcfrom.h"
#include "srcdat.h"
#include "bptexp.h"
#include "bptdata.h"
#include "srcpass.h"
#include "srcfunc.h"

#include "abiextfn.h"
#include "uicom2.h"

#include <ctype.h>

IMPLEMENT_DYNCREATE(CSourceView, CTextView)

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// Static variable

static char BASED_CODE szC[] = ".C";
static char BASED_CODE szCPP[] = ".CPP";
static char BASED_CODE szTitle[] = "...";
static char BASED_CODE szTab[] = " ";
static char BASED_CODE szFooter[] = "Page %d of %d";
static char BASED_CODE szTipEqu[] = " = ";
static char BASED_CODE szTipNil[] = "???";
static char BASED_CODE szLineFormat[] = " %5d ";
static char BASED_CODE szLoadAddr[] = " ~ ";

/////////////////////////////////////////////////////////////////////////////
// Global function

IsTestKey(const WORD wTestKey)
{
	// Abort by ESC
    MSG msg;
    if ( ::PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE|PM_NOYIELD) ) {
        ::PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE|PM_NOYIELD);
        if ( msg.message == WM_KEYDOWN && msg.wParam == wTestKey ) {
        	return TRUE;
        }
    }
    
    return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CSourceView

CSourceView::CSourceView()
	: m_bLine(TRUE), m_bSymbolic(::bSrcSymbolic), m_bSyntaxColor(TRUE),
	m_sizeScroll(0,0), m_rectClient(0,0,0,0), m_sizeClient(0,0),
	m_nOldPCLine(-1), m_nPCLine(-1), m_nPCStartCol(-1), m_nPCEndCol(-1),
	m_nDrawBP(bpNo), m_BP(::MEM_UNDEFINED, 0),
	m_strMarkToken(""), m_ptMarkToken(-1, -1), m_strSearchToken(""),
	m_uTimerID(0), m_strTip(""), m_ptTip(-1, -1),
	m_bCursorLink(FALSE), m_CursorLinkAddr(0,0)
{
	// Load resource from this DLL
	HINSTANCE hInstEXE = AfxGetResourceHandle();
	AfxSetResourceHandle(::SrcGetDLLHandle());

	// Load bitmap
    m_bmpBP.LoadBitmap(IDB_BP);
    m_bmpBPPC.LoadBitmap(IDB_BPPC);
    m_bmpPC.LoadBitmap(IDB_PC);
    m_bmpLink.LoadBitmap(IDB_LINK);

    // Restore the old resource chain
	AfxSetResourceHandle(hInstEXE);

	// Get Min & Max PC
	::SrcGetPCRange(m_dwMinPC, m_dwMaxPC);

	// Initial
	m_nBitWidth = 2 + 1;
	m_nNumWidth = ::SrcGetAddrWidth() + 1;
	m_nAddrWidth = m_nNumWidth;
	m_nObjWidth = ::SrcGetMaxByte() * 2 + 1;

	m_ptCaret = CPoint(m_nBitWidth + m_nNumWidth, 0);
	m_ptOldCaret = CPoint(m_nBitWidth + m_nNumWidth, 0);
}

CSourceView::~CSourceView()
{
    // Delete bitmap
    m_bmpBP.DeleteObject();
    m_bmpBPPC.DeleteObject();
    m_bmpPC.DeleteObject();
    m_bmpLink.DeleteObject();
}

BEGIN_MESSAGE_MAP(CSourceView, CTextView)
	//{{AFX_MSG_MAP(CSourceView)
	ON_WM_CREATE()
	ON_WM_SYSKEYDOWN()
	ON_WM_HSCROLL()
	ON_WM_VSCROLL()
	ON_WM_SIZE()
	ON_UPDATE_COMMAND_UI(ID_VIEW_LINENUMBERING, OnUpdateViewLinenumbering)
	ON_COMMAND(ID_VIEW_LINENUMBERING, OnViewLinenumbering)
	ON_UPDATE_COMMAND_UI(ID_VIEW_SYNTAXCOLORING, OnUpdateViewSyntaxcoloring)
	ON_COMMAND(ID_VIEW_SYNTAXCOLORING, OnViewSyntaxcoloring)
	ON_UPDATE_COMMAND_UI(ID_VIEW_SYMBOLICDISASSEMBLY, OnUpdateViewSymbolicdisassembly)
	ON_COMMAND(ID_VIEW_SYMBOLICDISASSEMBLY, OnViewSymbolicdisassembly)
	ON_UPDATE_COMMAND_UI(ID_VIEW_ASSEMBLE, OnUpdateViewAssemble)
	ON_COMMAND(ID_VIEW_ASSEMBLE, OnViewAssemble)
	ON_UPDATE_COMMAND_UI(ID_VIEW_MIXED, OnUpdateViewMixed)
	ON_COMMAND(ID_VIEW_MIXED, OnViewMixed)
	ON_UPDATE_COMMAND_UI(ID_VIEW_SOURCE, OnUpdateViewSource)
	ON_COMMAND(ID_VIEW_SOURCE, OnViewSource)
	ON_WM_KEYDOWN()
	ON_WM_SETFOCUS()
	ON_WM_LBUTTONDOWN()
	ON_WM_RBUTTONDOWN()
	ON_WM_LBUTTONDBLCLK()
	ON_COMMAND(ID_OPTIONS_LOADOPTION, OnOptionsLoadoption)
	ON_COMMAND(ID_OPTIONS_SOURCEEXTENSIONNAME, OnOptionsSourceextensionname)
	ON_UPDATE_COMMAND_UI(ID_FILE_BROWSEMODULE, OnUpdateFileBrowsemodule)
	ON_COMMAND(ID_FILE_BROWSEMODULE, OnFileBrowsemodule)
	ON_COMMAND(ID_OPTIONS_SOURCEPATH, OnOptionsSourcepath)
	ON_COMMAND(ID_FILE_LOAD, OnFileLoad)
	ON_COMMAND(ID_RUN_BREAKPOINT, OnRunBreakpoint)
	ON_UPDATE_COMMAND_UI(ID_EDIT_SEARCH, OnUpdateEditSearch)
	ON_COMMAND(ID_EDIT_SEARCH, OnEditSearch)
	ON_UPDATE_COMMAND_UI(ID_EDIT_SEARCHNEXT, OnUpdateEditSearchnext)
	ON_COMMAND(ID_EDIT_SEARCHNEXT, OnEditSearchnext)
	ON_COMMAND(ID_GROUP_SOURCE, OnGroupSource)
	ON_UPDATE_COMMAND_UI(ID_FILE_LOADINFO, OnUpdateFileLoadinfo)
	ON_COMMAND(ID_FILE_LOADINFO, OnFileLoadinfo)
	ON_UPDATE_COMMAND_UI(ID_FILE_PREVMODULE, OnUpdateFilePrevmodule)
	ON_COMMAND(ID_FILE_PREVMODULE, OnFilePrevmodule)
	ON_UPDATE_COMMAND_UI(ID_FILE_NEXTMODULE, OnUpdateFileNextmodule)
	ON_COMMAND(ID_FILE_NEXTMODULE, OnFileNextmodule)
	ON_COMMAND(ID_EDIT_BROWSEFROM, OnEditBrowsefrom)
	ON_WM_MOUSEMOVE()
	ON_WM_TIMER()
	ON_WM_DESTROY()
	ON_WM_KILLFOCUS()
	ON_WM_NCHITTEST()
	ON_COMMAND(ID_FILE_PRINT, OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, OnFilePrintPreview)
	ON_COMMAND(ID_FILE_PRINT_SETUP, OnFilePrintSetup)
	ON_WM_SETCURSOR()
	ON_COMMAND(ID_SHIFTF10, OnShiftf10)
	ON_COMMAND(ID_RUN_JUMP, OnRunJump)
	ON_COMMAND(ID_LOCAL_WARCHVAR, OnLocalWarchvar)
	ON_COMMAND(ID_LOCAL_INSPECTSOURCE, OnLocalInspectsource)
	ON_COMMAND(ID_LOCAL_SHOWLOADADDR, OnLocalShowloadaddr)
	ON_COMMAND(ID_LOCAL_SETBP, OnLocalSetbp)
	ON_COMMAND(ID_LOCAL_CLEARBP, OnLocalClearbp)
	ON_COMMAND(ID_LOCAL_SEARCHVAR, OnLocalSearchvar)
	ON_COMMAND(ID_RUN_GOTOCURSOR, OnRunGotocursor)
	ON_COMMAND(ID_VIEW_SOURCEMODE, OnViewSourcemode)
	ON_COMMAND(ID_TOGGLE_HINTS, OnToggleHints)
	ON_UPDATE_COMMAND_UI(ID_TOGGLE_HINTS, OnUpdateToggleHints)
	ON_UPDATE_COMMAND_UI(ID_EDIT_BROWSEFROM, OnUpdateEditBrowsefrom)
	ON_UPDATE_COMMAND_UI(ID_FILE_LOAD, OnUpdateFileLoad)
	ON_UPDATE_COMMAND_UI(ID_LOCAL_CLEARBP, OnUpdateLocalClearbp)
	ON_UPDATE_COMMAND_UI(ID_LOCAL_SETBP, OnUpdateLocalSetbp)
	ON_UPDATE_COMMAND_UI(ID_RUN_GOTOCURSOR, OnUpdateRunGotocursor)
	ON_UPDATE_COMMAND_UI(ID_VIEW_SOURCEMODE, OnUpdateViewSourcemode)
	ON_UPDATE_COMMAND_UI(ID_GROUP_SOURCE, OnUpdateGroupSource)
	ON_UPDATE_COMMAND_UI(ID_LOCAL_INSPECTSOURCE, OnUpdateLocalInspectsource)
	ON_UPDATE_COMMAND_UI(ID_LOCAL_SEARCHVAR, OnUpdateLocalSearchvar)
	ON_UPDATE_COMMAND_UI(ID_LOCAL_SHOWLOADADDR, OnUpdateLocalShowloadaddr)
	ON_UPDATE_COMMAND_UI(ID_LOCAL_WARCHVAR, OnUpdateLocalWarchvar)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_LOADOPTION, OnUpdateOptionsLoadoption)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_SOURCEEXTENSIONNAME, OnUpdateOptionsSourceextensionname)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_SOURCEPATH, OnUpdateOptionsSourcepath)
	ON_UPDATE_COMMAND_UI(ID_RUN_BREAKPOINT, OnUpdateRunBreakpoint)
	ON_UPDATE_COMMAND_UI(ID_RUN_JUMP, OnUpdateRunJump)
	ON_COMMAND(ID_RUN_JUMPTOCURSOR, OnRunJumptocursor)
	ON_UPDATE_COMMAND_UI(ID_RUN_JUMPTOCURSOR, OnUpdateRunJumptocursor)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSourceView common routine

BOOL CSourceView::GetCpuStatus() const
{
	// Check CPU status
	DWORD dwStatus;
	if ( ICE_OK != ::emuGetCpuStatus(&dwStatus) || (dwStatus & 0x10) ) {
		return FALSE;
	}
	
	return TRUE;
}

void CSourceView::UpdateSourceWindow(const int nMode)
{
	// Close this view if ABI error
	if ( !GetPC() ) {
		GetParent()->SendMessage(WM_CLOSE);
		return;
	}
	
	static int nLastHWindow = -1;
	
	
	// Dispatch update mode
	if ( ::srcOpen == nMode ) {
		// Open Source window
		CString strModulePath;
		SYM_DESCRIPTOR dwModuleDesc;
		if ( IsLoadSource() && IsPCHasSource() && ::SrcGetModule(m_PC, strModulePath, dwModuleDesc) ) {
			((CSourceDocument*)GetDocument())->SetMode(::modSrc);
			// Add to module descriptor list
			AddModuleDepth(dwModuleDesc);

		    // Using another module
			SetModulePath(strModulePath);
			DisplaySourceContent(CSourceDocument::getSrc);
			SendMessage(WM_VSCROLL, SB_TOP);
		}
		else {
			((CSourceDocument*)GetDocument())->SetMode(::modAsm);
			SetAsmRange(m_PC);
			DisplaySourceContent(CSourceDocument::getAsm);
		}
		// Scroll to top
		SendMessage(WM_HSCROLL, SB_LEFT);
		m_ptCaret = CPoint(GetLeftSpace(), 0);
		MoveCaretPos();
	}
	else if ( ::srcUpdate == nMode ) {
		// Update after Fill, Copy...
		if ( GetMode() != ::modSrc ) {
			// Refresh ASM code
			DisplaySourceContent(CSourceDocument::getAsm);

			// Scroll to top
			SendMessage(WM_HSCROLL, SB_LEFT);
			m_ptCaret = CPoint(GetLeftSpace(), 0);
			MoveCaretPos();
		}
	}
	else if ( ::srcReset == nMode  || ::srcStep == nMode) {
		// Update according to PC after Step, Go...

		int nCurWindow = ::GetCurHWindow();
		
		if (nCurWindow != nLastHWindow){
			if ( GetMode() != ::modSrc ) {
				// Refresh ASM code
				DisplaySourceContent(CSourceDocument::getAsm);
	
				// Scroll to top
				SendMessage(WM_HSCROLL, SB_LEFT);
				m_ptCaret = CPoint(GetLeftSpace(), 0);
				MoveCaretPos();
			}
		}
		
		UpdateToPC(nMode);
	}             
	else if ( ::srcBrowse == nMode ) {
		// Browse a module
		CString strModulePath;
		SYM_DESCRIPTOR dwModuleDesc;
		if ( IsLoadSource() && GetModule(strModulePath, dwModuleDesc) ) {
			((CSourceDocument*)GetDocument())->SetMode(::modSrc);
			if ( 0 == ::nSrcModuleDepth || ((CSourceDocument*)GetDocument())->GetPath() != strModulePath ) {
			    // Using another module
				SetModulePath(strModulePath);
				DisplaySourceContent(CSourceDocument::getSrc);
			}
			else {
				SetScrollSizes();
				InvalidateRect(NULL);
			}

			// Scroll to top
			SendMessage(WM_HSCROLL, SB_LEFT);
			SendMessage(WM_VSCROLL, SB_TOP);
			m_ptCaret = CPoint(GetLeftSpace(), 0);
			MoveCaretPos();
		}
	}
	else if ( ::srcBp == nMode ) {
		// Redraw BP column
		InvalidateRect(CRect(0, 0, m_nBitWidth*m_sizeFont.cx, m_sizeClient.cy*m_sizeFont.cy));
	}
	else {
		ASSERT(FALSE);
		return;
	}

	// Update window at once
	UpdateWindow();
}

void CSourceView::UpdateBrowseWindow(const int nMode)
{
	// Close this view if ABI error
	if ( !GetPC() ) {
		GetParent()->SendMessage(WM_CLOSE);
		return;
	}
	
	// Dispatch update mode
	if ( ::brwOpen == nMode ) {
		// Open Browse window
		CString strModulePath;
		SYM_DESCRIPTOR dwModuleDesc;
		if ( IsLoadSource() && IsPCHasSource() && ::SrcGetModule(m_PC, strModulePath, dwModuleDesc) ) {
			((CSourceDocument*)GetDocument())->SetMode(::modSrc);
			// Add to module descriptor list
			AddModuleDepth(dwModuleDesc);

		    // Using another module
			SetModulePath(strModulePath);
			DisplaySourceContent(CSourceDocument::getSrc);
			SendMessage(WM_VSCROLL, SB_TOP);
		}
		else {
			((CSourceDocument*)GetDocument())->SetMode(::modAsm);
			SetAsmRange(m_PC);
			DisplaySourceContent(CSourceDocument::getAsm);
		}
	}
	else if ( ::brwBrowse == nMode ) {
		// Browse a module
		CString strModulePath;
		SYM_DESCRIPTOR dwModuleDesc;
		if ( IsLoadSource() && GetModule(strModulePath, dwModuleDesc) ) {
			((CSourceDocument*)GetDocument())->SetMode(::modSrc);
			if ( 0 == ::nSrcModuleDepth || ((CSourceDocument*)GetDocument())->GetPath() != strModulePath ) {
			    // Using another module
				SetModulePath(strModulePath);
				DisplaySourceContent(CSourceDocument::getSrc);

				// Scroll to top
				SendMessage(WM_HSCROLL, SB_LEFT);
				SendMessage(WM_VSCROLL, SB_TOP);
				m_ptCaret = CPoint(GetLeftSpace(), 0);
				MoveCaretPos();
			}
		}
	}
	else if ( ::brwUpdate == nMode ) {
		// Update after Fill, Copy...
		if ( GetMode() != ::modSrc ) {
			// Refresh ASM code
			DisplaySourceContent(CSourceDocument::getAsm);

			// Scroll to top
			SendMessage(WM_HSCROLL, SB_LEFT);
			m_ptCaret = CPoint(GetLeftSpace(), 0);
			MoveCaretPos();
		}
	}
	else if ( ::brwLink == nMode ) {
		InvalidateRect(CRect(0, 0, m_nBitWidth*m_sizeFont.cx, m_sizeClient.cy*m_sizeFont.cy));
	}
	else {
		ASSERT(FALSE);
		return;
	}

	// Update window at once
	UpdateWindow();
}

void CSourceView::UpdateToPC(int nOpMode)
{
	// Update Source window to PC
	int nMode = GetMode();
	if ( ::modAsm == nMode ) {
		// Get PC location line
		GetPCLinePos();
		if ( m_nPCLine >= 0 && m_nPCLine < m_sizeClient.cy ) {
			if (nOpMode == ::srcReset) {
				// Change PC Mark
				((CSourceDocument*)GetDocument())->UpdateDocument();
	
				// Set scroll size
				SetScrollSizes();
	
				// Update the entire view.
				InvalidateRect(NULL);
			}
			else {
				if ( m_nOldPCLine >= 0 && m_nOldPCLine < m_sizeClient.cy ) {
					InvalidateRect(CRect(CPoint(0, m_nOldPCLine*m_sizeFont.cy), CSize(m_rectClient.Width(), m_sizeFont.cy)));
				}
				InvalidateRect(CRect(CPoint(0, m_nPCLine*m_sizeFont.cy), CSize(m_rectClient.Width(), m_sizeFont.cy)));
			}
		}
		else {
			SetAsmRange(m_PC);
			DisplaySourceContent(CSourceDocument::getAsm);
			SendMessage(WM_HSCROLL, SB_LEFT);
			m_ptCaret = CPoint(GetLeftSpace(), 0);
			MoveCaretPos();
		}
	}
	else if ( ::modMix == nMode ) {
		CString strModulePath;
		SYM_DESCRIPTOR dwModuleDesc;
		if ( IsLoadSource() && IsPCHasSource() && ::SrcGetModule(m_PC, strModulePath, dwModuleDesc) ) {
			if ( 0 == ::nSrcModuleDepth || ((CSourceDocument*)GetDocument())->GetPath() != strModulePath ) {
				// Add to module descriptor list
				AddModuleDepth(dwModuleDesc);

			    // Using another module
				SetModulePath(strModulePath);
				DisplaySourceContent(CSourceDocument::getMix);

				GetPCLinePos();
				SendMessage(WM_VSCROLL, SB_THUMBTRACK, MAKELONG(m_nPCLine-1, NULL));

				SendMessage(WM_HSCROLL, SB_LEFT);
				m_ptCaret = CPoint(GetLeftSpace(), 0);
				MoveCaretPos();
			}
			else {
				GetPCLinePos();
				if ( m_nPCLine > m_sizeScroll.cy && m_nPCLine <= m_sizeClient.cy + m_sizeScroll.cy ) {
					// Change PC Mark
					if ( m_nOldPCLine > m_sizeScroll.cy && m_nOldPCLine <= m_sizeClient.cy + m_sizeScroll.cy ) {
						InvalidateRect(CRect(CPoint(0, (m_nOldPCLine-m_sizeScroll.cy-1)*m_sizeFont.cy), CSize(m_rectClient.Width(), m_sizeFont.cy)));
					}
					InvalidateRect(CRect(CPoint(0, (m_nPCLine-m_sizeScroll.cy-1)*m_sizeFont.cy), CSize(m_rectClient.Width(), m_sizeFont.cy)));
				}
				else {
					SendMessage(WM_VSCROLL, SB_THUMBTRACK, MAKELONG(m_nPCLine-1, NULL));

					SendMessage(WM_HSCROLL, SB_LEFT);
					m_ptCaret = CPoint(GetLeftSpace(), 0);
					MoveCaretPos();
				}
			}
		}
		else {
			((CSourceDocument*)GetDocument())->SetMode(::modAsm);
			SetAsmRange(m_PC);
			DisplaySourceContent(CSourceDocument::getAsm);
			SendMessage(WM_HSCROLL, SB_LEFT);
			m_ptCaret = CPoint(GetLeftSpace(), 0);
			MoveCaretPos();
		}
	}
	else {
		CString strModulePath;
		SYM_DESCRIPTOR dwModuleDesc;
		if ( IsLoadSource() && IsPCHasSource() && ::SrcGetModule(m_PC, strModulePath, dwModuleDesc) ) {
			if ( 0 == ::nSrcModuleDepth || ((CSourceDocument*)GetDocument())->GetPath() != strModulePath ) {
				// Add to module descriptor list
				AddModuleDepth(dwModuleDesc);

			    // Using another module
				SetModulePath(strModulePath);
				DisplaySourceContent(CSourceDocument::getSrc);

				GetPCLinePos();

				// Scroll to source line
				CSize size(((CSourceDocument*)GetDocument())->GetTotalSize());
				if ( m_sizeClient.cy >= size.cy ) {
					SendMessage(WM_VSCROLL, SB_TOP);
				}
				else {
					SendMessage(WM_VSCROLL, SB_THUMBTRACK, MAKELONG(m_nPCLine-1, NULL));
				}
				SendMessage(WM_HSCROLL, SB_LEFT);

				m_ptCaret = CPoint(GetLeftSpace(), 0);
				MoveCaretPos();
			}
			else {
				GetPCLinePos();
				if ( m_nPCLine > m_sizeScroll.cy && m_nPCLine <= m_sizeClient.cy + m_sizeScroll.cy ) {
					// Change PC Mark
					if ( m_nOldPCLine > m_sizeScroll.cy && m_nOldPCLine <= m_sizeClient.cy + m_sizeScroll.cy ) {
						InvalidateRect(CRect(CPoint(0, (m_nOldPCLine-m_sizeScroll.cy-1)*m_sizeFont.cy), CSize(m_rectClient.Width(), m_sizeFont.cy)));
					}
					InvalidateRect(CRect(CPoint(0, (m_nPCLine-m_sizeScroll.cy-1)*m_sizeFont.cy), CSize(m_rectClient.Width(), m_sizeFont.cy)));
				}
				else {
					// Scroll to source line
					CSize size(((CSourceDocument*)GetDocument())->GetTotalSize());
					if ( m_sizeClient.cy >= size.cy ) {
						SendMessage(WM_VSCROLL, SB_TOP);
					}
					else {
						SendMessage(WM_VSCROLL, SB_THUMBTRACK, MAKELONG(m_nPCLine-1, NULL));
					}
					SendMessage(WM_HSCROLL, SB_LEFT);

					m_ptCaret = CPoint(GetLeftSpace(), 0);
					MoveCaretPos();
				}
			}
		}
		else {
			((CSourceDocument*)GetDocument())->SetMode(::modAsm);
			SetAsmRange(m_PC);
			DisplaySourceContent(CSourceDocument::getAsm);
			SendMessage(WM_HSCROLL, SB_LEFT);
			m_ptCaret = CPoint(GetLeftSpace(), 0);
			MoveCaretPos();
		}
	}
}

void CSourceView::BrowseFromAddr(const CSourceAddr& Addr)
{
	// SRC mode
	if ( GetMode() == ::modSrc ) {
		// Get module path
		CString strModulePath;
		SYM_DESCRIPTOR dwModuleDesc;
		if ( !::SrcGetModule(Addr, strModulePath, dwModuleDesc) ) {
			((CSourceDocument*)GetDocument())->SetMode(::modAsm);
		}
		else {
			if ( 0 == ::nSrcModuleDepth || ((CSourceDocument*)GetDocument())->GetPath() != strModulePath ) {
				// Add to module descriptor list
				AddModuleDepth(dwModuleDesc);
	
			    // Using another module
				SetModulePath(strModulePath);
				DisplaySourceContent(CSourceDocument::getSrc);
			}

			// Get scrolled line
			int nLine, nStart, nEnd;
			if ( !::SrcAddrToLine(Addr, nLine, nStart, nEnd) ) {
				ASSERT(FALSE);
				return;
			}

			// Scroll to source line
			CSize size(((CSourceDocument*)GetDocument())->GetTotalSize());
			if ( m_sizeClient.cy >= size.cy ) {
				SendMessage(WM_VSCROLL, SB_TOP);
			}
			else {
				SendMessage(WM_VSCROLL, SB_THUMBTRACK, MAKELONG(nLine-2, NULL));
			}
			SendMessage(WM_HSCROLL, SB_LEFT);

			m_ptCaret = CPoint(GetLeftSpace(), 0);
			MoveCaretPos();

			return;
		}
	}

    // MIX mode
	if ( GetMode() == ::modMix ) {
		// Get module path
		CString strModulePath;
		SYM_DESCRIPTOR dwModuleDesc;
		if ( !::SrcGetModule(Addr, strModulePath, dwModuleDesc) ) {
			((CSourceDocument*)GetDocument())->SetMode(::modAsm);
		}
		else {
			if ( 0 == ::nSrcModuleDepth || ((CSourceDocument*)GetDocument())->GetPath() != strModulePath ) {
				// Add to module descriptor list
				AddModuleDepth(dwModuleDesc);
	
			    // Using another module
				SetModulePath(strModulePath);
				DisplaySourceContent(CSourceDocument::getMix);
			}
			
			// Get scrolled line
			int nLine, nStart, nEnd;
			if ( !::SrcAddrToLine(Addr, nLine, nStart, nEnd) ) {
				ASSERT(FALSE);
				return;
			}

			// Scroll to source line
			CSize size(((CSourceDocument*)GetDocument())->GetTotalSize());
			if ( m_sizeClient.cy >= size.cy ) {
				SendMessage(WM_VSCROLL, SB_TOP);
			}
			else {
				SendMessage(WM_VSCROLL, SB_THUMBTRACK, MAKELONG(nLine, NULL));
			}
			SendMessage(WM_HSCROLL, SB_LEFT);

			m_ptCaret = CPoint(GetLeftSpace(), 0);
			MoveCaretPos();

			return;
		}
	}

	// ASM mode
	if ( GetMode() == ::modAsm ) {
		SetAsmRange(Addr);
		DisplaySourceContent(CSourceDocument::getAsm);
		SendMessage(WM_HSCROLL, SB_LEFT);
	}

	// Set caret
	m_ptCaret = CPoint(GetLeftSpace(), 0);
	MoveCaretPos();
}

void CSourceView::AddModuleDepth(const SYM_DESCRIPTOR dwModuleDesc)
{
	// Add to module descriptor list
	::pSourceModuleDescList->AddTail(new CModuleDesc(dwModuleDesc));
	::nSrcModuleIndex++;
	::nSrcModuleDepth++;

	// Update wizard bar
	CWnd* pWnd = GetParent();
	if ( pWnd == ::pSourceWnd ) {
		::SrcUpdateSourceWizardBar(FALSE, FALSE, TRUE, TRUE, FALSE);
	}
	else if ( pWnd == ::pBrowseWnd ) {
		::SrcUpdateBrowseWizardBar(FALSE, FALSE, TRUE, TRUE, FALSE);
	}
	else {
		ASSERT(FALSE);
	}
}

BOOL CSourceView::GetPC()
{
	// Get PC
	if ( !::SrcGetPC(m_PC) ) {
		::SrcDisplayErrorMessage(::errGetPC);
		return FALSE;
	}

	return TRUE;
}

BOOL CSourceView::SetPC()
{
	// Set PC
	if ( !::SrcSetPC(m_PC) ) {
		::SrcDisplayErrorMessage(::errSetPC);
		return FALSE;
	}

	return TRUE;
}

BOOL CSourceView::IsLoadSource() const
{
	// Check from Loader server
	return ( ::SrcIsLoadedSymbol() );
}

BOOL CSourceView::IsInSourceRange(CSourceAddr& Addr, CString& strModulePath, SYM_DESCRIPTOR& dwModuleDesc)
{
	// Get the first address
	CObList* pList = ((CAssemblyData*)((CSourceDocument*)GetDocument())->GetAsmList()->GetHead())->GetList();
	POSITION pos = pList->GetHeadPosition();
	while ( pos ) {
		CAssemblyCode* pObj = (CAssemblyCode*)pList->GetNext(pos);
		if ( ::SrcTextToAddr(pObj->GetAddr(), Addr) ) {
			// Get module path
			if ( ::SrcGetModule(Addr, strModulePath, dwModuleDesc) ) {
				return TRUE;
			}
		}
	}
	
	return FALSE;
}

BOOL CSourceView::IsPCHasSource() const
{
	// PC's module has source?
	return (::SrcIsInSource(m_PC));
}

BOOL CSourceView::GetModule(CString& strModulePath, SYM_DESCRIPTOR& dwModuleDesc)
{
	// Get current browsed module
	if ( !::SrcGetModuleDesc(dwModuleDesc) ) {
		return FALSE;
	}

	CString strModuleName;
	if ( !::SrcGetModuleName(dwModuleDesc, strModuleName) ) {
		return FALSE;
	}
	
	// Get module path
	if ( !::SrcGetModulePath(strModuleName, strModulePath) ) {
//		::SrcDisplayErrorMessage(::errFile);
		return FALSE;
	}

    return TRUE;
}

BOOL CSourceView::IsMarkVariable() const
{
	// Only in source mode
	if ( GetMode() != ::modSrc ) {
		return FALSE;
	}

	// Not loaded
	if ( !IsLoadSource() ) {
		return FALSE;
	}

	// Is searched token?
	if ( !m_strSearchToken.IsEmpty() ) {
		return FALSE;
	}
	
	// Is marked token?
	if ( m_strMarkToken.IsEmpty() ) {
		return FALSE;
	}
	
	// Query from symbol server
	int nLine = m_sizeScroll.cy + 1 + m_ptCaret.y;
	CString strToken('#');
	if ( m_strMarkToken[0] != '#' ) {
		strToken += m_strMarkToken;
	}
	else {
		strToken = m_strMarkToken;
	}

	return (::SrcIsMarkVariable(strToken, nLine));
}

BOOL CSourceView::IsMarkFunction() const
{
	// Only in source mode
	if ( GetMode() != ::modSrc ) {
		return FALSE;
	}

	// Not loaded
	if ( !IsLoadSource() ) {
		return FALSE;
	}

	// Is searched token?
	if ( !m_strSearchToken.IsEmpty() ) {
		return FALSE;
	}
	
	// Is marked token?
	if ( m_strMarkToken.IsEmpty() ) {
		return FALSE;
	}
	
	// Query from symbol server
	CString strToken('#');
	if ( m_strMarkToken[0] != '#' ) {
		strToken += m_strMarkToken;
	}
	else {
		strToken = m_strMarkToken;
	}

	return (::SrcIsMarkFunction(strToken));
}

void CSourceView::SetModulePath(const CString& strModulePath)
{
	// Get full path of the current module
	((CSourceDocument*)GetDocument())->SetPath(strModulePath);

	// ASM module has no syntax color
	int nPos = strModulePath.ReverseFind('.');
	if ( -1 != nPos ) {
		CString strExt(strModulePath.Right(strModulePath.GetLength()-nPos));
		strExt.MakeUpper();
		if ( strExt != ::szC && strExt != ::szCPP ) {
			m_bSyntaxColor = FALSE;
		}
	}
}

void CSourceView::ShowTitle()
{
	// Get full path of the current module
	CString strPath = ((CSourceDocument*)GetDocument())->GetPath();

	// Set window title
	HINSTANCE hInstEXE = AfxGetResourceHandle();
	AfxSetResourceHandle(::SrcGetDLLHandle());

	CString strTitle, str;
	strTitle.LoadString(m_bBrowse ? IDR_BROWSE : IDR_SOURCE);

	switch ( GetMode() ) {
	case ::modAsm:
		str.LoadString(IDS_ASM_TITLE);
		strTitle += str;
		break;
	case ::modMix:
		str.LoadString(IDS_MIXED_TITLE);
		strTitle += str;
		if ( strPath.GetLength() > 30 ) {
			strTitle += ::szTitle;
			strTitle += strPath.Right(30);
		}
		else {
			strTitle += strPath;
		}
		break;
	case ::modSrc:
		str.LoadString(IDS_SOURCE_TITLE);
		strTitle += str;
		if ( strPath.GetLength() > 30 ) {
			strTitle += ::szTitle;
			strTitle += strPath.Right(30);
		}
		else {
			strTitle += strPath;
		}
		break;
	default:
		ASSERT(FALSE);
		break;
	}

	AfxSetResourceHandle(hInstEXE);

	GetDocument()->SetTitle(strTitle);
}

void CSourceView::SetAsmRange(const CSourceAddr& AddrStart) const
{
	// Decide the assembly range
	((CSourceDocument*)GetDocument())->SetAsmRange(AddrStart, m_sizeClient.cy);
}

BOOL CSourceView::GetStartAddr(CSourceAddr& AddrStart)
{
	// Get start address in SRC / MIX mode
	BOOL bOK(FALSE);
	if ( GetMode() == ::modSrc ) {
		// SRC => ASM
		int nLine = m_sizeScroll.cy;
		int nMaxLine = ((CSourceDocument*)GetDocument())->GetSrcList()->GetCount();
		CSourceAddr AddrEnd;
		while ( nLine < nMaxLine ) {
			if ( ::SrcGetLineRange(nLine++, AddrStart, AddrEnd) ) {
				bOK = TRUE;
				break;
			}
		}
	}
	else if ( GetMode() == ::modMix ) {
		// MIX => ASM
		CSourceDocument* pDoc = (CSourceDocument*)GetDocument();
		CObList* pSrcList = pDoc->GetSrcList();
		CObList* pAsmList = pDoc->GetAsmList();

		// Get the list index according to the scroll position
		int nSrcIndex, nAsmIndex;
		GetListIndex(m_sizeScroll.cy, pSrcList, pAsmList, nSrcIndex, nAsmIndex);
		ASSERT(nSrcIndex >= 0 && nAsmIndex >= -1);
		if ( nAsmIndex > -1 ) {
			CObList* pList = ((CAssemblyData*)pAsmList->GetAt(pAsmList->FindIndex(nSrcIndex)))->GetList();
			CString strAddr = ((CAssemblyCode*)pList->GetAt(pList->FindIndex(nAsmIndex)))->GetAddr();
			if ( ::SrcTextToAddr(strAddr, AddrStart) ) {
				bOK = TRUE;
			}
		}
		else {
			int nLine = nSrcIndex;
			int nMaxLine = ((CSourceDocument*)GetDocument())->GetSrcList()->GetCount();
			CSourceAddr AddrEnd;
			while ( nLine < nMaxLine ) {
				if ( ::SrcGetLineRange(nLine++, AddrStart, AddrEnd) ) {
					bOK = TRUE;
					break;
				}
			}
		}
	}
	else {
		// ASM => ASM, OnSize()
		CObList* pList1 = ((CSourceDocument*)GetDocument())->GetAsmList();
		if ( !pList1->IsEmpty() ) {
			CObList* pList2 = ((CAssemblyData*)pList1->GetHead())->GetList();
			if ( !pList2->IsEmpty() ) {
				CString strAddr = ((CAssemblyCode*)pList2->GetHead())->GetAddr();
				if ( ::SrcTextToAddr(strAddr, AddrStart) ) {
					bOK = TRUE;
				}
			}
		}
	}
	
	return bOK;
}

void CSourceView::ScrollAsmCode(UINT nSBCode, const int nPos)
{
	// Get asm list
	CObList* pList1 = ((CSourceDocument*)GetDocument())->GetAsmList();
	if ( !pList1 || pList1->IsEmpty() ) {
		return;
	}
	CObList* pList = ((CAssemblyData*)pList1->GetHead())->GetList();

	CSourceAddr Addr;
	if ( !::SrcTextToAddr(((CAssemblyCode*)pList->GetHead())->GetAddr(), Addr) ) {
		return;
	}
	int yMin, yMax;
	GetScrollRange(SB_VERT, &yMin, &yMax);
    
    // Top or Bottom
	if ( SB_THUMBPOSITION == nSBCode ) {
		if ( nPos == yMin ) {
			nSBCode = SB_TOP;
		}
		else if ( nPos == yMax ) {
			nSBCode = SB_BOTTOM;
		}
	}

	// Scroll in ASM mode
	POSITION pos;
	int nLine = m_sizeClient.cy;
	switch ( nSBCode ) {
	case SB_TOP:
		if ( Addr == m_dwMinPC ) {
			return;
		}
		Addr = CSourceAddr(m_dwMinPC, Addr.GetType());
		break;
	case SB_BOTTOM:
		if ( Addr == m_dwMaxPC || pList->GetCount() < m_sizeClient.cy ) {
			return;
		}
		GetStartAddr(CSourceAddr(m_dwMaxPC, Addr.GetType()), Addr, nLine);
		break;
	case SB_LINEUP:
		if ( Addr == m_dwMinPC ) {
			return;
		}
		GetStartAddr(Addr-1, Addr);
		break;
	case SB_LINEDOWN:
		if ( Addr == m_dwMaxPC || pList->GetCount() < m_sizeClient.cy ) {
			return;
		}
		pos = pList->FindIndex(1);
		if ( !pos || !::SrcTextToAddr(((CAssemblyCode*)pList->GetAt(pos))->GetAddr(), Addr) ) {
			return;
		}
		break;
	case SB_PAGEUP:
		if ( Addr == m_dwMinPC ) {
			return;
		}
		if ( !::SrcTextToAddr(((CAssemblyCode*)pList->GetHead())->GetAddr(), Addr) ) {
			return;
		}
		GetStartAddr(Addr-1, Addr, nLine);
		break;
	case SB_PAGEDOWN:
		if ( Addr == m_dwMaxPC || pList->GetCount() < m_sizeClient.cy ) {
			return;
		}
		if ( !::SrcTextToAddr(((CAssemblyCode*)pList->GetTail())->GetAddr(), Addr) ) {
			return;
		}
		break;
//	case SB_THUMBTRACK:
	case SB_THUMBPOSITION:
		Addr = CSourceAddr(m_dwMinPC+(m_dwMaxPC-m_dwMinPC)*nPos/yMax, Addr.GetType());
		break;
	default:
		return;
	}

	// Update source view
	((CSourceDocument*)GetDocument())->SetAsmRange(Addr, nLine);
	DisplaySourceContent(CSourceDocument::getAsm);
}

void CSourceView::GetStartAddr(const CSourceAddr& AddrBase, CSourceAddr& AddrStart, const nUpLine /* = 1 */)
{
	// Get line up address
	::SrcGetStartAddr(AddrBase, AddrStart, nUpLine);
}

void CSourceView::SetAsmScrollPos()
{
	// According to the first line address
	CObList* pList = ((CAssemblyData*)((CSourceDocument*)GetDocument())->GetAsmList()->GetHead())->GetList();
	CString strAddr = ((CAssemblyCode*)pList->GetHead())->GetAddr();
	CSourceAddr Addr;
	if ( ::SrcTextToAddr(strAddr, Addr) ) {
		int nPos = int((Addr.GetAddr()-m_dwMinPC+1)*100 / (m_dwMaxPC-m_dwMinPC+1));
		SetScrollPos(SB_VERT, nPos);
	}
}

void CSourceView::DisplaySourceContent(const int nAction)
{
	// Check CPU status
    if ( !GetCpuStatus() ) {
		::SrcDisplayErrorMessage(::errEPRun);
		GetParent()->SendMessage(WM_CLOSE);
		return;
	}

	// Show title
	ShowTitle();

	// Update the document content
	((CSourceDocument*)GetDocument())->SetAction(nAction);
	((CSourceDocument*)GetDocument())->UpdateDocument();

	// Set scroll size
	SetScrollSizes();

	// Update the entire view.
	InvalidateRect(NULL);
}

void CSourceView::GetListIndex(const int nStartLine, CObList* pSrcList, CObList* pAsmList, int& nSrcIndex, int& nAsmIndex)
{
	// Query from source list
	int nCount(-1);
	for ( int i(0); i < pSrcList->GetCount(); i++ ) {
		nAsmIndex = -1;
		nSrcIndex = i;
		if ( ++nCount == nStartLine ) {
			return;
		}

		// Query from assembly list
		POSITION pos = pAsmList->FindIndex(i);
		if ( pos && -1 != ((CAssemblyData*)pAsmList->GetAt(pos))->GetLine() ) {
			for ( int j(0); j < ((CAssemblyData*)pAsmList->GetAt(pos))->GetList()->GetCount(); j++ ) {
				nAsmIndex = j;
				if ( ++nCount == nStartLine ) {
					return;
				}
			}
		}
	}
}

void CSourceView::GetPCLinePos()
{
	// ASM: base = 0; MIX & SRC: base = 1
	int nMode = GetMode();
	if ( nMode == ::modSrc ) {
		// SRC mode
		if ( !::SrcAddrToLine(m_PC, m_nPCLine, m_nPCStartCol, m_nPCEndCol) ) {
			m_nPCLine = -1;
			m_nPCStartCol = -1;
			m_nPCEndCol = -1;
		}
	}
	else if ( nMode == ::modMix ) {
		int nLine;
		if ( GetMixedLine(m_PC, nLine) ) {
			m_nPCLine = nLine;
		}
		else {
			m_nPCLine = -1;
		}
	}
	else {
		// ASM mode
		CObList* pAsmList = ((CSourceDocument*)GetDocument())->GetAsmList();
		if ( !pAsmList->IsEmpty() ) {
			CObList* pList = ((CAssemblyData*)pAsmList->GetHead())->GetList();
			int nLine(0);
			POSITION pos = pList->GetHeadPosition();
			while ( pos ) {
				CAssemblyCode* pObj = (CAssemblyCode*)pList->GetNext(pos);
				CSourceAddr Addr;
				if ( !pObj->GetLabel().IsEmpty() ) {
					nLine++;
				}
				if ( ::SrcTextToAddr(pObj->GetAddr(), Addr) && m_PC == Addr ) {
					m_nPCLine = nLine;
					return;
				}
				nLine++;
			}
		}
		m_nPCLine = -1;
	}
}

void CSourceView::MoveCaretPos()
{
	// 1. LBDown
	// 2. LBDblClick
	// 3. KeyDown
	// 4. Search

	// Set the caret position & show it
    if ( GetFocus() == this ) {
        SetCaretPos(CPoint(m_ptCaret.x*m_sizeFont.cx, m_ptCaret.y*m_sizeFont.cy));
    	ShowCaret();
    }
}

/////////////////////////////////////////////////////////////////////////////
// CSourceView BP routines

BOOL CSourceView::LineToAddr(const int nLine, CSourceAddr& Addr)
{
	// Get current line address if has code
	int nMode = GetMode();
	if ( ::modAsm == nMode ) {
		CObList* pList = ((CAssemblyData*)(((CSourceDocument*)GetDocument())->GetAsmList()->GetHead()))->GetList();
		POSITION pos = pList->GetHeadPosition();
		int nCount(0);
		while ( pos ) {
			CAssemblyCode* pObj = (CAssemblyCode*)pList->GetNext(pos);
			if ( !pObj->GetLabel().IsEmpty() ) {
				nCount++;
			}
			if ( nLine == nCount ) {
				return ::SrcTextToAddr(pObj->GetAddr(), Addr);
			}
			else if ( nLine < nCount ) {
				break;
			}
			else {
				nCount++;
			}
		}
	}
	else if ( ::modSrc == nMode ) {
		CSourceAddr AddrEnd;
		return ::SrcGetLineRange(nLine+1, Addr, AddrEnd);
	}
	else {
		// Get the list index according to the scroll position
		CSourceDocument* pDoc = (CSourceDocument*)GetDocument();
		CObList* pSrcList = pDoc->GetSrcList();
		CObList* pAsmList = pDoc->GetAsmList();
		int nSrcIndex, nAsmIndex;
		GetListIndex(nLine, pSrcList, pAsmList, nSrcIndex, nAsmIndex);
		ASSERT(nSrcIndex >= 0 && nAsmIndex >= -1);
		if ( nAsmIndex > -1 ) {
			CObList* pList = ((CAssemblyData*)pAsmList->GetAt(pAsmList->FindIndex(nSrcIndex)))->GetList();
			CString strAddr = ((CAssemblyCode*)pList->GetAt(pList->FindIndex(nAsmIndex)))->GetAddr();
			return ::SrcTextToAddr(strAddr, Addr);
		}
//		else {
//			CSourceAddr AddrEnd;
//			return ::SrcGetLineRange(nSrcIndex+1, Addr, AddrEnd);
//		}
	}
	
	return FALSE;
}

void CSourceView::MarkBP()
{
	// Get address according to (m_ptCaret.y+m_sizeScroll.cy)
	CSourceAddr Addr;
	int nLine = GetMode() == ::modAsm ? m_ptCaret.y : m_ptCaret.y + m_sizeScroll.cy;
	if ( LineToAddr(nLine, Addr) ) {
		// Query from BP list
		m_nDrawBP = bpSet;
		CObList* pList = ::BptGetBpList();
		POSITION pos = pList->GetHeadPosition();
		while ( pos ) {
			POSITION posOld = pos;
			CBpData* pObj = (CBpData*)pList->GetNext(pos);
			if ( pObj->GetAddr() == Addr ) {
				m_nDrawBP = bpClear;
				if ( ::BptClrBp(Addr) ) {
					m_BP = Addr;
					delete pObj;
					pList->RemoveAt(posOld);
				}
				else {
					::SrcDisplayErrorMessage(::errBpClr);
					return;
				}
				break;
			}
		}

		// Add to BP list
		if ( bpSet == m_nDrawBP ) {
			if ( ::BptSetBp(Addr) ) {
				m_BP = Addr;
				pList->AddTail(new CBpData(Addr, "", TRUE));
			}
			else {
				::SrcDisplayErrorMessage(::errBpSet);
				return;
			}
		}

		// Invalidate
		InvalidateRect(CRect(0, m_ptCaret.y*m_sizeFont.cy, m_nBitWidth*m_sizeFont.cx, (m_ptCaret.y+1)*m_sizeFont.cy));
	}
	else {
		// Comment line
		::MessageBeep(-1);
	}
}

void CSourceView::MarkBP(CDC* pDC)
{
	// Draw/Clear BP mark
	CObList* pList = ::BptGetBpList();
	if ( bpSet == m_nDrawBP ) {
		// Set one BP
		if ( m_BP == m_PC ) {
			DrawBitmap(pDC, &m_bmpBPPC, 0, m_ptCaret.y*m_sizeFont.cy);
			m_BP = CSourceAddr(::MEM_UNDEFINED, 0);
		}
		else {
			DrawBitmap(pDC, &m_bmpBP, 0, m_ptCaret.y*m_sizeFont.cy);
		}
	}
	else if ( bpClear == m_nDrawBP ) {
		// Clear one BP
		if ( m_BP == m_PC ) {
			DrawBitmap(pDC, &m_bmpPC, 0, m_ptCaret.y*m_sizeFont.cy);
			m_BP = CSourceAddr(::MEM_UNDEFINED, 0);
		}
	}
	else {
		return;
	}

	// Set caret position
	m_ptCaret = CPoint(max(m_ptCaret.x, GetLeftSpace()), m_ptCaret.y);
	MoveCaretPos();

	m_nDrawBP = bpNo;
}

void CSourceView::DrawBitmap(CDC* pDC, CBitmap* pBmp, const int xPos, const int yPos)
{
    // Draw a bitmap
    CDC dc;
    dc.CreateCompatibleDC(pDC);

    CBitmap* pBmpOld = dc.SelectObject(pBmp);

    pDC->BitBlt(xPos, yPos, bmpWidth, bmpHeight, &dc, 0, 0, SRCCOPY);

    dc.SelectObject(pBmpOld);
}

/////////////////////////////////////////////////////////////////////////////
// CSourceView Search & Mark token routines

void CSourceView::MarkToken(BOOL bGetToken /* = TRUE */)
{
	// Only in SOURCE mode
	if ( GetMode() != ::modSrc ) {
		::MessageBeep(-1);
		return;
	}

	// Get token
	if ( bGetToken ) {
		m_ptMarkToken = CPoint(-1, -1);
		m_strMarkToken = "";
		GetMarkToken();

		// Cleanup for IsMarkXXXX()
		m_strSearchToken = "";
	}
	
	// Mark token
	if ( !m_strMarkToken.IsEmpty() && m_ptMarkToken != CPoint(-1, -1) ) {
		int nLeft = GetLeftSpace() * m_sizeFont.cx;
		int left = nLeft + (m_ptMarkToken.x >= m_sizeScroll.cx ? (m_ptMarkToken.x-m_sizeScroll.cx)*m_sizeFont.cx : 0);
		int right = nLeft + (m_ptMarkToken.y <= m_sizeClient.cx+m_sizeScroll.cx ? (m_ptMarkToken.y-m_sizeScroll.cx+1)*m_sizeFont.cx : m_rectClient.right);
		int top = m_ptCaret.y * m_sizeFont.cy;
		int bottom = top + m_sizeFont.cy;
		InvalidateRect(CRect(left, top, right, bottom));
	}
}

void CSourceView::GetMarkToken()
{
	// Input: 	m_ptCaret
	// Output:	m_strMarkToken, m_ptMarkToken
	
	// Get row & column number
	int nRow = m_ptCaret.y + m_sizeScroll.cy;
	int nCol = m_ptCaret.x + m_sizeScroll.cx - GetLeftSpace();

	// Query from list
	POSITION pos = ((CSourceDocument*)GetDocument())->GetSrcList()->FindIndex(nRow);
	CSourceData* pData = (CSourceData*)((CSourceDocument*)GetDocument())->GetSrcList()->GetNext(pos);
	CString strLine(pData->GetLine());

	pos = pData->GetList()->GetHeadPosition();
	while ( pos ) {
		CSourceAttr* pObj = (CSourceAttr*)pData->GetList()->GetNext(pos);
		int nStart(pObj->GetStart()), nEnd(pObj->GetEnd());
		if ( nCol >= nStart && nCol <= nEnd && CSourceDocument::m_clrCode == pObj->GetColor() ) {
			// Get mark token
			m_strMarkToken = strLine.Mid(nStart, nEnd-nStart+1);
			if ( !iscsymf(m_strMarkToken[0]) ) {
				m_strMarkToken = "";
				return;
			}

			// Get mark point
			m_ptMarkToken = CPoint(nStart, nEnd);
			return;
	    }
	}
}

void CSourceView::ClearTokenMark(BOOL bRedraw /* = TRUE */)
{
	// Is marked token?
	if ( m_strMarkToken.IsEmpty() || m_ptMarkToken == CPoint(-1,-1) ) {
		return;
	}
	
	int nLeft = GetLeftSpace() * m_sizeFont.cx;
	int left = nLeft + (m_ptMarkToken.x >= m_sizeScroll.cx ? (m_ptMarkToken.x-m_sizeScroll.cx)*m_sizeFont.cx : 0);
	int right = nLeft + (m_ptMarkToken.y <= m_sizeClient.cx+m_sizeScroll.cx ? (m_ptMarkToken.y-m_sizeScroll.cx+1)*m_sizeFont.cx : m_rectClient.right);
	int top = m_ptCaret.y * m_sizeFont.cy;
	int bottom = top + m_sizeFont.cy;

	// Clear flag
	m_strMarkToken = "";
	m_ptMarkToken = CPoint(-1, -1);

	// Redraw window
	if ( bRedraw ) {
		InvalidateRect(CRect(left, top, right, bottom));
	}
}

void CSourceView::SearchToken()
{
	// Get current position
	int nRow = m_ptCaret.y + m_sizeScroll.cy;
	int nCol = m_ptCaret.x + m_sizeScroll.cx - GetLeftSpace();

	// Search whitin the whole file according to the direction
	if ( CSourceSearchDlg::dirDown == ::nSrcSearchDirect ) {
		ViewToken(SearchToken(nRow, nCol));
	}
	else if ( CSourceSearchDlg::dirUp == ::nSrcSearchDirect ) {
		ViewToken(SearchToken(nRow, nCol, FALSE), FALSE);
	}
	else if ( CSourceSearchDlg::dirAll == ::nSrcSearchDirect ) {
		CPoint ptFound = SearchToken(nRow, nCol);
		if ( CPoint(-1,-1) == ptFound && CPoint(0,0) != CPoint(nCol, nRow) ) {
			ptFound = SearchToken(0, 0);
		}
		ViewToken(ptFound);
	}
}

void CSourceView::ViewToken(const CPoint ptFound, const BOOL bDown /* = TRUE */)
{
	// Set focus to Source view
	SetFocus();

	// Not found
	if ( CPoint(-1, -1) == ptFound ) {
		HINSTANCE hInstEXE = AfxGetResourceHandle();
		AfxSetResourceHandle(::SrcGetDLLHandle());

		CString strFormat, strPrompt;
		strFormat.LoadString(IDS_SRC_NOTFOUND);
		strPrompt.Format(strFormat, m_strSearchToken);

		AfxSetResourceHandle(hInstEXE);

		AfxMessageBox(strPrompt);
		return;
	}
	
	// Clear previous token
	ClearTokenMark();

	// If the token is in the client area, set caret & mark it;
	// else scroll to (ptFound.x, ptFound,y), and then set caret & mark it;
	if ( ptFound.x < m_sizeScroll.cx || ptFound.x+m_strSearchToken.GetLength()-1 >= m_sizeScroll.cx+m_sizeClient.cx-GetLeftSpace() ||
		 ptFound.y < m_sizeScroll.cy || ptFound.y >= m_sizeScroll.cy+m_sizeClient.cy ) {
		ScrollToPosition(ptFound.y, ptFound.x, bDown);
	}

    // Mark searched token
    m_strMarkToken = m_strSearchToken;
    m_ptMarkToken.x = ptFound.x;
	m_ptMarkToken.y = m_ptMarkToken.x + m_strSearchToken.GetLength() - 1;
	m_ptCaret.y = ptFound.y - m_sizeScroll.cy;
	MarkToken(FALSE);

	// Set caret position
	m_ptCaret.x = ptFound.x + (bDown ? m_strSearchToken.GetLength() : 0) - m_sizeScroll.cx + GetLeftSpace();
	MoveCaretPos();
}

void CSourceView::ScrollToPosition(const int nRow, const int nCol /* = -1 */, const BOOL bDown /* = TRUE */)
{
	// Scroll in the direction
	if ( GetMode() == ::modSrc ) {
		// Vertical scroll
		if ( nRow < m_sizeScroll.cy || nRow >= m_sizeScroll.cy+m_sizeClient.cy ) {
			SendMessage(WM_VSCROLL, SB_THUMBTRACK, MAKELONG(nRow, NULL));
		}
		
		// Horizontal scroll
		int nTail = nCol + m_strSearchToken.GetLength() - 1;
		int nLeft = GetLeftSpace();
		if ( nCol < m_sizeScroll.cx || nTail >= m_sizeScroll.cx+m_sizeClient.cx-nLeft ) {
			if ( bDown ) {
				// Tail is first
				int nPos = max(0, nTail+1-m_sizeClient.cx+nLeft+1);
				SendMessage(WM_HSCROLL, SB_THUMBTRACK, MAKELONG(nPos, NULL));
			}
			else {
				// Head is first
				int nPos = max(0, nCol-1);
				SendMessage(WM_HSCROLL, SB_THUMBTRACK, MAKELONG(nPos, NULL));
			}
		}
	}
	else if ( GetMode() == ::modMix ) {
		// Vertical scroll
		ASSERT(-1 == nCol);
		
		// Get total line count
		int nLine(nRow);
		
		CObList* pList = ((CSourceDocument*)GetDocument())->GetAsmList();
		ASSERT(!pList->IsEmpty());
		
		POSITION pos = pList->GetHeadPosition();
		while ( pos ) {
			CAssemblyData* pObj = (CAssemblyData*)pList->GetNext(pos);
			if ( pObj->GetLine() < nRow ) {
				if ( pObj->GetLine() != -1 ) {
					nLine += pObj->GetList()->GetCount();
				}
			}
			else {
				break;
			}
		}

		SendMessage(WM_VSCROLL, SB_THUMBTRACK, MAKELONG(nLine, NULL));
	}
}

void CSourceView::ScrollToAddr(const CSourceAddr& Addr)
{
	// Scroll to Addr
	int nMode = GetMode();
	if ( ::modSrc == nMode ) {
		// ASM => SRC
		int nLine, nStart, nEnd;
		if ( !::SrcAddrToLine(Addr, nLine, nStart, nEnd) ) {
			nLine = 0;
		}
		SendMessage(WM_VSCROLL, SB_THUMBTRACK, MAKELONG(nLine-2, NULL));
	}
	else if ( ::modMix == nMode ) {
		// ASM => MIX
		int nLine;
		if ( !GetMixedLine(Addr, nLine) ) {
			nLine = 1;
		}
		SendMessage(WM_VSCROLL, SB_THUMBTRACK, MAKELONG(nLine-1, NULL));
	}
}

BOOL CSourceView::GetMixedLine(const CSourceAddr& Addr, int& nLine)
{
	// Get current scrolled line in mixed mode
	int nSrcLine, nStart, nEnd;
	int nCount(1);
	if ( ::SrcAddrToLine(Addr, nSrcLine, nStart, nEnd) ) {
		nLine = nSrcLine;
		CObList* pAsmList = ((CSourceDocument*)GetDocument())->GetAsmList();
		POSITION pos = pAsmList->GetHeadPosition();
		while ( pos ) {
			CAssemblyData* pObj = (CAssemblyData*)pAsmList->GetNext(pos);
			CObList* pList = pObj->GetList();
			if ( nCount == nSrcLine ) {
				POSITION pos = pList->GetHeadPosition();
				while ( pos ) {
					nLine++;
					CString strAddr = ((CAssemblyCode*)pList->GetNext(pos))->GetAddr();
					CSourceAddr AddrTmp;
					if ( ::SrcTextToAddr(strAddr, AddrTmp) && Addr == AddrTmp ) {
						return TRUE;
					}
				}
				ASSERT(FALSE);
			}
			else {
				if ( pObj->GetLine() != -1 ) {
					nLine += pList->GetCount();
				}
			}
			nCount++;
		}
		ASSERT(FALSE);
	}

	return FALSE;
}

void CSourceView::GetMixedLine(const int nSrcLine, int& nLine)
{
	// SRC => MIX
	nLine = nSrcLine;
	int nCount(1);

	CObList* pAsmList = ((CSourceDocument*)GetDocument())->GetAsmList();
	POSITION pos = pAsmList->GetHeadPosition();
	while ( pos ) {
		CAssemblyData* pObj = (CAssemblyData*)pAsmList->GetNext(pos);
		CObList* pList = pObj->GetList();
		if ( nCount < nSrcLine && pObj->GetLine() != -1 ) {
			nLine += pList->GetCount();
		}
		nCount++;
	}
}

CPoint CSourceView::SearchToken(const int nRow, const int nCol, const BOOL bDown /* = TRUE */)
{
	// Case sensitive?
	CString strSearchToken(m_strSearchToken);
	if ( !::bSrcSearchCase ) {
		strSearchToken.MakeUpper();
	}

	// Found token position
	if ( bDown ) {
		// Search to the end
		POSITION pos = ((CSourceDocument*)GetDocument())->GetSrcList()->FindIndex(nRow);
		int nCount(0);
		while ( pos ) {
			CSourceData* pData = (CSourceData*)((CSourceDocument*)GetDocument())->GetSrcList()->GetNext(pos);
			CString strLine(pData->GetLine());

			if ( 0 == nCount ) {
				if ( strLine.GetLength() <= nCol ) {
					nCount++;
					continue;
				}
				else {
					strLine = strLine.Mid(nCol);
				}
			}
			
			if ( strSearchToken.GetLength() > strLine.GetLength() ) {
				nCount++;
				continue;
			}

			// Case sensitive?
			if ( !::bSrcSearchCase ) {
				strLine.MakeUpper();
			}

			// Forward search in a line
			if ( !::bSrcSearchWord ) {
				int nIndex = strLine.Find(strSearchToken);
				if ( -1 != nIndex ) {
					return CPoint(0 == nCount ? nIndex+nCol : nIndex, nRow+nCount);
				}
			}
			else {
				int nWholeIndex(0);
				while ( TRUE ) {
					int nIndex = strLine.Find(strSearchToken);
					if ( -1 != nIndex ) {
						nWholeIndex += nIndex;
						// Get prev & next char
						char chPrev(' '), chNext(' ');
						if ( 0 == nIndex && 0 == nCount && nCol > 0 ) {
							chPrev = pData->GetLine().GetAt(nCol-1);
						}
						else if ( nIndex > 0 ) {
							chPrev = strLine[nIndex-1];
						}
						
						if ( nIndex + strSearchToken.GetLength() < strLine.GetLength() ) {
							chNext = strLine[nIndex+strSearchToken.GetLength()];
						}
						
						// Check previous & next char
						if ( ((CSourceDocument*)GetDocument())->IsDelimiter(chPrev) && 
							 ((CSourceDocument*)GetDocument())->IsDelimiter(chNext)) {
							return CPoint(0 == nCount ? nWholeIndex+nCol : nWholeIndex, nRow+nCount);
						}
						else if ( nIndex + strSearchToken.GetLength() < strLine.GetLength() ) {
							strLine = strLine.Mid(nIndex+1);
							nWholeIndex++;
							continue;
						}
					}
					break;
				}
			}

			// Next node
			nCount++;
		}
	}
	else {
		// Search to the begin
		POSITION pos = ((CSourceDocument*)GetDocument())->GetSrcList()->FindIndex(nRow);
		int nCount(0);
		while ( pos ) {
			CSourceData* pData = (CSourceData*)((CSourceDocument*)GetDocument())->GetSrcList()->GetPrev(pos);
			CString strLine(pData->GetLine());

			if ( 0 == nCount ) {
				if ( strLine.GetLength() < nCol ) {
					nCount++;
					continue;
				}
				else {
					strLine = strLine.Left(nCol);
				}
			}
			
			if ( strSearchToken.GetLength() > strLine.GetLength() ) {
				nCount++;
				continue;
			}

			// Case sensitive?
			if ( !::bSrcSearchCase ) {
				strLine.MakeUpper();
			}

			// Backward search in a line
			if ( !::bSrcSearchWord ) {
				int nIndex = ReverseFind(strLine, strSearchToken);
				if ( -1 != nIndex ) {
					return CPoint(nIndex, nRow-nCount);
				}
			}
			else {
				while ( TRUE ) {
					int nIndex = ReverseFind(strLine, strSearchToken);
					if ( -1 != nIndex ) {
						// Get prev & next char
						char chPrev(' '), chNext(' ');
						if ( nIndex > 0 ) {
							chPrev = strLine[nIndex-1];
						}
						
						if ( nIndex + strSearchToken.GetLength() < strLine.GetLength() ) {
							if ( 0 == nCount && nCol < pData->GetLine().GetLength() ) {
								chNext = strLine[nIndex+strSearchToken.GetLength()];
							}
						    else {
								chNext = strLine[nIndex+strSearchToken.GetLength()];
							}
						}
						
						// Check previous & next char
						if ( ((CSourceDocument*)GetDocument())->IsDelimiter(chPrev) && 
							 ((CSourceDocument*)GetDocument())->IsDelimiter(chNext)) {
							return CPoint(nIndex, nRow-nCount);
						}
						else if ( nIndex >= strSearchToken.GetLength() ) {
							strLine = strLine.Left(nIndex);
							continue;
						}
					}
					break;
				}
			}

			// Next node
			nCount++;
		}
	}

	return CPoint(-1, -1);
}

int CSourceView::ReverseFind(const CString& strSrc, const CString& strSub) const
{
	// Empty string is error
	if ( strSrc.IsEmpty() || strSub.IsEmpty() ) {
		return -1;
	}

	// The length of source string must be large than or equare to sub string
	int nSrcLen = strSrc.GetLength();
	int nSubLen = strSub.GetLength();
	
	if ( nSrcLen < nSubLen ) {
		return -1;
	}
	
	// Compare from the tail
	BOOL bMatch(FALSE);
	for ( int i(nSrcLen-1); i >= nSubLen-1; i-- ) {
		for ( int j(nSubLen-1); j >= 0; j-- ) {
			if ( strSub[j] != strSrc[i-nSubLen+1+j] ) {
				bMatch = FALSE;
				break;
			}
			else {
				bMatch = TRUE;
			}
		}
		if ( bMatch ) {
			return i-nSubLen+1;
		}
		else {
			continue;
		}
	}

	return bMatch ? 0 : -1;
}


/////////////////////////////////////////////////////////////////////////////
// CSourceView data tip routines

void CSourceView::SetDataTip()
{
	// Hide tip window
	HideDataTip();

	// Only within Source mode
	if ( !m_bBrowse && ::bSrcHintsOn && GetMode() == ::modSrc ) {
		// Get tip token
		CString strToken;
		GetTipToken(strToken);
		if ( !strToken.IsEmpty() ) {
			// Get data value
			GetTipValue(strToken);
			if ( !m_strTip.IsEmpty() ) {
				// Set timer
			 	m_uTimerID = SetTimer(tmEvent, tmElapse, NULL);
			}
		}
	}
}

void CSourceView::HideDataTip()
{
	// Kill timer
	if ( 0 != m_uTimerID ) {
		KillTimer(m_uTimerID);
		m_uTimerID = 0;
	}

	// Hide tip window
	m_wndTip.UpdateTip();
}

void CSourceView::GetTipToken(CString& strToken)
{
	// Empty token indicates failure
	strToken = "";

	// Get row & column number
	int nRow = m_ptTip.y + m_sizeScroll.cy;
	int nCol = m_ptTip.x + m_sizeScroll.cx - GetLeftSpace();
	if ( nCol < 0 || nRow >= ((CSourceDocument*)GetDocument())->GetSrcList()->GetCount() ) {
		return;
	}

	// Query from list
	POSITION pos = ((CSourceDocument*)GetDocument())->GetSrcList()->FindIndex(nRow);
	CSourceData* pData = (CSourceData*)((CSourceDocument*)GetDocument())->GetSrcList()->GetNext(pos);
	CString strLine(pData->GetLine());

	pos = pData->GetList()->GetHeadPosition();
	while ( pos ) {
		CSourceAttr* pObj = (CSourceAttr*)pData->GetList()->GetNext(pos);
		int nStart(pObj->GetStart()), nEnd(pObj->GetEnd());
		if ( nCol >= nStart && nCol <= nEnd && CSourceDocument::m_clrCode == pObj->GetColor() ) {
			// Get tip token
			strToken = strLine.Mid(nStart, nEnd-nStart+1);
			if ( !iscsymf(strToken[0]) ) {
				strToken = "";
			}
			return;
	    }
	}
}

void CSourceView::GetTipValue(const CString& strToken)
{
	// Query from Symbol server
	CString strValue(::szTipNil);

	// Get module descriptor
	SYM_DESCRIPTOR dwModuleDesc;
	if ( ::SrcGetModuleDesc(dwModuleDesc) ) {
		int nLine = m_sizeScroll.cy + 1 + m_ptCaret.y;
	 	if ( 0 != ::SrcGetTipValue(strToken, dwModuleDesc, nLine, strValue) ) {
			strValue = ::szTipNil;
	 	}
		ASSERT(strValue.GetLength() <= 40);
	}

	m_strTip = strToken + ::szTipEqu + strValue;
	if ( m_strTip.GetLength() > 40 ) {
		m_strTip = strValue;
	}
}


/////////////////////////////////////////////////////////////////////////////
// CSourceView drawing & printing

void CSourceView::UpdateOtherWindows()
{
	// Update other windows
	::RepaintCPU();
	::RepaintMemory();
	::RepaintStack();
	::RepaintVariable();
	::RepaintPeri();
}

int CSourceView::GetLeftSpace() const
{
	// Get left leading space
	return (m_bLine ? m_nBitWidth+m_nNumWidth : m_nBitWidth);
}

void CSourceView::SetScrollSizes()
{
	// TODO: calculate the total size of this view
	CSize size(((CSourceDocument*)GetDocument())->GetTotalSize());
	int nLeft = GetLeftSpace();
	CSize sizeTotal(size.cx+nLeft+1, size.cy+1);

	CSize sizePage(5, m_rectClient.Height()/m_sizeFont.cy - 1);

	CSize sizeLine(1, 1);

    CTextView::SetScrollSizes(sizeTotal, sizePage, sizeLine);
}

void CSourceView::GetScrollSize()
{
	// Get scroll bar position
	m_sizeScroll.cx = GetScrollPos(SB_HORZ);
	m_sizeScroll.cy = GetScrollPos(SB_VERT);
}

void CSourceView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo /* = NULL */)
{
	CTextView::OnPrepareDC(pDC, pInfo);

    // TODO: add extra preparing DC code here

	// Select different fonts for display or print
	if ( !pDC->IsPrinting() ) {
    	pDC->SelectStockObject(ANSI_FIXED_FONT);
    }
    else {
    	pDC->SelectStockObject(DEVICE_DEFAULT_FONT);
    	m_nCurPage = pInfo->m_nCurPage;
    }
}

void CSourceView::OnDraw(CDC* pDC)
{
	// TODO: add draw code here
    
    // Output to Screen or Printer
    if ( !pDC->IsPrinting() ) {
		GetScrollSize();
		DisplayDocumentContent(pDC);
	}
	else {
		PrintDocumentContent(pDC);
		PrintFooter(pDC);
	}
}

void CSourceView::DisplayDocumentContent(CDC* pDC)
{
	// Display code, mark PC, mark BP
	GetPCLinePos();
	switch ( GetMode() ) {
	case ::modAsm:
		DisplayAssemblyCode(pDC);
		break;
	case ::modMix:
		DisplayMixedCode(pDC);
		break;
	case ::modSrc:
		DisplaySourceCode(pDC);
		break;
	default:
		return;
	}

	// Mark BP
	MarkBP(pDC);
}

void CSourceView::PrintDocumentContent(CDC* pDC)
{
	// Source & Mixed: all of file; Asm: current displayed range
	switch ( GetMode() ) {
	case ::modAsm:
		PrintAssemblyCode(pDC);
		break;
	case ::modMix:
		PrintMixedCode(pDC);
		break;
	case ::modSrc:
		PrintSourceCode(pDC);
		break;
	default:
		return;
	}
}

void CSourceView::PrintFooter(CDC* pDC)
{
	// Print page foot
	pDC->SetBkColor(CSourceDocument::m_clrWhite);
	pDC->SetTextColor(CSourceDocument::m_clrCode);
	CString strFooter;
	strFooter.Format(::szFooter, m_nCurPage, m_nTotalPage);
	pDC->TextOut(((m_sizePage.cx-strFooter.GetLength())/2)*m_sizeFont.cx, (m_sizePage.cy-1)*m_sizeFont.cy, strFooter);
}

void CSourceView::DisplayAssemblyCode(CDC* pDC)
{
	// Get document
	CSourceDocument* pDoc = (CSourceDocument*)GetDocument();
	if ( pDoc->GetAsmList()->IsEmpty() ) {
		return;
	}

	// Only one node in this list
	CObList* pAsmList = ((CAssemblyData*)(pDoc->GetAsmList()->GetHead()))->GetList();

	// Show assemble code
	int nLine(0);
	POSITION pos = pAsmList->GetHeadPosition();
	while ( pos ) {
		// Get object
		CAssemblyCode* pObj = (CAssemblyCode*)pAsmList->GetNext(pos);
		CString strLabel(pObj->GetLabel());
		CString strAddr(pObj->GetAddr());
		CString strObj(pObj->GetObj());
		CString strInst(pObj->GetInst());

		// Normal attribute
		pDC->SetBkColor(CSourceDocument::m_clrWhite);
		pDC->SetTextColor(CSourceDocument::m_clrCode);

		// Show label if exist
		if ( !strLabel.IsEmpty() ) {
			CString str("");
			for ( int i(0); i < m_nObjWidth; i++ ) {
				str += ' ';
			}
			strLabel = str + strLabel;
			int nLen = strLabel.GetLength();
			if ( nLen > m_sizeScroll.cx ) {
				strLabel = strLabel.Right(nLen-m_sizeScroll.cx);
			}
			else {
				strLabel = "";
			}
			pDC->TextOut((m_nBitWidth+m_nNumWidth)*m_sizeFont.cx, nLine*m_sizeFont.cy, strLabel);
			++nLine;
		}

		// Mark BP if exist
		CSourceAddr Addr;
		if ( ::SrcTextToAddr(strAddr, Addr) && !m_bBrowse ) {
			if ( ::BptIsBreakpoint(Addr) ) {
				DrawBitmap(pDC, &m_bmpBP, 0, nLine*m_sizeFont.cy);
			}
		}

		// Mark cursor link
		if ( m_bBrowse && m_bCursorLink && Addr == m_CursorLinkAddr ) {
			DrawBitmap(pDC, &m_bmpLink, 0, nLine*m_sizeFont.cy);
		}

		// Show address
		pDC->TextOut(m_nBitWidth*m_sizeFont.cx, nLine*m_sizeFont.cy, strAddr);

		// Show Object & instruction
		CString strLine = strObj + ::szTab + strInst;
		int nLen = strLine.GetLength();
		if ( nLen > m_sizeScroll.cx ) {
			strLine = strLine.Right(nLen-m_sizeScroll.cx);
		}
		else {
			strLine = "";
		}

		// Invert PC line
		if ( nLine == m_nPCLine && !m_bBrowse ) {
			pDC->SetBkColor(CSourceDocument::m_clrCode);
			pDC->SetTextColor(CSourceDocument::m_clrWhite);
			// Mark PC
			if ( ::BptIsBreakpoint(m_PC) ) {
				DrawBitmap(pDC, &m_bmpBPPC, 0, nLine*m_sizeFont.cy);
			}
			else {
				DrawBitmap(pDC, &m_bmpPC, 0, nLine*m_sizeFont.cy);
			}

			// Save current PC mark line
			m_nOldPCLine = m_nPCLine;
		}
		else {
			pDC->SetBkColor(CSourceDocument::m_clrWhite);
			pDC->SetTextColor(CSourceDocument::m_clrCode);
		}
		pDC->TextOut((m_nBitWidth+m_nNumWidth)*m_sizeFont.cx, nLine*m_sizeFont.cy, strLine);

		// Next line
		++nLine;
	}
	
	// Set scroll position
	SetAsmScrollPos();
}

void CSourceView::DisplayMixedCode(CDC* pDC)
{
	// Get document
	CSourceDocument* pDoc = (CSourceDocument*)GetDocument();
	CObList* pSrcList = pDoc->GetSrcList();
	CObList* pAsmList = pDoc->GetAsmList();
	if ( pSrcList->IsEmpty() ) {
		return;
	}
	if ( pAsmList->IsEmpty() ) {
		return;
	}

	// Get the list index according to the scroll position
	int nSrcIndex(0), nAsmIndex(-1);
	GetListIndex(m_sizeScroll.cy, pSrcList, pAsmList, nSrcIndex, nAsmIndex);
	ASSERT(nSrcIndex >= 0 && nAsmIndex >= -1);

	// Show source code
	int nLine(0), nLineNum(nSrcIndex);
	POSITION posSrc = pSrcList->FindIndex(nSrcIndex);
	while ( posSrc ) {
		// Show assembly code first
		if ( nAsmIndex > -1 ) {
			// Show assembly code
			POSITION posAsm = pAsmList->FindIndex(nSrcIndex);
			ASSERT(posAsm);
			ASSERT(((CAssemblyData*)pAsmList->GetAt(posAsm))->GetLine() > -1);
			if ( posAsm && -1 != ((CAssemblyData*)pAsmList->GetAt(posAsm))->GetLine() ) {
				CObList* pAsmList = ((CAssemblyData*)pAsmList->GetAt(posAsm))->GetList();
				POSITION pos = pAsmList->FindIndex(nAsmIndex);
				while ( pos ) {
					// Get object
					CAssemblyCode* pObj = (CAssemblyCode*)pAsmList->GetNext(pos);
					CString strLabel(pObj->GetLabel());
					CString strAddr(pObj->GetAddr());
					CString strObj(pObj->GetObj());
					CString strInst(pObj->GetInst());
				
					// Normal attribute
					pDC->SetBkColor(CSourceDocument::m_clrWhite);
					pDC->SetTextColor(CSourceDocument::m_clrCode);
				
					// Show label if exist
					int nLeft = GetLeftSpace();
//					if ( !strLabel.IsEmpty() ) {
//						CString str("");
//						for ( int i(0); i < m_nObjWidth+m_nAddrWidth; i++ ) {
//							str += ' ';
//						}
//						strLabel = str + strLabel;
//						int nLen = strLabel.GetLength();
//						if ( nLen > m_sizeScroll.cx ) {
//							strLabel = strLabel.Right(nLen-m_sizeScroll.cx);
//						}
//						else {
//							strLabel = "";
//						}
//						pDC->TextOut(nLeft*m_sizeFont.cx, nLine*m_sizeFont.cy, strLabel);
//						if ( ++nLine > m_sizeClient.cy + m_sizeScroll.cy ) {
//							return;
//						}
//					}
		
					// Mark BP if exist
					CSourceAddr Addr;
					if ( ::SrcTextToAddr(strAddr, Addr) && !m_bBrowse ) {
						if ( ::BptIsBreakpoint(Addr) ) {
							DrawBitmap(pDC, &m_bmpBP, 0, nLine*m_sizeFont.cy);
						}
					}

					// Mark cursor link
					if ( m_bBrowse && m_bCursorLink && Addr == m_CursorLinkAddr ) {
						DrawBitmap(pDC, &m_bmpLink, 0, nLine*m_sizeFont.cy);
					}

					// Show Address, Object & Instruction
					CString strLine = strAddr + ::szTab + strObj + ::szTab + strInst;
					int nLen = strLine.GetLength();
					if ( nLen > m_sizeScroll.cx ) {
						strLine = strLine.Right(nLen-m_sizeScroll.cx);
					}
					else {
						strLine = "";
					}
				
					// Invert PC line
					if ( m_sizeScroll.cy+nLine+1 == m_nPCLine && !m_bBrowse ) {
						pDC->SetBkColor(CSourceDocument::m_clrCode);
						pDC->SetTextColor(CSourceDocument::m_clrWhite);
						// Mark PC
						if ( ::BptIsBreakpoint(m_PC) ) {
							DrawBitmap(pDC, &m_bmpBPPC, 0, nLine*m_sizeFont.cy);
						}
						else {
							DrawBitmap(pDC, &m_bmpPC, 0, nLine*m_sizeFont.cy);
						}

						// Save current PC mark line
						m_nOldPCLine = m_nPCLine;
					}
					else {
						pDC->SetBkColor(CSourceDocument::m_clrWhite);
						pDC->SetTextColor(CSourceDocument::m_clrCode);
					}
					pDC->TextOut(nLeft*m_sizeFont.cx, nLine*m_sizeFont.cy, strLine);
		
					// Next line
					if ( ++nLine > m_sizeClient.cy + m_sizeScroll.cy ) {
						return;
					}
				}
			}
		}

		// Get source data object
		CSourceData* pSrcObj = (CSourceData*)pSrcList->GetNext(posSrc);
		if ( nAsmIndex > -1 ) {
			nAsmIndex = -1;
			nLineNum++;
			continue;
		}

		// Show line number
		if ( m_bLine ) {
			CString strNum("");
			strNum.Format(::szLineFormat, nLineNum+1);
			pDC->SetBkColor(CSourceDocument::m_clrWhite);
			pDC->SetTextColor(CSourceDocument::m_clrCode);
			pDC->TextOut(m_nBitWidth*m_sizeFont.cx, nLine*m_sizeFont.cy, strNum);
		}
		int nLeft = GetLeftSpace();

		// Show line code & mark PC if necessary
		CString strLine(pSrcObj->GetLine());
		if ( !m_bSyntaxColor ) {
			// Without syntax color
			int nLen = strLine.GetLength();
			if ( nLen > m_sizeScroll.cx ) {
				strLine = strLine.Right(nLen-m_sizeScroll.cx);
			}
			else {
				strLine = "";
			}
			pDC->SetBkColor(CSourceDocument::m_clrWhite);
			pDC->SetTextColor(CSourceDocument::m_clrGray);
			pDC->TextOut(nLeft*m_sizeFont.cx, nLine*m_sizeFont.cy, strLine);
		}
		else {
			// With syntax color
			CObList* pAttrList = pSrcObj->GetList();
			POSITION pos = pAttrList->GetHeadPosition();
			pDC->SetBkColor(CSourceDocument::m_clrWhite);
			while ( pos ) {
				CSourceAttr* pObj = (CSourceAttr*)pAttrList->GetNext(pos);
				int nStart(pObj->GetStart()), nEnd(pObj->GetEnd());
				ASSERT(nStart >= 0 && nEnd >= 0 && nStart <= nEnd);
				if ( nStart > m_sizeScroll.cx + m_sizeClient.cx || nEnd < m_sizeScroll.cx ) {
					continue;
				}
				else {
					int x1 = max(nStart, m_sizeScroll.cx);
					int x2 = min(nEnd, m_sizeClient.cx + m_sizeScroll.cx);
					CString strToken = strLine.Mid(x1, x2-x1+1);
					COLORREF clr = pObj->GetColor() == CSourceDocument::m_clrCode ? CSourceDocument::m_clrGray : pObj->GetColor();
					pDC->SetTextColor(clr);
					pDC->TextOut((nLeft+x1-m_sizeScroll.cx)*m_sizeFont.cx, nLine*m_sizeFont.cy, strToken);
				}
			}
		}

		// Next line
		nLineNum++;
		if ( ++nLine > m_sizeClient.cy + m_sizeScroll.cy ) {
			return;
		}

		// Show assembly code
		POSITION posAsm = pAsmList->FindIndex(nLineNum-1);
		if ( posAsm && -1 != ((CAssemblyData*)pAsmList->GetAt(posAsm))->GetLine() ) {
			CObList* pAsmList = ((CAssemblyData*)pAsmList->GetAt(posAsm))->GetList();
			POSITION pos = pAsmList->GetHeadPosition();
			while ( pos ) {
				// Get object
				CAssemblyCode* pObj = (CAssemblyCode*)pAsmList->GetNext(pos);
				CString strLabel(pObj->GetLabel());
				CString strAddr(pObj->GetAddr());
				CString strObj(pObj->GetObj());
				CString strInst(pObj->GetInst());
		
				// Normal attribute
				pDC->SetBkColor(CSourceDocument::m_clrWhite);
				pDC->SetTextColor(CSourceDocument::m_clrCode);
		
				// Show label if exist
//				if ( !strLabel.IsEmpty() ) {
//					CString str("");
//					for ( int i(0); i < m_nObjWidth+m_nAddrWidth; i++ ) {
//						str += ' ';
//					}
//					strLabel = str + strLabel;
//					int nLen = strLabel.GetLength();
//					if ( nLen > m_sizeScroll.cx ) {
//						strLabel = strLabel.Right(nLen-m_sizeScroll.cx);
//					}
//					else {
//						strLabel = "";
//					}
//					pDC->TextOut(nLeft*m_sizeFont.cx, nLine*m_sizeFont.cy, strLabel);
//					if ( ++nLine > m_sizeClient.cy + m_sizeScroll.cy ) {
//						return;
//					}
//				}

				// Mark BP if exist
				CSourceAddr Addr;
				if ( ::SrcTextToAddr(strAddr, Addr) && !m_bBrowse ) {
					if ( ::BptIsBreakpoint(Addr) ) {
						DrawBitmap(pDC, &m_bmpBP, 0, nLine*m_sizeFont.cy);
					}
				}

				// Mark cursor link
				if ( m_bBrowse && m_bCursorLink && Addr == m_CursorLinkAddr ) {
					DrawBitmap(pDC, &m_bmpLink, 0, nLine*m_sizeFont.cy);
				}

				// Show Address, Object & Instruction
				CString strLine = strAddr + ::szTab + strObj + ::szTab + strInst;
				int nLen = strLine.GetLength();
				if ( nLen > m_sizeScroll.cx ) {
					strLine = strLine.Right(nLen-m_sizeScroll.cx);
				}
				else {
					strLine = "";
				}
		
				// Invert PC line
				if ( m_sizeScroll.cy+nLine+1 == m_nPCLine && !m_bBrowse ) {
					pDC->SetBkColor(CSourceDocument::m_clrCode);
					pDC->SetTextColor(CSourceDocument::m_clrWhite);
					// Mark PC
					if ( ::BptIsBreakpoint(m_PC) ) {
						DrawBitmap(pDC, &m_bmpBPPC, 0, nLine*m_sizeFont.cy);
					}
					else {
						DrawBitmap(pDC, &m_bmpPC, 0, nLine*m_sizeFont.cy);
					}

					// Save current PC mark line
					m_nOldPCLine = m_nPCLine;
				}
				else {
					pDC->SetBkColor(CSourceDocument::m_clrWhite);
					pDC->SetTextColor(CSourceDocument::m_clrCode);
				}
				pDC->TextOut(nLeft*m_sizeFont.cx, nLine*m_sizeFont.cy, strLine);

				// Next line
				if ( ++nLine > m_sizeClient.cy + m_sizeScroll.cy ) {
					return;
				}
			}
		}
	}
}

void CSourceView::DisplaySourceCode(CDC* pDC)
{
	// Get document
	CSourceDocument* pDoc = (CSourceDocument*)GetDocument();
	CObList* pSrcList = pDoc->GetSrcList();
	if ( pSrcList->IsEmpty() ) {
		return;
	}

	// Show source code
	int nLine(0);
	POSITION pos = pSrcList->FindIndex(m_sizeScroll.cy);

	while ( pos ) {
		// Get source data object
		CSourceData* pObj = (CSourceData*)pSrcList->GetNext(pos);

		// Show line number
		if ( m_bLine ) {
			CString strNum("");
			strNum.Format(::szLineFormat, nLine+m_sizeScroll.cy+1);
			pDC->SetBkColor(CSourceDocument::m_clrWhite);
			pDC->SetTextColor(CSourceDocument::m_clrCode);
			pDC->TextOut(m_nBitWidth*m_sizeFont.cx, nLine*m_sizeFont.cy, strNum);
		}
		int nLeft = GetLeftSpace();

		// Mark BP if exist
		CSourceAddr Addr(0,::MEM_UNDEFINED), AddrEnd;
		if ( ::SrcGetLineRange(nLine+m_sizeScroll.cy+1, Addr, AddrEnd) && !m_bBrowse ) {
			if ( ::BptIsBreakpoint(Addr) ) {
				DrawBitmap(pDC, &m_bmpBP, 0, nLine*m_sizeFont.cy);
			}
		}

		// Mark cursor link
		if ( m_bBrowse && m_bCursorLink && Addr == m_CursorLinkAddr ) {
			DrawBitmap(pDC, &m_bmpLink, 0, nLine*m_sizeFont.cy);
		}

		// Show line code & mark PC if necessary
		CString strLine(pObj->GetLine());
		if ( !m_bSyntaxColor ) {
			// Without syntax color
			int nLen = strLine.GetLength();
			if ( nLen > m_sizeScroll.cx ) {
				strLine = strLine.Right(nLen-m_sizeScroll.cx);
			}
			else {
				strLine = "";
			}

			pDC->SetTextColor(CSourceDocument::m_clrCode);
			if ( nLine + m_sizeScroll.cy + 1 == m_nPCLine && !m_bBrowse ) {
				// Ingore statement info
				ASSERT(-1 == m_nPCStartCol && -1 == m_nPCEndCol);
				pDC->SetBkColor(CSourceDocument::m_clrYellow);
				// Mark PC
				if ( ::BptIsBreakpoint(m_PC) ) {
					DrawBitmap(pDC, &m_bmpBPPC, 0, nLine*m_sizeFont.cy);
				}
				else {
					DrawBitmap(pDC, &m_bmpPC, 0, nLine*m_sizeFont.cy);
				}

				// Save current PC mark line
				m_nOldPCLine = m_nPCLine;
			}
			else {
				pDC->SetBkColor(CSourceDocument::m_clrWhite);
			}

			// Skip left leading space
			int nSpace(0);
			while ( nSpace < strLine.GetLength() && ' ' == strLine[nSpace] ) {
				nSpace++;
			}
			pDC->TextOut((nLeft+nSpace)*m_sizeFont.cx, nLine*m_sizeFont.cy, (const char*)strLine+nSpace);
		}
		else {
			// With syntax color
			CObList* pAttrList = pObj->GetList();
			POSITION pos = pAttrList->GetHeadPosition();
			COLORREF clrBk;
			if ( nLine + m_sizeScroll.cy + 1 == m_nPCLine && !m_bBrowse ) {
				// Ingore statement info
				ASSERT(-1 == m_nPCStartCol && -1 == m_nPCEndCol);
				clrBk = CSourceDocument::m_clrYellow;
				// Mark PC
				if ( ::BptIsBreakpoint(m_PC) ) {
					DrawBitmap(pDC, &m_bmpBPPC, 0, nLine*m_sizeFont.cy);
				}
				else {
					DrawBitmap(pDC, &m_bmpPC, 0, nLine*m_sizeFont.cy);
				}

				// Save current PC mark line
				m_nOldPCLine = m_nPCLine;
			}
			else {
				clrBk = CSourceDocument::m_clrWhite;
			}
			pDC->SetBkColor(clrBk);

			BOOL bStartMarkPC(FALSE);
			while ( pos ) {
				CSourceAttr* pObj = (CSourceAttr*)pAttrList->GetNext(pos);
				int nStart(pObj->GetStart()), nEnd(pObj->GetEnd());
				ASSERT(nStart >= 0 && nEnd >= 0 && nStart <= nEnd);
				if ( nStart > m_sizeScroll.cx + m_sizeClient.cx ||
					 nEnd < m_sizeScroll.cx ) {
					continue;
				}
				else {
					pDC->SetTextColor(pObj->GetColor());

					int x1 = max(nStart, m_sizeScroll.cx);
					int x2 = min(nEnd, m_sizeClient.cx + m_sizeScroll.cx);

					// Skip left leading space
					CString strToken = strLine.Mid(x1, x2-x1+1);
					if ( nLine + m_sizeScroll.cy + 1 == m_nPCLine && !bStartMarkPC ) {
						bStartMarkPC = TRUE;
						int nSpace(0);
						while ( nSpace < strToken.GetLength() && ' ' == strToken[nSpace] ) {
							nSpace++;
						}
						pDC->TextOut((nLeft+x1-m_sizeScroll.cx+nSpace)*m_sizeFont.cx, nLine*m_sizeFont.cy, (const char*)strToken+nSpace);
					}
					else {
						pDC->TextOut((nLeft+x1-m_sizeScroll.cx)*m_sizeFont.cx, nLine*m_sizeFont.cy, strToken);
					}
				}
			}
		}

		// Mark token
		if ( CPoint(-1, -1) != m_ptMarkToken && m_ptCaret.y == nLine && m_ptMarkToken.y >= m_sizeScroll.cx ) {
			CString strToken = pObj->GetLine().Mid(m_ptMarkToken.x, m_strMarkToken.GetLength());

			pDC->SetBkColor(CSourceDocument::m_clrCode);
			pDC->SetTextColor(CSourceDocument::m_clrWhite);
				
			if ( m_ptMarkToken.x >= m_sizeScroll.cx ) {
				pDC->TextOut((nLeft+m_ptMarkToken.x-m_sizeScroll.cx)*m_sizeFont.cx, nLine*m_sizeFont.cy, strToken);
			}
			else {
				pDC->TextOut(nLeft*m_sizeFont.cx, nLine*m_sizeFont.cy, (const char*)strToken+m_sizeScroll.cx-m_ptMarkToken.x);
			}
		}

		// Next line
		if ( ++nLine > m_sizeClient.cy + m_sizeScroll.cy ) {
			return;
		}
	}
}

void CSourceView::PrintAssemblyCode(CDC* pDC)
{
	// Get document
	CSourceDocument* pDoc = (CSourceDocument*)GetDocument();
	if ( pDoc->GetAsmList()->IsEmpty() ) {
		return;
	}

	// Only one node in this list
	ASSERT(1 == m_nTotalPage);
	CObList* pAsmList = ((CAssemblyData*)(pDoc->GetAsmList()->GetHead()))->GetList();

	// Show assemble code
	int nLine(0);
	POSITION pos = pAsmList->GetHeadPosition();
	while ( pos ) {
		// Get object
		CAssemblyCode* pObj = (CAssemblyCode*)pAsmList->GetNext(pos);
		CString strLabel(pObj->GetLabel());
		CString strAddr(pObj->GetAddr());
		CString strObj(pObj->GetObj());
		CString strInst(pObj->GetInst());

		// Normal attribute
		pDC->SetBkColor(CSourceDocument::m_clrWhite);
		pDC->SetTextColor(CSourceDocument::m_clrCode);

		// Show label if exist
		if ( !strLabel.IsEmpty() ) {
			CString str("");
			for ( int i(0); i < m_nObjWidth; i++ ) {
				str += ' ';
			}
			strLabel = str + strLabel;
			pDC->TextOut((m_nBitWidth+m_nNumWidth)*m_sizeFont.cx, nLine*m_sizeFont.cy, strLabel);
			++nLine;
		}

		// Show address
		pDC->TextOut(m_nBitWidth*m_sizeFont.cx, nLine*m_sizeFont.cy, strAddr);

		// Show Object & instruction
		CString strLine = strObj + ::szTab + strInst;
		pDC->TextOut((m_nBitWidth+m_nNumWidth)*m_sizeFont.cx, nLine*m_sizeFont.cy, strLine);

		// Next line
		++nLine;
	}
}

void CSourceView::PrintMixedCode(CDC* pDC)
{
	// Get document
	CSourceDocument* pDoc = (CSourceDocument*)GetDocument();
	CObList* pSrcList = pDoc->GetSrcList();
	CObList* pAsmList = pDoc->GetAsmList();
	if ( pSrcList->IsEmpty() ) {
		return;
	}
	if ( pAsmList->IsEmpty() ) {
		return;
	}

	// Get the list index according to the scroll position
	int nSrcIndex(0), nAsmIndex(-1);
	GetListIndex((m_nCurPage-1)*(m_sizePage.cy-1), pSrcList, pAsmList, nSrcIndex, nAsmIndex);
	ASSERT(nSrcIndex >= 0 && nAsmIndex >= -1);

	// Show source code
	int nLine(0), nLineNum(nSrcIndex);
	POSITION posSrc = pSrcList->FindIndex(nSrcIndex);
	while ( posSrc ) {
		// Show assembly code first
		int nLeft = GetLeftSpace() - m_nBitWidth;
		pDC->SetBkColor(CSourceDocument::m_clrWhite);
		if ( nAsmIndex > -1 ) {
			// Show assembly code
			POSITION posAsm = pAsmList->FindIndex(nSrcIndex);
			if ( posAsm && -1 != ((CAssemblyData*)pAsmList->GetAt(posAsm))->GetLine() ) {
				CObList* pAsmList = ((CAssemblyData*)pAsmList->GetAt(posAsm))->GetList();
				POSITION pos = pAsmList->FindIndex(nAsmIndex);
				while ( pos ) {
					// Get object
					CAssemblyCode* pObj = (CAssemblyCode*)pAsmList->GetNext(pos);
					CString strLabel(pObj->GetLabel());
					CString strAddr(pObj->GetAddr());
					CString strObj(pObj->GetObj());
					CString strInst(pObj->GetInst());
				
					// Normal attribute
					pDC->SetTextColor(CSourceDocument::m_clrCode);
				
					// Show label if exist
//					if ( !strLabel.IsEmpty() ) {
//						CString str("");
//						for ( int i(0); i < m_nObjWidth+m_nAddrWidth; i++ ) {
//							str += ' ';
//						}
//						strLabel = str + strLabel;
//						pDC->TextOut(nLeft*m_sizeFont.cx, nLine*m_sizeFont.cy, strLabel);
//						if ( ++nLine >= m_sizePage.cy-1 ) {
//							return;
//						}
//					}
		
					// Show Address, Object & Instruction
					CString strLine = strAddr + ::szTab + strObj + ::szTab + strInst;
					pDC->TextOut(nLeft*m_sizeFont.cx, nLine*m_sizeFont.cy, strLine);
		
					// Next line
					if ( ++nLine >= m_sizePage.cy-1 ) {
						return;
					}
				}
			}
		}

		// Get source data object
		CSourceData* pSrcObj = (CSourceData*)pSrcList->GetNext(posSrc);
		if ( nAsmIndex > -1 ) {
			nAsmIndex = -1;
			nLineNum++;
			continue;
		}

		// Show line number
		if ( m_bLine ) {
			CString strNum("");
			strNum.Format(::szLineFormat, nLineNum+1);
			pDC->SetTextColor(CSourceDocument::m_clrCode);
			pDC->TextOut(0*m_sizeFont.cx, nLine*m_sizeFont.cy, strNum);
		}

		// Without syntax color
		CString strLine(pSrcObj->GetLine());
		// Printer doesn't support Gray color
//		pDC->SetTextColor(CSourceDocument::m_clrGray);
		pDC->SetTextColor(CSourceDocument::m_clrCode);
		pDC->TextOut(nLeft*m_sizeFont.cx, nLine*m_sizeFont.cy, strLine);

		// Next line
		nLineNum++;
		if ( ++nLine >= m_sizePage.cy-1 ) {
			return;
		}

		// Show assembly code
		POSITION posAsm = pAsmList->FindIndex(nLineNum-1);
		if ( posAsm && -1 != ((CAssemblyData*)pAsmList->GetAt(posAsm))->GetLine() ) {
			CObList* pAsmList = ((CAssemblyData*)pAsmList->GetAt(posAsm))->GetList();
			POSITION pos = pAsmList->GetHeadPosition();
			while ( pos ) {
				// Get object
				CAssemblyCode* pObj = (CAssemblyCode*)pAsmList->GetNext(pos);
				CString strLabel(pObj->GetLabel());
				CString strAddr(pObj->GetAddr());
				CString strObj(pObj->GetObj());
				CString strInst(pObj->GetInst());
		
				// Normal attribute
				pDC->SetTextColor(CSourceDocument::m_clrCode);
		
				// Show label if exist
//				if ( !strLabel.IsEmpty() ) {
//					CString str("");
//					for ( int i(0); i < m_nObjWidth+m_nAddrWidth; i++ ) {
//						str += ' ';
//					}
//					strLabel = str + strLabel;
//					pDC->TextOut(nLeft*m_sizeFont.cx, nLine*m_sizeFont.cy, strLabel);
//					if ( ++nLine >= m_sizePage.cy-1 ) {
//						return;
//					}
//				}

				// Show Address, Object & Instruction
				CString strLine = strAddr + ::szTab + strObj + ::szTab + strInst;
				pDC->TextOut(nLeft*m_sizeFont.cx, nLine*m_sizeFont.cy, strLine);

				// Next line
				if ( ++nLine >= m_sizePage.cy-1 ) {
					return;
				}
			}
		}
	}
}

void CSourceView::PrintSourceCode(CDC* pDC)
{
	// Get document
	CSourceDocument* pDoc = (CSourceDocument*)GetDocument();
	CObList* pSrcList = pDoc->GetSrcList();
	if ( pSrcList->IsEmpty() ) {
		return;
	}

	// Show source code
	int nLine(0), nLineNum((m_nCurPage-1)*(m_sizePage.cy-1));
	POSITION pos = pSrcList->FindIndex(nLineNum);

	while ( pos ) {
		// Get source data object
		CSourceData* pObj = (CSourceData*)pSrcList->GetNext(pos);

		// Show line number
		pDC->SetTextColor(CSourceDocument::m_clrCode);
		pDC->SetBkColor(CSourceDocument::m_clrWhite);
		if ( m_bLine ) {
			CString strNum("");
			strNum.Format(::szLineFormat, nLineNum+1);
			pDC->TextOut(0*m_sizeFont.cx, nLine*m_sizeFont.cy, strNum);
		}

		// Print one line
		int nLeft = GetLeftSpace() - m_nBitWidth;
		CString strLine = pObj->GetLine();

		// Without syntax color: Default preview mode doesn't support color
		pDC->TextOut(nLeft*m_sizeFont.cx, nLine*m_sizeFont.cy, strLine);

		// Next line
		++nLineNum;
		if ( ++nLine >= m_sizePage.cy-1 ) {
			return;
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
// CSourceView printing

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

void CSourceView::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo)
{
	// TODO: add extra initialization before printing
    	
	// Set DEVICE_DEFAULT_FONT size for Printing
	pDC->SelectStockObject(DEVICE_DEFAULT_FONT);

    TEXTMETRIC tm;
    pDC->GetTextMetrics(&tm);	// m_sizeFont(3c,78)
    m_sizeFont.cx = tm.tmAveCharWidth;
    m_sizeFont.cy = tm.tmHeight + tm.tmExternalLeading;

	// Get page size: A4 line number = (77, 56)
	m_sizePage = CSize(pDC->GetDeviceCaps(HORZRES)/m_sizeFont.cx, pDC->GetDeviceCaps(VERTRES)/m_sizeFont.cy);
	
	// Set max page
	m_nTotalPage = ((CSourceDocument*)GetDocument())->GetTotalPage(m_sizePage, GetLeftSpace());
	pInfo->SetMaxPage(m_nTotalPage);
}

void CSourceView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: add cleanup after printing

	// Retrieve ANSI_FIXED_FONT size for Painting
    CDC* pDC = GetDC();
    pDC->SelectStockObject(ANSI_FIXED_FONT);

    TEXTMETRIC tm;
    pDC->GetTextMetrics(&tm);	// m_sizeFont(8,d)
    m_sizeFont.cx = tm.tmAveCharWidth;
    m_sizeFont.cy = tm.tmHeight + tm.tmExternalLeading;

    ReleaseDC(pDC);
}


/////////////////////////////////////////////////////////////////////////////
// CSourceView diagnostics

#ifdef _DEBUG
void CSourceView::AssertValid() const
{
	CTextView::AssertValid();
}

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


/////////////////////////////////////////////////////////////////////////////
// CSourceView message handlers

void CSourceView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	// TODO: Add your message handler code here and/or call default

	// Repaint if necessary
	if ( OnHScrollBy(nSBCode, nPos) ) {
		// Clear token mark
		ClearTokenMark(FALSE);

		// Update window
		int nLeft = GetLeftSpace() * m_sizeFont.cx;
		InvalidateRect(CRect(nLeft, 0, m_rectClient.right, m_rectClient.bottom));

		// Adjust caret position
		int xMin, xMax;
		GetScrollRange(SB_HORZ, &xMin, &xMax);
		GetScrollSize();
		if ( m_ptCaret.x + m_sizeScroll.cx + 1 > xMax ) {
			m_ptCaret.x = max(GetLeftSpace(), xMax-m_sizeScroll.cx-1);
			MoveCaretPos();
		}
	}

	CTextView::OnHScroll(nSBCode, nPos, pScrollBar);
}

void CSourceView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	// TODO: Add your message handler code here and/or call default
	
	// Get ASM code
	if ( GetMode() == ::modAsm ) {
		ScrollAsmCode(nSBCode, nPos);
		return;
	}

	// Repaint if necessary
	if ( OnVScrollBy(nSBCode, nPos) ) {
		// Clear token mark
		ClearTokenMark(FALSE);

		// Update window
		InvalidateRect(m_rectClient);

		// Adjust caret position
		int yMin, yMax;
		GetScrollRange(SB_VERT, &yMin, &yMax);
		GetScrollSize();
		if ( m_ptCaret.y + m_sizeScroll.cy + 1 > yMax - 1 ) {
			m_ptCaret.y = max(0, yMax-1-m_sizeScroll.cy-1);
			MoveCaretPos();
		}
	}

	CTextView::OnVScroll(nSBCode, nPos, pScrollBar);
}

void CSourceView::OnSize(UINT nType, int cx, int cy) 
{
	CTextView::OnSize(nType, cx, cy);
	
	// TODO: Add your message handler code here

	// Get the client rect
	CSize sizeOld = m_sizeClient;
	GetClientRect(m_rectClient);
	m_sizeClient = CSize(m_rectClient.Width()/m_sizeFont.cx, m_rectClient.Height()/m_sizeFont.cy);

	// Clear previous token
	ClearTokenMark();

	// Refresh ASM code
	if ( GetMode() == ::modAsm && sizeOld.cy < m_sizeClient.cy ) {
		// Check CPU status
	    if ( !GetCpuStatus() ) {
			::MessageBeep(-1);
			return;
		}

		CSourceAddr AddrStart;
		if ( GetStartAddr(AddrStart) ) {
			SetAsmRange(AddrStart);
			// Update the document content
			((CSourceDocument*)GetDocument())->SetAction(CSourceDocument::getAsm);
			((CSourceDocument*)GetDocument())->UpdateDocument();
		}
	}

	// Set scroll size
	SetScrollSizes();
}

void CSourceView::OnUpdateViewLinenumbering(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here

	// Default: has line number
	pCmdUI->Enable(GetMode() != ::modAsm);
	pCmdUI->SetCheck(m_bLine ? 1 : 0);
}

void CSourceView::OnViewLinenumbering() 
{
	// TODO: Add your command handler code here

	// Set flag & Update window
	m_bLine = !m_bLine;

	// Set scroll size
	SetScrollSizes();

	// Clear token mark
	ClearTokenMark(FALSE);

	// Set caret position
	if ( m_bLine && m_ptCaret.x < m_nBitWidth+m_nNumWidth ) {
		m_ptCaret.x = m_nBitWidth+m_nNumWidth;
		MoveCaretPos();
	}

	// Update window
	InvalidateRect(CRect(m_nBitWidth*m_sizeFont.cx, 0, m_rectClient.right, m_rectClient.bottom));
}

void CSourceView::OnUpdateViewSyntaxcoloring(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here

	// Default: has syntax color
	pCmdUI->Enable(GetMode() != ::modAsm);
	pCmdUI->SetCheck(m_bSyntaxColor ? 1 : 0);
}

void CSourceView::OnViewSyntaxcoloring() 
{
	// TODO: Add your command handler code here
	
	// Clear token mark
	ClearTokenMark(FALSE);

	// Set flag & Update window
	m_bSyntaxColor = !m_bSyntaxColor;
	int nLeft = GetLeftSpace() * m_sizeFont.cx;
	InvalidateRect(CRect(nLeft, 0, m_rectClient.right, m_rectClient.bottom));
}

void CSourceView::OnUpdateViewSymbolicdisassembly(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here

	// Check CPU status
	if ( !GetCpuStatus() ) {
		pCmdUI->Enable(FALSE);
	}
	else {
		// Default: has symbol
		pCmdUI->Enable(GetMode() != ::modSrc);
		pCmdUI->SetCheck(m_bSymbolic ? 1 : 0);
	}
}

void CSourceView::OnViewSymbolicdisassembly() 
{
	// TODO: Add your command handler code here
	
	// Set flag & Update window
	m_bSymbolic = !m_bSymbolic;
	::bSrcSymbolic = m_bSymbolic;

//	int nLeft = GetLeftSpace() * m_sizeFont.cx;
//	InvalidateRect(CRect(nLeft, 0, m_rectClient.right, m_rectClient.bottom));
	if ( GetMode() == ::modAsm ) {
		DisplaySourceContent(CSourceDocument::getAsm);
	}
	else if ( GetMode() == ::modMix ) {
		DisplaySourceContent(CSourceDocument::getMix);
	}
}

void CSourceView::OnMouseMove(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default

	// Data tip operation
	CPoint ptNow(point.x/m_sizeFont.cx, point.y/m_sizeFont.cy);
	if ( m_ptTip != ptNow ) {
		m_ptTip = ptNow;
		SetDataTip();
	}

	CTextView::OnMouseMove(nFlags, point);
}

void CSourceView::OnUpdateViewAssemble(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	if ( !GetCpuStatus() ) {
		pCmdUI->Enable(FALSE);
	}
	else {
		// Indicate display mode
		pCmdUI->SetRadio(GetMode() == ::modAsm);
	}
}

void CSourceView::OnViewAssemble() 
{
	// TODO: Add your command handler code here
	
	// Assemble mode
	if ( GetMode() != ::modAsm ) {
		CSourceAddr AddrStart;
		if ( GetStartAddr(AddrStart) ) {
			((CSourceDocument*)GetDocument())->SetMode(::modAsm);
			SetAsmRange(AddrStart);
			DisplaySourceContent(CSourceDocument::getAsm);
		}
		else {
			::MessageBeep(-1);
		}
	}
}

void CSourceView::OnUpdateViewMixed(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	if ( !GetCpuStatus() ) {
		pCmdUI->Enable(FALSE);
	}
	else {
		// Indicate display mode
		pCmdUI->Enable(IsLoadSource());
		pCmdUI->SetRadio(GetMode() == ::modMix);
	}
}

void CSourceView::OnViewMixed() 
{
	// TODO: Add your command handler code here
	
	// Mixed mode
	if ( GetMode() != ::modMix ) {
		CSourceAddr Addr;
		CString strModulePath;
		SYM_DESCRIPTOR dwModuleDesc;
	    if ( GetMode() == ::modSrc ) {
	    	// Show source
			((CSourceDocument*)GetDocument())->SetMode(::modMix);
			DisplaySourceContent(CSourceDocument::getAsm);

			// Scroll to source line in Mixed mode
			int nLine;
			GetMixedLine(m_sizeScroll.cy, nLine);
			SendMessage(WM_VSCROLL, SB_THUMBTRACK, MAKELONG(nLine, NULL));
	    }
	    else if ( IsInSourceRange(Addr, strModulePath, dwModuleDesc) ) {
			((CSourceDocument*)GetDocument())->SetMode(::modMix);
			if ( 0 == ::nSrcModuleDepth || ((CSourceDocument*)GetDocument())->GetPath() != strModulePath ) {
				// Add to module descriptor list
				AddModuleDepth(dwModuleDesc);

			    // Using another module
				SetModulePath(strModulePath);
				DisplaySourceContent(CSourceDocument::getMix);
			}
			else {
				DisplaySourceContent(CSourceDocument::getAsm);
			}
			ScrollToAddr(Addr);
		}
		else {
			::MessageBeep(-1);
		}
	}
}

void CSourceView::OnUpdateViewSource(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	if ( !GetCpuStatus() ) {
		pCmdUI->Enable(FALSE);
	}
	else {
		// Indicate display mode
		pCmdUI->Enable(IsLoadSource());
		pCmdUI->SetRadio(GetMode() == ::modSrc);
	}
}

void CSourceView::OnViewSource() 
{
	// TODO: Add your command handler code here

	// Source mode
	if ( GetMode() != ::modSrc ) {
		CSourceAddr Addr;
		CString strModulePath;
		SYM_DESCRIPTOR dwModuleDesc;
	    if ( GetMode() == ::modMix ) {
			// Get the list index according to the scroll position
			CSourceDocument* pDoc = (CSourceDocument*)GetDocument();
			int nSrcIndex(0), nAsmIndex(-1);
			GetListIndex(m_sizeScroll.cy, pDoc->GetSrcList(), pDoc->GetAsmList(), nSrcIndex, nAsmIndex);
			ASSERT(nSrcIndex >= 0 && nAsmIndex >= -1);

			// Show in SRC mode
			((CSourceDocument*)GetDocument())->SetMode(::modSrc);
			ShowTitle();
			SetScrollSizes();
			InvalidateRect(NULL);

			// Scroll to source line
			CSize size(((CSourceDocument*)GetDocument())->GetTotalSize());
			if ( m_sizeClient.cy >= size.cy ) {
				SendMessage(WM_VSCROLL, SB_TOP);
			}
			else {
				SendMessage(WM_VSCROLL, SB_THUMBTRACK, MAKELONG(nSrcIndex, NULL));
			}
	    }
	    else if ( IsInSourceRange(Addr, strModulePath, dwModuleDesc) ) {
			((CSourceDocument*)GetDocument())->SetMode(::modSrc);
			if ( 0 == ::nSrcModuleDepth || ((CSourceDocument*)GetDocument())->GetPath() != strModulePath ) {
				// Add to module descriptor list
				AddModuleDepth(dwModuleDesc);
		
			    // Using another module
				SetModulePath(strModulePath);
				DisplaySourceContent(CSourceDocument::getSrc);
			}
			else {
				ShowTitle();
				SetScrollSizes();
				InvalidateRect(NULL);
			}
			ScrollToAddr(Addr);
		}
		else {
			::MessageBeep(-1);
		}
	}
}

int CSourceView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CTextView::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	// TODO: Add your specialized creation code here
	
	// Create the tip window
	if ( !m_wndTip.Create() ) {
		ASSERT(FALSE);
		return -1;
	}

    // Calc the fixed font size
    CDC* pDC = GetDC();
    pDC->SelectStockObject(ANSI_FIXED_FONT);

    TEXTMETRIC tm;
    pDC->GetTextMetrics(&tm);
    m_sizeFont.cx = tm.tmAveCharWidth;
    m_sizeFont.cy = tm.tmHeight + tm.tmExternalLeading;

    ReleaseDC(pDC);

	SetFontSize(m_sizeFont);

	return 0;
}

void CSourceView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// TODO: Add your message handler code here and/or call default

	// Hide tip window
	HideDataTip();

	// Clear token mark
	ClearTokenMark();

	// Get scroll range
	int yMin, yMax, xMin, xMax;
	GetScrollRange(SB_VERT, &yMin, &yMax);
	GetScrollRange(SB_HORZ, &xMin, &xMax);
	int nLeft = GetLeftSpace();

	// Dispatch the key process
	switch ( nChar ) {
    case VK_UP:
    	if ( --m_ptCaret.y < 0 ) {
    		m_ptCaret.y = 0;
    		SendMessage(WM_VSCROLL, SB_LINEUP);
    	}
    	else {
    		MoveCaretPos();
    	}
    	break;
    case VK_DOWN:
    	if ( m_ptCaret.y + m_sizeScroll.cy + 1 == yMax - 1 ) {
			//SetScrollPos(SB_VERT, yMax);
    		break;
    	}
    	else if ( ++m_ptCaret.y > m_sizeClient.cy-1 ) {
    		m_ptCaret.y = m_sizeClient.cy-1;
    		SendMessage(WM_VSCROLL, SB_LINEDOWN);
    	}
    	else {
    		MoveCaretPos();
    	}
    	break;
    case VK_PRIOR:
    	SendMessage(WM_VSCROLL, SB_PAGEUP);
    	break;
    case VK_NEXT:
    	SendMessage(WM_VSCROLL, SB_PAGEDOWN);
    	break;
    case VK_LEFT:
    	if ( --m_ptCaret.x < nLeft ) {
    		m_ptCaret.x = nLeft;
    		SendMessage(WM_HSCROLL, SB_LINELEFT);
    	}
    	else {
    		MoveCaretPos();
    	}
    	break;
    case VK_RIGHT:
    	if ( m_ptCaret.x + m_sizeScroll.cx + 1 == xMax ) {
			//SetScrollPos(SB_HORZ, xMax);
    		break;
    	}
    	else if ( ++m_ptCaret.x > m_sizeClient.cx-1 ) {
    		m_ptCaret.x = m_sizeClient.cx-1;
    		SendMessage(WM_HSCROLL, SB_LINERIGHT);
    	}
    	else {
    		MoveCaretPos();
    	}
    	break;
	case VK_HOME:
    	SendMessage(WM_HSCROLL, SB_LEFT);
    	m_ptCaret.x = nLeft;
    	MoveCaretPos();
    	break;
	case VK_END:
		if ( GetMode() == ::modSrc ) {
			CObList* pSrcList = ((CSourceDocument*)GetDocument())->GetSrcList();
			POSITION pos = pSrcList->FindIndex(m_ptCaret.y+m_sizeScroll.cy);
			int nCol = ((CSourceData*)pSrcList->GetAt(pos))->GetLine().GetLength()-1;
			
			// Scroll to position
			ScrollToPosition(m_ptCaret.y+m_sizeScroll.cy, nCol);
			
			// Set caret
			m_ptCaret.x = nLeft + nCol - m_sizeScroll.cx + 1;
    		MoveCaretPos();
		}
    	break;
    default:
    	break;
    }
		
	CTextView::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CSourceView::OnSetFocus(CWnd* pOldWnd) 
{
	CTextView::OnSetFocus(pOldWnd);
	
	// TODO: Add your message handler code here

	// Create the caret shape & show it
    CreateSolidCaret(2, m_sizeFont.cy);
	MoveCaretPos();
}

void CSourceView::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default

	// Hide tip window
	HideDataTip();

	// Set focus
	SetFocus();

	// Clear token mark
	ClearTokenMark();

	// Get mouse cursor position
	CClientDC dc(this);
	OnPrepareDC(&dc);
	dc.DPtoLP(&point);

	int nLeft = GetLeftSpace() * m_sizeFont.cx;
    CRect rectCode(nLeft, 0, m_rectClient.right, m_sizeClient.cy*m_sizeFont.cy);
    CRect rectBP(0, 0, nLeft, m_sizeClient.cy*m_sizeFont.cy);

	m_ptOldCaret = m_ptCaret;
	m_ptCaret = CPoint(point.x/m_sizeFont.cx, point.y/m_sizeFont.cy);

	int yMin, yMax, xMin, xMax;
	GetScrollRange(SB_VERT, &yMin, &yMax);
	GetScrollRange(SB_HORZ, &xMin, &xMax);

//	if ( GetMode() == modAsm || m_ptCaret.y + m_sizeScroll.cy + 1 <= yMax - 1 && m_ptCaret.x + m_sizeScroll.cx + 1 <= xMax ) {
	    if ( rectCode.PtInRect(point) ) {
	    	// Set caret
	    	MoveCaretPos();
	    }
	    else if ( rectBP.PtInRect(point) && !m_bBrowse ) {
	    	// Set/Clear BP
	    	MarkBP();
	    }
	    else {
	    	// Move caret
			m_ptCaret = CPoint(max(m_ptCaret.x, GetLeftSpace()), m_ptCaret.y);
			MoveCaretPos();

			::MessageBeep(-1);
		}
//	}
//	else {
//		::MessageBeep(-1);
//		//m_ptCaret.y = yMax - 1 - m_sizeScroll.cy - 1;
//	}

	dc.LPtoDP(&point);
	
	CTextView::OnLButtonDown(nFlags, point);
}

void CSourceView::OnRButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default

	// Hide tip window
	HideDataTip();

	// Set focus
	SetFocus();

	// Load resource from this DLL
	HINSTANCE hInstEXE = AfxGetResourceHandle();
	AfxSetResourceHandle(::SrcGetDLLHandle());

	// Get menu position
    int nMenuPos;
    CString strItem;
    UINT uID;
    if ( IsMarkVariable() ) {
    	nMenuPos = localVariable;
    	uID = ID_LOCAL_WARCHVAR;
    	strItem.LoadString(IDS_LOCAL_WATCH);
    	strItem += m_strMarkToken;
    }
    else if ( IsMarkFunction() ) {
    	nMenuPos = localFunction;
    	uID = ID_LOCAL_INSPECTSOURCE;
    	strItem.LoadString(IDS_LOCAL_INSPECT);
    	strItem += m_strMarkToken;
    }
    else if ( !m_bBrowse ) {
		if ( (nFlags & MK_CONTROL) && (nFlags & MK_SHIFT) ) {
    		nMenuPos = localPrint;
    	}
    	else {
    		nMenuPos = localGeneral;
    	}
    }
    else {
		if ( (nFlags & MK_CONTROL) && (nFlags & MK_SHIFT) ) {
    		nMenuPos = localPrint;
    	}
    	else {
    		nMenuPos = localBrowse;
    	}
    }
    
    // Create the local menu
    CMenu menuLocal;
    if ( menuLocal.LoadMenu(IDR_LOCAL_SOURCE) ) {
        CMenu* pSubMenu = menuLocal.GetSubMenu(nMenuPos);
        if ( pSubMenu ) {
        	if ( localVariable == nMenuPos || localFunction == nMenuPos ) {
        		// Append variable / function name
        		pSubMenu->ModifyMenu(uID, MF_STRING, uID, strItem);
        	}
            ClientToScreen(&point);
            pSubMenu->TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, GetParent());
        }
        menuLocal.DestroyMenu();
    }

    // Restore the old resource chain
	AfxSetResourceHandle(hInstEXE);

	CTextView::OnRButtonDown(nFlags, point);
}

void CSourceView::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	
	// Hide tip window
	HideDataTip();

	// Set focus
	SetFocus();

	// Clear token mark
	ClearTokenMark();

	// Get mouse cursor position
	CClientDC dc(this);
	OnPrepareDC(&dc);
	dc.DPtoLP(&point);

	int nLeft = GetLeftSpace() * m_sizeFont.cx;
    CRect rectCode(nLeft, 0, m_rectClient.right, m_sizeClient.cy*m_sizeFont.cy);

	m_ptOldCaret = m_ptCaret;
	m_ptCaret = CPoint(point.x/m_sizeFont.cx, point.y/m_sizeFont.cy);

	int yMin, yMax, xMin, xMax;
	GetScrollRange(SB_VERT, &yMin, &yMax);
	GetScrollRange(SB_HORZ, &xMin, &xMax);

//	if ( GetMode() == modAsm || m_ptCaret.y + m_sizeScroll.cy + 1 <= yMax - 1 && m_ptCaret.x + m_sizeScroll.cx + 1 <= xMax ) {
	    if ( rectCode.PtInRect(point) ) {
	    	// Set caret
	    	MoveCaretPos();
	    	
	    	// Mark token
	    	if ( !m_bBrowse ) {
		    	MarkToken();
		    }
	    }
//	}
//	else {
//		::MessageBeep(-1);
//		//m_ptCaret.y = yMax - 1 - m_sizeScroll.cy - 1;
//	}

	dc.LPtoDP(&point);
	
	CTextView::OnLButtonDblClk(nFlags, point);
}

void CSourceView::OnOptionsLoadoption() 
{
	// TODO: Add your command handler code here
	
	// Open LoadOption page
	::nSrcGroupEntry = m_bBrowse ? ::SRC_BROWSE : ::SRC_SOURCE;
	CSourceSheet sheet(NULL, CSourceSheet::pageLoadOption);
	sheet.DoModal();
}

void CSourceView::OnOptionsSourceextensionname() 
{
	// TODO: Add your command handler code here
	
	// Open LoadOption page
	::nSrcGroupEntry = m_bBrowse ? ::SRC_BROWSE : ::SRC_SOURCE;
	CSourceSheet sheet(NULL, CSourceSheet::pageExtName);
	sheet.DoModal();
}

void CSourceView::OnUpdateFileBrowsemodule(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here

	// Check CPU status
	if ( !GetCpuStatus() ) {
		pCmdUI->Enable(FALSE);
	}
	else {
		// Grayed if no module info
		pCmdUI->Enable(IsLoadSource());
	}
}

void CSourceView::OnFileBrowsemodule() 
{
	// TODO: Add your command handler code here
	
	// Open BrowseModule page
	::nSrcGroupEntry = m_bBrowse ? ::SRC_BROWSE : ::SRC_SOURCE;
	CSourceSheet sheet(NULL, CSourceSheet::pageModule);
	sheet.DoModal();
}

void CSourceView::OnOptionsSourcepath() 
{
	// TODO: Add your command handler code here
	
	// Open SourcePath page
	::nSrcGroupEntry = m_bBrowse ? ::SRC_BROWSE : ::SRC_SOURCE;
	CSourceSheet sheet(NULL, CSourceSheet::pagePath);
	sheet.DoModal();
}


void CSourceView::OnFileLoad()
{
	// TODO: Add your command handler code here
	
	// Open file load dialog
	::SetCurLoadPath();
	::SrcOnFileLoad();
	::RestoreLastPath();
}


void CSourceView::OnRunBreakpoint()
{
	// TODO: Add your command handler code here
	
	// Open BrowseModule page
	::nSrcGroupEntry = m_bBrowse ? ::SRC_BROWSE : ::SRC_SOURCE;
	CSourceSheet sheet(NULL, CSourceSheet::pageBp);
	sheet.DoModal();
}

void CSourceView::OnUpdateEditSearch(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here

	// Only Source Mode	
	pCmdUI->Enable(GetMode() == ::modSrc);
}

void CSourceView::OnEditSearch()
{
	// TODO: Add your command handler code here

	// Open Search dialog
	if ( !::pSrcSearchDlg ) {
		::pSrcSearchDlg = new CSourceSearchDlg(this);
	}
	else {
		::pSrcSearchDlg->SetFocus();
	}
}

void CSourceView::OnUpdateEditSearchnext(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Get token from Search combo-box
	CString strToken = ::SrcGetBarToken();

	// Only Source Mode	
	pCmdUI->Enable(GetMode() == ::modSrc && !strToken.IsEmpty());
}

void CSourceView::OnEditSearchnext()
{
	// TODO: Add your command handler code here

	// Get token from Search combo-box
	m_strSearchToken = ::SrcGetBarToken();

	// Add to global list without same token
	POSITION pos = ::pSourceSearchList->GetHeadPosition();
	while ( pos ) {
		POSITION posOld(pos);
		if ( ::pSourceSearchList->GetNext(pos) == m_strSearchToken ) {
			::pSourceSearchList->RemoveAt(posOld);
			break;
		}
	}
	::pSourceSearchList->AddHead(m_strSearchToken);

	// Add to toolbar combo-box without same token
	::SrcSetBarCombo();

	// Add to dialog combo-box without same token
	if ( ::pSrcSearchDlg ) {
		::pSrcSearchDlg->m_comboWhat.ResetContent();
		pos = ::pSourceSearchList->GetHeadPosition();
		while ( pos ) {
			::pSrcSearchDlg->m_comboWhat.AddString(::pSourceSearchList->GetNext(pos));
		}
		::pSrcSearchDlg->m_comboWhat.SetCurSel(0);
	}

	// Search in Source view
	SearchToken();
}

void CSourceView::OnGroupSource()
{
	// TODO: Add your command handler code here
	
	// Open LoadOption page
	::nSrcGroupEntry = m_bBrowse ? ::SRC_BROWSE : ::SRC_SOURCE;
	CSourceSheet sheet;
	sheet.DoModal();
}

void CSourceView::OnUpdateFileLoadinfo(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	if ( !GetCpuStatus() ) {
		pCmdUI->Enable(FALSE);
	}
	else {
		// Only Source Mode
		pCmdUI->Enable(IsLoadSource());
	}
}

void CSourceView::OnFileLoadinfo()
{
	// TODO: Add your command handler code here
	
	// Open LoadInformation page
	::nSrcGroupEntry = ::SRC_SOURCE;
	CSourceSheet sheet(NULL, CSourceSheet::pageInfo);
	sheet.DoModal();
}

void CSourceView::OnUpdateFilePrevmodule(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here

	// Check CPU status
	if ( !GetCpuStatus() ) {
		pCmdUI->Enable(FALSE);
	}
	else {
		// Has previous module
		pCmdUI->Enable(::nSrcModuleIndex > 0);
	}
}

void CSourceView::OnFilePrevmodule()
{
	// TODO: Add your command handler code here

	// Previous browsed module
	::nSrcModuleIndex--;

	// Browse one module
	::nSrcGroupEntry = m_bBrowse ? ::SRC_BROWSE : ::SRC_SOURCE;
	::SrcBrowseModule();
}

void CSourceView::OnUpdateFileNextmodule(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	if ( !GetCpuStatus() ) {
		pCmdUI->Enable(FALSE);
	}
	else {
		// Has next module
		pCmdUI->Enable(::nSrcModuleIndex >= 0 && ::nSrcModuleIndex < ::nSrcModuleDepth-1 );
	}
}

void CSourceView::OnFileNextmodule()
{
	// TODO: Add your command handler code here
	
	// Next browsed module
	::nSrcModuleIndex++;

	// Browse one module
	::nSrcGroupEntry = m_bBrowse ? ::SRC_BROWSE : ::SRC_SOURCE;
	::SrcBrowseModule();
}

void CSourceView::OnEditBrowsefrom()
{
	// TODO: Add your command handler code here

	// Open Browse From dialog
	CSourceBrowseFromDlg dlg;
	if ( IDOK == dlg.DoModal() ) {
		if ( dlg.m_bPC ) {
			// Scroll to PC
			BrowseFromAddr(m_PC);
		}
		else {
			// Scroll to head of the list
			CString strAddr = ::pSourceFromList->GetHead();
			CSourceAddr Addr;
			if ( ::SrcTextToAddr(strAddr, Addr) ) {
				if ( GetMode() == ::modMix && strAddr.Find('#') == -1 ) {
					((CSourceDocument*)GetDocument())->SetMode(::modAsm);
				}
				BrowseFromAddr(Addr);
			}
		}
	}
}

void CSourceView::OnTimer(UINT nIDEvent)
{
	// TODO: Add your message handler code here and/or call default
	
	// Our timer: Data tip
	if ( m_uTimerID == nIDEvent ) {
		// Kill timer
		KillTimer(m_uTimerID);
		m_uTimerID = 0;
		// Show tip
		CPoint pt(m_ptTip.x*m_sizeFont.cx, m_ptTip.y*m_sizeFont.cy);
		ClientToScreen(&pt);
		m_wndTip.UpdateTip(m_strTip, pt);
	}
	else {
		// Not our timer
		CTextView::OnTimer(nIDEvent);
	}
}

void CSourceView::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO: Add your message handler code here and/or call default
	
	// Hide tip window
	HideDataTip();

	CTextView::OnSysKeyDown(nChar, nRepCnt, nFlags);
}

void CSourceView::OnDestroy()
{
	CTextView::OnDestroy();
	
	// TODO: Add your message handler code here
	
	// Destroy tip window
	m_wndTip.DestroyWindow();
}

void CSourceView::OnKillFocus(CWnd* pNewWnd)
{
	CTextView::OnKillFocus(pNewWnd);
	
	// TODO: Add your message handler code here
    
	// Destroy & hide caret
	HideCaret();
	::DestroyCaret();
}

UINT CSourceView::OnNcHitTest(CPoint point)
{
	// TODO: Add your message handler code here and/or call default

	// Hide tip if not within client area
	UINT uHit = CTextView::OnNcHitTest(point);
	if ( HTCLIENT != uHit ) {
		HideDataTip();
	}
	
	return uHit;
}

void CSourceView::OnFilePrint() 
{
	// TODO: Add your command handler code here
	
	// Standard printing commands
	CView::OnFilePrint();
}

void CSourceView::OnFilePrintPreview() 
{
	// TODO: Add your command handler code here
	
	// Standard printing commands
	CView::OnFilePrintPreview();
}

void CSourceView::OnFilePrintSetup() 
{
	// TODO: Add your command handler code here
	
	// Standard print setup command
	::SrcPrintSetup();
}

BOOL CSourceView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
	// TODO: Add your message handler code here and/or call default

	// Not client
	if ( HTCLIENT != nHitTest ) {
		return CTextView::OnSetCursor(pWnd, nHitTest, message);
	}

    // Client coordinates of mouse position
	CPoint point;
	::GetCursorPos(&point);
	ScreenToClient(&point);

    // Change cursor
	int nLeft = GetLeftSpace();
    if ( point.x <= (nLeft-1)*m_sizeFont.cx && !m_bBrowse ) {
		// Load resource from this DLL
		HINSTANCE hInstEXE = AfxGetResourceHandle();
		AfxSetResourceHandle(::SrcGetDLLHandle());

		::SetCursor(AfxGetApp()->LoadCursor(IDC_BULLEYE));

	    // Restore the old resource chain
		AfxSetResourceHandle(hInstEXE);
    }
    else {
		::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
    }

	return 0;
}

void CSourceView::OnShiftf10() 
{
	// TODO: Add your command handler code here
	
    // Client coordinates of mouse position
	CPoint point;
	::GetCursorPos(&point);
	ScreenToClient(&point);

	// WM_RBUTTONDOWN: local menu
	SendMessage(WM_RBUTTONDOWN, 0, m_rectClient.PtInRect(point) ? MAKELONG(point.x, point.y) : 0);
}

void CSourceView::OnRunJump() 
{
	// TODO: Add your command handler code here

	// Open Jump dialog
	::SrcOnRunJump();

	// Update other windows
	UpdateOtherWindows();
}

void CSourceView::OnLocalWarchvar() 
{
	// TODO: Add your command handler code here

	// Add a variable to Variable window
	ASSERT(!m_strMarkToken.IsEmpty());
	CString strToken('#');
	if ( m_strMarkToken[0] != '#' ) {
		strToken += m_strMarkToken;
	}
	else {
		strToken = m_strMarkToken;
	}

	::SrcAddVariable(strToken);
}

void CSourceView::OnLocalInspectsource() 
{
	// TODO: Add your command handler code here

	// Get function range
	ASSERT(!m_strMarkToken.IsEmpty());
	CSourceAddr AddrStart, AddrEnd;
	if ( ::SrcGetFuncRange(m_strMarkToken, AddrStart, AddrEnd) ) {
		BrowseFromAddr(AddrStart);
	}
	else {
		::MessageBeep(-1);
	}
}

void CSourceView::OnLocalShowloadaddr() 
{
	// TODO: Add your command handler code here

	// Get function range
	ASSERT(!m_strMarkToken.IsEmpty());
	CSourceAddr AddrStart, AddrEnd;
	if ( ::SrcGetFuncRange(m_strMarkToken, AddrStart, AddrEnd) ) {
		CString strAddr1, strAddr2;
		if ( ::SrcAddrToText(AddrStart, strAddr1) && ::SrcAddrToText(AddrEnd, strAddr2) ) {
			CString strRange = strAddr1 + ::szLoadAddr + strAddr2;
			CSourceFunctionRangeDlg(m_strMarkToken, strRange).DoModal();
			return;
		}
	}

	::MessageBeep(-1);
}

void CSourceView::OnLocalSetbp() 
{
	// TODO: Add your command handler code here

	// Get function range
	ASSERT(!m_strMarkToken.IsEmpty());
	CSourceAddr AddrStart, AddrEnd;
	if ( ::SrcGetFuncRange(m_strMarkToken, AddrStart, AddrEnd) ) {
		::BptSetBpFromList(AddrStart);
	}
	else {
		::MessageBeep(-1);
	}
}

void CSourceView::OnLocalClearbp() 
{
	// TODO: Add your command handler code here

	// Get function range
	ASSERT(!m_strMarkToken.IsEmpty());
	CSourceAddr AddrStart, AddrEnd;
	if ( ::SrcGetFuncRange(m_strMarkToken, AddrStart, AddrEnd) ) {
		::BptClrBpFromList(AddrStart);
	}
	else {
		::MessageBeep(-1);
	}
}

void CSourceView::OnLocalSearchvar() 
{
	// TODO: Add your command handler code here

	// Add marked variable name into search list
	::SrcSetBarToken(m_strMarkToken);

	// Send WM_COMMAND message
	SendMessage(WM_COMMAND, ID_EDIT_SEARCHNEXT);
}

void CSourceView::OnRunJumptocursor() 
{
	// TODO: Add your command handler code here

	// Get address according to (m_ptCaret.y+m_sizeScroll.cy)
	CSourceAddr Addr;
	int nLine = GetMode() == ::modAsm ? m_ptCaret.y : m_ptCaret.y + m_sizeScroll.cy;
	if ( LineToAddr(nLine, Addr) ) {
		// Goto cursor location address

		if ( ::SrcSetPC(Addr) ) {
			::SrcUpdateSourceWindow(::srcReset, TRUE);
			// Update other windows
			UpdateOtherWindows();
		}
		else {
			::SrcDisplayErrorMessage(::errSetPC);
		}
	}
	else {
		::MessageBeep(-1);
	}
}

void CSourceView::OnViewSourcemode() 
{
	// TODO: Add your command handler code here

	// F3 hot key to switch Source window display mode: SRC => MIX => ASM => SRC
	switch ( GetMode() ) {
	case ::modSrc:
		SendMessage(WM_COMMAND, ID_VIEW_MIXED);
		break;
	case ::modMix:
		SendMessage(WM_COMMAND, ID_VIEW_ASSEMBLE);
		break;
	case ::modAsm:
		SendMessage(WM_COMMAND, ID_VIEW_SOURCE);
		break;
	default:
		ASSERT(FALSE);
		return;
	}
}

void CSourceView::OnToggleHints() 
{
	// TODO: Add your command handler code here
	
	// Toggle source hints
	if ( CSourceHintsDlg().DoModal() == IDOK ) {
		::bSrcHintsOn = !::bSrcHintsOn;
	}
	else {
		::bSrcHintsOn = FALSE;
	}
}

void CSourceView::OnUpdateToggleHints(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	if ( !GetCpuStatus() ) {
		pCmdUI->Enable(FALSE);
	}
	else {
		// Grayed if no hints
		if ( ::SrcIsLoadedSymbol() && ::SrcGetMode() == ::modSrc ) {
			pCmdUI->Enable(TRUE);
			pCmdUI->SetCheck(::bSrcHintsOn ? 1 : 0);
		}
		else {
			pCmdUI->Enable(FALSE);
		}
	}
}

void CSourceView::OnUpdateEditBrowsefrom(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here

	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateFileLoad(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateLocalClearbp(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateLocalSetbp(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateRunGotocursor(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateViewSourcemode(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateGroupSource(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateLocalInspectsource(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateLocalSearchvar(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateLocalShowloadaddr(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateLocalWarchvar(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateOptionsLoadoption(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateOptionsSourceextensionname(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateOptionsSourcepath(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateRunBreakpoint(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnUpdateRunJump(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	
	// Check CPU status
	pCmdUI->Enable(GetCpuStatus());
}

void CSourceView::OnRunGotocursor()
{
	// TODO: Add your command handler code here
	// Get address according to (m_ptCaret.y+m_sizeScroll.cy)
	CSourceAddr Addr;
	int nLine = GetMode() == ::modAsm ? m_ptCaret.y : m_ptCaret.y + m_sizeScroll.cy;
	if ( LineToAddr(nLine, Addr) ) {
		// Goto cursor location address
		::ADDR addr1, addr2;
		addr1.addrType = 0;
		addr2.addr = Addr.GetAddr();
		addr2.addrType = Addr.GetType();
		if ( ICE_OK != ::emuGo(::NORMAL_RUN, addr1, addr2) ) {
			::SrcDisplayErrorMessage(::errGo);
			return;
		}

		BeginWaitCursor();

		// Waiting if EP is running
		DWORD dwStatus;
		do {
			if ( ::IsTestKey(VK_ESCAPE) ) {
				::emuAbort();
				break;
			}

			// Check EP status
			if ( ICE_OK != ::emuGetCpuStatus(&dwStatus) ) {
				::SrcDisplayErrorMessage(::errCpuStatus);
				return;
			}

			if ( ::IsTestKey(VK_ESCAPE) ) {
				::emuAbort();
				break;
			}
		} while	( dwStatus & 0x10 );
		
		EndWaitCursor();

		// Update source window
		UpdateSourceWindow(::srcReset);

		// Update other windows
		UpdateOtherWindows();
	}
	else {
		::MessageBeep(-1);
	}
	
}

void CSourceView::OnUpdateRunJumptocursor(CCmdUI* pCmdUI)
{
	// TODO: Add your command update UI handler code here
	pCmdUI->Enable(GetCpuStatus());
}
