// DiagramEngDoc.cpp : implementation of the CDiagramEngDoc class
//

#include "stdafx.h"
#include "DiagramEng.h"
#include "DiagramEngDoc.h"
// DiagramEng (start)
#include <fstream>					// rqd. since ifstram/ofstream used for file i/o
#include <math.h>					// rqd. since OnDeleteItem() uses math fns.
#include "afxext.h"					// rqd. since TrackItem() uses a CRectTracker obj.
#include "BlockLibDlg.h"			// rqd. since OnEditAddMultipleBlocks() has a CBlockLibDlg obj.
//#include "TreeDialog.h"			// rqd. since OnEditAddMultipleBlocks() has a CTreeDialog obj.
#include "OutputBlockView.h"		// rqd. since DeleteOutputBlockView() declares a ptr-to-COutputBlockView
#include "MainFrm.h"				// rqd. since UpdateCommonOpsToolbar() uses a ptr-to-CMainFrame
#include "DiagramEngView.h"			// rqd. since GetDocumentGlobalFn() uses a ptr-to-CDiagramEngView
#include "Annotation.h"				// rqd. since OnFormatAnnotation() uses a ptr-to-CAnnotation
#include "DiagnosticInfoDialog.h"	// rqd. since OnToolsDiagnosticInfo() uses a CDiagnosticInfoDlg obj.
#include <windows.h>				// rqd. for OnToolsDiagnosticInfo() memory-related structures and fns.
//#include "psapi.h"				// rqd. for OnToolsDiagnosticInfo() memory-related structures and fns.
// DiagramEng (end)

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

// DiagramEng (start)
// theApp needs to be accessed from within the DiagramEngDoc.cpp file, although it's declared in the DiagramEng.cpp file.
// It is used in the fn GetDocumentGlobalFn() below.
extern CDiagramEngApp theApp;
// DiagramEng (end)

/////////////////////////////////////////////////////////////////////////////
// CDiagramEngDoc

IMPLEMENT_DYNCREATE(CDiagramEngDoc, CDocument)

BEGIN_MESSAGE_MAP(CDiagramEngDoc, CDocument)
	//{{AFX_MSG_MAP(CDiagramEngDoc)
	ON_COMMAND(ID_EDIT_SELECTALL, OnEditSelectAll)
	ON_COMMAND(ID_MODEL_BUILD, OnModelBuild)
	ON_COMMAND(ID_MODEL_BUILD_SUBSYS, OnModelBuildSubsystem)
	ON_COMMAND(ID_SIM_START, OnSimStart)
	ON_COMMAND(ID_SIM_STOP, OnSimStop)
	ON_COMMAND(ID_FORMAT_SHOW_ANNOTATIONS, OnFormatShowAnnotations)
	ON_COMMAND(ID_TOOLS_DIAGNOSTIC_INFO, OnToolsDiagnosticInfo)
	ON_COMMAND(ID_HELP_USING_DIA_ENG, OnHelpUsingDiagramEng)
	ON_COMMAND(ID_BLOCK_CTS_DERIVATIVE, OnBlockCtsDerivative)
	ON_COMMAND(ID_BLOCK_CTS_INTEGRATOR, OnBlockCtsIntegrator)
	ON_COMMAND(ID_BLOCK_CTS_TRANSFERFN, OnBlockCtsTransferfn)
	ON_COMMAND(ID_BLOCK_MATHOPS_DIVIDE, OnBlockMathopsDivide)
	ON_COMMAND(ID_BLOCK_MATHOPS_GAIN, OnBlockMathopsGain)
	ON_COMMAND(ID_BLOCK_MATHOPS_SUM, OnBlockMathopsSum)
	ON_COMMAND(ID_BLOCK_SINK_OUTPUT, OnBlockSinkOutput)
	ON_COMMAND(ID_BLOCK_SOURCE_CONST, OnBlockSourceConst)
	ON_COMMAND(ID_BLOCK_SOURCE_LINEARFN, OnBlockSourceLinearfn)
	ON_COMMAND(ID_BLOCK_SOURCE_SIGNALGEN, OnBlockSourceSignalgen)
	ON_COMMAND(ID_BLOCK_SUBSYS_SUBSYS, OnBlockSubsysSubsys)
	ON_COMMAND(ID_BLOCK_SUBSYS_SUBSYSIN, OnBlockSubsysSubsysin)
	ON_COMMAND(ID_BLOCK_SUBSYS_SUBSYSOUT, OnBlockSubsysSubsysout)
	ON_COMMAND(IDM_DELETE_ITEM, OnDeleteItem)
	ON_COMMAND(IDM_INSERT_BEND_POINT, OnInsertBendPoint)
	ON_COMMAND(ID_INIT_TRACK_MULTIPLE_ITEMS, OnInitTrackMultipleItems)
	ON_COMMAND(ID_SIM_NUM_SOLVER, OnSimNumericalSolver)
	ON_COMMAND(ID_EDIT_ADD_MULTI_BLOCKS, OnEditAddMultipleBlocks)
	ON_COMMAND(ID_EDIT_DELETE_GROUPED_ITEMS, OnEditDeleteGroupedItems)
	ON_COMMAND(IDM_SET_ITEM_PROPERTIES, OnSetItemProperties)
	ON_COMMAND(IDM_FINE_MOVE_ITEM, OnFineMoveItem)
	ON_COMMAND(IDM_REVERSE_BLOCK_DIREC, OnReverseBlockDirection)
	ON_COMMAND(IDM_SET_OUTPUT_SIGNAL, OnSetOutputSignal)
	ON_COMMAND(ID_FILE_SAVE, OnFileSave)
	ON_COMMAND(ID_FILE_SAVE_AS, OnFileSaveAs)
	ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
	ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
	ON_COMMAND(ID_EDIT_CUT, OnEditCut)
	ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
	ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
	ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
	ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
	ON_COMMAND(IDM_FORMAT_ANNOTATION, OnFormatAnnotation)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDiagramEngDoc construction/destruction

CDiagramEngDoc::CDiagramEngDoc()
{
	// TODO: add one-time construction code here

	// DiagramEng (start)
	m_iEditRedoFlag = 0;
	m_iEditUndoFlag = 0;
	m_iFineMoveFlag = 0;
	m_iIndexSystemModel = 0;
	m_iKeyFlagTrack = 0;
	m_iNSystemModels = 20;
	m_iRubberBandCreated = 0;
	m_iSelectAll = 0;
	m_dDeltaLength = 50.0;
	m_strFilePath = "";
	// DiagramEng (end)
}

CDiagramEngDoc::~CDiagramEngDoc()
{
	// Delete the list of pointers-to-CSystemModel "m_lstSystemModel"
	DeleteSystemModelList();		
}

BOOL CDiagramEngDoc::OnNewDocument()
{
	if (!CDocument::OnNewDocument())
		return FALSE;

	// TODO: add reinitialization code here
	// (SDI documents will reuse this document)

	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CDiagramEngDoc serialization

void CDiagramEngDoc::Serialize(CArchive& ar)
{
	if(ar.IsStoring())
	{
		// TODO: add storing code here
	}
	else
	{
		// TODO: add loading code here
	}
}

/////////////////////////////////////////////////////////////////////////////
// CDiagramEngDoc diagnostics

#ifdef _DEBUG
void CDiagramEngDoc::AssertValid() const
{
	CDocument::AssertValid();
}

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

/////////////////////////////////////////////////////////////////////////////
// CDiagramEngDoc commands

void CDiagramEngDoc::OnEditSelectAll() 
{
	// TODO: Add your command handler code here

	// DiagramEng (start)
	int buffer = 20;		// 20 point buffer
	CPoint ptExtrema;		// extreme coordinates of diagram entities of system model
	CPoint ptMinima;		// minimum coordinates of diagram entities of system model
	
	//AfxMessageBox("\n CDiagramEngDoc::OnEditSelectAll()\n", MB_OK, 0);

	// Get the position of the first view in the list of views for the current doc.
	POSITION posn = GetFirstViewPosition();

	// Get a pointer to the view identified by posn.
	CView *pView = GetNextView(posn);		// CView is derived from CWnd, so pView can be used with the rect tracker

	// Create a device context using CView *pView where CView is derived from CWnd
	CClientDC dc(pView);

	// CScrollView-related impl.
	((CScrollView*)pView)->OnPrepareDC(&dc);	// prepare device context casting pView to ptr-to-CScrollView

	// Determine Diagram Coordinate Minima and Extrema and creating a bounding box
	ptMinima = m_SystemModel.DetermineDiagramCoordinateMinima();		// pseudo minima
	ptExtrema = m_SystemModel.DetermineDiagramCoordinateExtrema();		// pseudo maxima
	ptMinima.x -= buffer;
	ptMinima.y -= buffer;
	ptExtrema.x += buffer;
	ptExtrema.y += buffer;

	// -- IF A TRACKER NOT BEEN CREATED, THEN CREATE IT.
	if(m_iRubberBandCreated == 0) 
	{
		// Set up the CRectTracker "m_RectTracker" object directly
		m_RectTracker.m_rect.SetRect(ptMinima, ptExtrema);	

		// Manually create a dotted lined rectangle circumscribing all diagram entities	
		CPen lpen(PS_DOT, 1, RGB(50,50,50));			// create a pen: RGB(0,0,0) black
		CPen *pOldPen = dc.SelectObject(&lpen);		// create a copy
		dc.SetROP2(R2_NOTXORPEN);					// make the rectangle transparent
		dc.Rectangle(m_RectTracker.m_rect.TopLeft().x , m_RectTracker.m_rect.TopLeft().y, m_RectTracker.m_rect.BottomRight().x, m_RectTracker.m_rect.BottomRight().y); // create the rectangle using the m_RectTracker object coords.
		dc.SelectObject(pOldPen);					// reset the old pen
		
		// Set flags.
		m_iRubberBandCreated = 1;	// rubber band state is active
		m_iSelectAll = 1;			// select all state is active
	}

	// DiagramEng (end)
}

void CDiagramEngDoc::OnModelBuild() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	int error_flag = 0;		// error flag for model validation
	CString	sMsg;			// string to be displayed
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.

	// Validate Model
	error_flag = m_SystemModel.ValidateModel();

	// Display a msg. box 
	//sMsg.Format("\n CDiagramEngDoc::OnModelBuild()    \n error_flag = %d    \n\n", error_flag);
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// DiagramEng (end)	
}

void CDiagramEngDoc::OnModelBuildSubsystem() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	CString sMsg;
	CString sMsgTemp;

	sMsgTemp.Format("\n CDiagramEngDoc::OnModelBuildSubsystem()    \n");
	sMsg += sMsgTemp;
	sMsgTemp.Format("\n At present there is no subsystem building functionality.    \n");
	sMsg += sMsgTemp;
	AfxMessageBox(sMsg, MB_OK, 0);

	// DiagramEng (end)
}

void CDiagramEngDoc::OnSimStart() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	int error_flag = 0;		// flag indicating whether model is in error or has feedback loops
	CString	sMsg;			// string to be displayed
	CString sMsgTemp;		// temp string msg.
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.

	// Display a msg. box 
	//sMsg.Format("\n CDiagramEngDoc::OnSimStart()\n\n");
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// Validate model
	error_flag = m_SystemModel.ValidateModel();

	// Propagate signals
	if((error_flag == 0) && (m_SystemModel.GetNFeedbackLoops() == 0))
	{
		m_SystemModel.SignalPropagationDirect();
	}
	else if((error_flag == 0) && (m_SystemModel.GetNFeedbackLoops() > 0))
	{
		m_SystemModel.SignalPropagationSystemOfEquations();
	}
	else
	{
		sMsgTemp.Format("\n CDiagramEngDoc::OnSimStart()    \n\n");
		sMsg+=sMsgTemp;
		sMsgTemp.Format(" Model is in error and simulation cannot start.    \n");
		sMsg+=sMsgTemp;
		sMsgTemp.Format(" Please resolve model errors before restarting.    \n");
		sMsg+=sMsgTemp;
		AfxMessageBox(sMsg, nType, nIDhelp);
	}

	// DiagramEng (end)
}

void CDiagramEngDoc::OnSimStop() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	HWND hWnd = NULL;			// handle to window whose window procedure is to receive the msg.
	LPARAM lParam = NULL;		// windows procedure based parameter with msg. specific info.
	UINT msg = WM_COMMAND;		// message
	WPARAM wParam = MAKEWPARAM(ID_SIM_STOP, BN_CLICKED);	// make the para using ID and BN_CLICKED event
	
	//AfxMessageBox("\n CDiagramEngDoc::OnSimStop()\n", MB_OK, 0);

	// Post a message to be processed in CSystemModel::CheckStopButtonPressed()
	PostMessage(hWnd, msg, wParam, lParam);

	// DiagramEng (end)
}

void CDiagramEngDoc::OnFormatShowAnnotations() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	CSystemModel &system_model = GetSystemModel();

	int list_size = system_model.GetAnnotationList().size();

	//AfxMessageBox("\n CDiagramEngDoc::OnFormatShowAnnotations()\n", MB_OK, 0);

	if((list_size > 0) && (system_model.GetDrawAnnotationFlag() == 0))
	{
		system_model.SetDrawAnnotationFlag(1);	// toggle the annotation-drawing state
		SetModifiedFlag(TRUE); 					// set the doc. as having been modified to prompt user to save
		UpdateAllViews (NULL); 					// indicate that sys. should redraw.
	}
	else if((list_size > 0) && (system_model.GetDrawAnnotationFlag() == 1))
	{
		system_model.SetDrawAnnotationFlag(0);	// toggle the annotation-drawing state
		SetModifiedFlag(TRUE); 					// set the doc. as having been modified to prompt user to save
		UpdateAllViews (NULL); 					// indicate that sys. should redraw.
	}

	// DiagramEng (end)
}

void CDiagramEngDoc::OnToolsDiagnosticInfo() 
{
	// TODO: Add your command handler code here

	// DiagramEng (start)
//	int bik = 1024;					// no. of bytes in 1 kilobyte
//	char strVar[50];				// string var used in sprintf() statements
	CString sMsg = "";				// msg string
	CString sMsgTemp = "";			// temp msg string
//	DWORD physMemUsed;				// physical memory used
//	DWORD virtualMemUsed;			// virtual memory used
//	HANDLE hProcess;				// handle to the process
//	PROCESS_MEMORY_COUNTERS pmc;	// structure containing memory statistics for a process
//	MEMORYSTATUS msMemStatus;		// structure containing info about current state of physical and virtual memory

	// MESSAGE TO USER ////////////////////////////////////////////////////////////////////////////
	// To use this function the user should follow the instructions in Chapter 31 Tools Menu.
	// Then the relevant lines of this function should be uncommented.

	// Display Message
	sMsgTemp = "\n CDiagramEngDoc::OnToolsDiagnosticInfo() \n";
	sMsg += sMsgTemp;
	sMsgTemp = " To use this function the user must follow the instructions in Chapter 31 Tools Menu.    \n";
	sMsg += sMsgTemp;
	sMsgTemp = " Thereafter, uncomment the relevant material in this function.    \n";
	sMsg += sMsgTemp;
	AfxMessageBox(sMsg, MB_OK, 0);
	sMsg = "";
	///////////////////////////////////////////////////////////////////////////////////////////////

	// Create a dlg obj. of class CDiagnosticInfoDialog : public CDialog
	CDiagnosticInfoDialog oDlg;	
/*
	// GET PROCESS MEMORY INFO //////////////////
	hProcess = GetCurrentProcess();

	if(GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
	{
		// Page Fault Count
		sprintf(strVar, "%d", pmc.PageFaultCount);
		oDlg.m_strPageFaultCount = strVar;

		// Peak Working Set Size
		sprintf(strVar, "%d", pmc.PeakWorkingSetSize/bik);
		oDlg.m_strPeakWorkingSetSize = strVar;

		// Working Set Size
		sprintf(strVar, "%d", pmc.WorkingSetSize/bik);
		oDlg.m_strWorkingSetSize = strVar;

		// Quota Peak Paged Pool Usage
		sprintf(strVar, "%d", pmc.QuotaPeakPagedPoolUsage/bik);
		oDlg.m_strQuotaPeakPagedPoolUsage = strVar;

		// Quota Paged Pool Usage
		sprintf(strVar, "%d", pmc.QuotaPagedPoolUsage/bik);
		oDlg.m_strQuotaPagedPoolUsage = strVar;

		// Quota Peak Non Paged Pool Usage
		sprintf(strVar, "%d", pmc.QuotaPeakNonPagedPoolUsage/bik);
		oDlg.m_strQuotaPeakNonPagedPoolUsage = strVar;

		// Quota Non Paged Pool Usage
		sprintf(strVar, "%d", pmc.QuotaNonPagedPoolUsage/bik);
		oDlg.m_strQuotaNonPagedPoolUsage = strVar;

		// Pagefile Usage
		sprintf(strVar, "%d", pmc.PagefileUsage/bik);
		oDlg.m_strPagefileUsage = strVar;

		// Peak Pagefile Usage
		sprintf(strVar, "%d", pmc.PeakPagefileUsage/bik);
		oDlg.m_strPeakPagefileUsage = strVar;

		// Physical Memory Used by Process
		sprintf(strVar, "%d", pmc.WorkingSetSize/bik);
		oDlg.m_strPhysicalMemoryUsedProcess = strVar;
	}
	else
	{
		sMsgTemp.Format("\n CDiagramEngDoc::OnToolsDiagnosticInfo()    \n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n GetProcessMemoryInfo() failed: %d.    \n", GetLastError());
		sMsg += sMsgTemp;
		AfxMessageBox(sMsg, MB_OK, 0);
	}

	// MEMORY STATUS ////////////////////////////

	// GlobalMemoryStatus() obtains info about computer system's current usage of physical and virtual memory
	GlobalMemoryStatus(&msMemStatus);	// no return value to check.

	// Memory Load
	sprintf(strVar, "%d%%", msMemStatus.dwMemoryLoad);
	oDlg.m_strMemoryLoad = strVar;

	// Total Physical Memory
	sprintf(strVar, "%d", msMemStatus.dwTotalPhys/bik);	
	oDlg.m_strTotalPhysicalMemory = strVar;

	// Available Physical Memory
	sprintf(strVar, "%d", msMemStatus.dwAvailPhys/bik);	
	oDlg.m_strAvailPhysicalMemory = strVar;

	// Total Pagefile Memory
	sprintf(strVar, "%d", msMemStatus.dwTotalPageFile/bik);	
	oDlg.m_strTotalPagefileMemory = strVar;

	// Available Pagefile Memory
	sprintf(strVar, "%d", msMemStatus.dwAvailPageFile/bik);	
	oDlg.m_strAvailPagefileMemory = strVar;

	// Total Virtual Memory (User)
	sprintf(strVar, "%d", msMemStatus.dwTotalVirtual/bik);	
	oDlg.m_strTotalVirtualMemoryUser = strVar;

	// Available Virtual Memory (User)
	sprintf(strVar, "%d", msMemStatus.dwAvailVirtual/bik);
	oDlg.m_strAvailVirtualMemoryUser = strVar;

	// Total Virtual Memory
	sprintf(strVar, "%d", msMemStatus.dwTotalPageFile/bik);	
	oDlg.m_strTotalVirtualMemory = strVar;

	// Virtual Memory Used
	virtualMemUsed = msMemStatus.dwTotalPageFile - msMemStatus.dwAvailPageFile;
	sprintf(strVar, "%d", virtualMemUsed/bik);
	oDlg.m_strVirtualMemoryUsed = strVar;

	// Physical Memory Used
	physMemUsed = msMemStatus.dwTotalPhys - msMemStatus.dwAvailPhys;
	sprintf(strVar, "%d", physMemUsed/bik);
	oDlg.m_strPhysicalMemoryUsed = strVar;
*/
	// DISPLAY DLG WND //////////////////////////
	// Return val of DoModal() fn of ancestor class CDialog is checked to determine which btn was clicked.
	if(oDlg.DoModal() == IDOK) 
	{	
		// No action to be taken at present
	}
	else
	{
		// No action to be taken at present
	}

	// DiagramEng (end)
}

void CDiagramEngDoc::OnHelpUsingDiagramEng() 
{
	// TODO: Add your command handler code here

	// DiagramEng (start)
	int lenBuffer = 0;						// length of buffer to get dir name
	char *appName = NULL;					// app name (not used since cmd line used instead)
	char cmdLine[1024] = "Acrobat.exe ";	// cmd line, starting with the app name 
	char dirPath[1024] = "";				// dir path 
	char dirPathExe[1024] = "";				// dir path including exe file name
	CString sMsg = "";						// msg string
	CString sMsgTemp = "";					// temp msg string
	DWORD retval = 0;						// return val of GetCurrentDirectory()
	PROCESS_INFORMATION pi;					// PROCESS_INFORMATION structure
	STARTUPINFO si;							// STARTUPINFO structure

	//AfxMessageBox("\n CDiagramEngDoc::OnHelpUsingDiagramEng()\n", MB_OK, 0);

	// GET CURRENT DIR (DiagramEng.exe) /////////
	lenBuffer = sizeof(dirPathExe)/sizeof(char);
	retval = GetModuleFileName(AfxGetInstanceHandle(), dirPathExe, lenBuffer);

	if(retval == 0)		// handle error
	{
		sMsgTemp.Format("\n CDiagramEngDoc::OnHelpUsingDiagramEng()    \n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n GetModuleFileName() failed: %d \n", GetLastError());
		sMsg += sMsgTemp;
		AfxMessageBox(sMsg, MB_OK, 0);
	}
	else
	{
		// Truncate dir path since it includes "\DiagramEng.exe": i.e. remove last 15 chars.
		strncpy(dirPath, dirPathExe, (retval - strlen("\\DiagramEng.exe")));	

		sMsg.Format("\n %s has %d chars not including the null char.    \n", dirPathExe, retval);
		//AfxMessageBox(sMsg, MB_OK, 0);
	}

	// CONSTRUCT THE CMD LINE ///////////////////
	// WARNING!
	// If there are spaces in the dir path then quotes must surround the path.
	// E.g., "C:\BudFox\C++\Work\Diagram Eng Project\DiagramEng (19 finalize)\DiagramEng\UsingDiagramEng.pdf"
	// If there are no spaces in the dir path, then quotes need not surround the path.
	// E.g., C:\BudFox\Temp\UsingDiagramEng.pdf
	// Hence, place quotes around the dir path to be safe.
	// An explicit example without spaces in the dir path is: 
	// char cmdLine[1024] = "Acrobat.exe C:\\BudFox\\Temp\\UsingDiagramEng.pdf"
	// The surrounding quotes are necessary here purely to init the char array as a string. 
	// The escape char, \, is used, as in, \\, to allow a, \, to be present in the string.

	// Concatenate the cmd line
	// Acrobat.exe + "C:\BudFox\C++\Work\Diagram Eng Project\DiagramEng + \UsingDiagramEng.pdf"
	strcat(cmdLine, "\"");							// cmdLine = Acrobat.exe "
	strcat(cmdLine, dirPath);						// cmdLine = Acrobat.exe "dir path
	strcat(cmdLine, "\\UsingDiagramEng.pdf\"");		// cmdLine = Acrobat.exe "dir path\UsingDiagramEng.pdf"
	//AfxMessageBox(cmdLine, MB_OK, 0);
	
	// FILL BLOCKS OF MEMORY WITH ZEROS /////////
	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);
	ZeroMemory(&pi, sizeof(pi));

	// CREATE THE PROCESS ///////////////////////
	if(!CreateProcess(appName, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
	{
		sMsg = "";
		sMsgTemp.Format("\n\n Failed to open the \"UsingDiagramEng.pdf\" document.    \n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n Check the following to resolve this problem: \n\n"); 
		sMsg += sMsgTemp;
		sMsgTemp.Format(" 1) The \"UsingDiagramEng.pdf\" document is in the same directory as \"DiagramEng.exe\".    \n");
		sMsg += sMsgTemp;
		sMsgTemp.Format(" 2) The location of \"Acrobat.exe\" is specified in the Path variable.    \n");
		sMsg +=sMsgTemp;
		sMsgTemp.Format("\n To set the Path variable on Windows, perform the following steps:    \n\n");
		sMsg += sMsgTemp;
		sMsgTemp.Format(" 1) Left click Start, right click My Computer and select Properties.    \n");
		sMsg += sMsgTemp;
		sMsgTemp.Format(" 2) In the System Properites dialog window select the Advanced tab and click Environment Variables.    \n");
		sMsg += sMsgTemp;
		sMsgTemp.Format(" 3) In the System Variables group box scroll down and highlight the Path variable.    \n");
		sMsg += sMsgTemp;
		sMsgTemp.Format(" 4) Click the Edit button to edit the path, typing in the new path: click OK to save the setting.    \n");
		sMsg += sMsgTemp;
		sMsgTemp.Format(" 5) Restart the DiagramEng application.    \n");
		sMsg += sMsgTemp;
		AfxMessageBox(sMsg, MB_OK, 0);
	}

	// Wait until child process exists
	WaitForSingleObject(pi.hProcess, 0);	// wait for 0 milliseconds not INFINITE

	// CLOSE THE NEWLY OPENED PROCESS AND THREAD HANDLES
	if(!CloseHandle(pi.hProcess))	// close handle of the newly created process
	{
		sMsg = "";
		sMsgTemp.Format("\n CDiagramEngDoc::OnHelpUsingDiagramEng()    \n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n CloseHandle(pi.hProcess) failed: %d \n", GetLastError());
		sMsg += sMsgTemp;
		AfxMessageBox(sMsg, MB_OK, 0);
	}
    if(!CloseHandle(pi.hThread))	// close handle of the primary thread of the newly created process
	{
		sMsg = "";
		sMsgTemp.Format("\n CDiagramEngDoc::OnHelpUsingDiagramEng()    \n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n CloseHandle(pi.hThread) failed: %d \n", GetLastError());
		sMsg += sMsgTemp;
		AfxMessageBox(sMsg, MB_OK, 0);
	}

	// DiagramEng (end)
}

void CDiagramEngDoc::OnBlockCtsDerivative() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	//AfxMessageBox("\n CDiagramEngDoc::OnBlockCtsDerivative()\n", MB_OK, 0);
	
	// Invoke a general block creation fn passing an enum EBlockType var.
	EBlockType e_block_type = eDerivativeBlock;		// enum block type defined in DiagramEngDoc.cpp	
	ConstructBlock(e_block_type);
	// DiagramEng (end)
}

void CDiagramEngDoc::OnBlockCtsIntegrator() 
{
	// TODO: Add your command handler code here

	// DiagramEng (start)
	//AfxMessageBox("\n CDiagramEngDoc::OnBlockCtsIntegrator()\n", MB_OK, 0);

	// Invoke a general block creation fn passing an enum EBlockType var.
	EBlockType e_block_type = eIntegratorBlock;
	ConstructBlock(e_block_type);
	// DiagramEng (end)
}

void CDiagramEngDoc::OnBlockCtsTransferfn() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	//AfxMessageBox("\n CDiagramEngDoc::OnBlockCtsTransferfn()\n", MB_OK, 0);

	// Invoke a general block creation fn passing an enum EBlockType var.
	EBlockType e_block_type = eTransferFnBlock;
	ConstructBlock(e_block_type);
	// DiagramEng (end)
}

void CDiagramEngDoc::OnBlockMathopsDivide() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	//AfxMessageBox("\n CDiagramEngDoc::OnBlockMathopsDivide()\n", MB_OK, 0);

	// Invoke a general block creation fn passing an enum EBlockType var.
	EBlockType e_block_type = eDivideBlock;
	ConstructBlock(e_block_type);
	// DiagramEng (end)
}

void CDiagramEngDoc::OnBlockMathopsGain() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	//AfxMessageBox("\n CDiagramEngDoc::OnBlockMathopsGain()\n", MB_OK, 0);

	// Invoke a general block creation fn passing an enum EBlockType var.
	EBlockType e_block_type = eGainBlock;
	ConstructBlock(e_block_type);
	// DiagramEng (end)
}

void CDiagramEngDoc::OnBlockMathopsSum() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	//AfxMessageBox("\n CDiagramEngDoc::OnBlockMathopsSum()\n", MB_OK, 0);

	// Invoke a general block creation fn passing an enum EBlockType var.
	EBlockType e_block_type = eSumBlock;
	ConstructBlock(e_block_type);
	// DiagramEng (end)
}

void CDiagramEngDoc::OnBlockSinkOutput() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	//AfxMessageBox("\n CDiagramEngDoc::OnBlockSinkOutput()\n", MB_OK, 0);
	
	// Invoke a general block creation fn passing an enum EBlockType var.
	EBlockType e_block_type = eOutputBlock;
	ConstructBlock(e_block_type);
	// DiagramEng (end)
}

void CDiagramEngDoc::OnBlockSourceConst() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	//AfxMessageBox("\n CDiagramEngDoc::OnBlockSourceConst()\n", MB_OK, 0);

	// Invoke a general block creation fn passing an enum EBlockType var.
	EBlockType e_block_type = eConstBlock;
	ConstructBlock(e_block_type);
	// DiagramEng (end)
}

void CDiagramEngDoc::OnBlockSourceLinearfn() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	//AfxMessageBox("\n CDiagramEngDoc::OnBlockSourceLinearfn()\n", MB_OK, 0);

	// Invoke a general block creation fn passing an enum EBlockType var.
	EBlockType e_block_type = eLinearFnBlock;
	ConstructBlock(e_block_type);
	// DiagramEng (end)
}

void CDiagramEngDoc::OnBlockSourceSignalgen() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	//AfxMessageBox("\n CDiagramEngDoc::OnBlockSourceSignalgen()\n", MB_OK, 0);

	// Invoke a general block creation fn passing an enum EBlockType var.
	EBlockType e_block_type = eSignalGenBlock;
	ConstructBlock(e_block_type);
	// DiagramEng (end)
}

void CDiagramEngDoc::OnBlockSubsysSubsys() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	//AfxMessageBox("\n CDiagramEngDoc::OnBlockSubsysSubsys()\n", MB_OK, 0);

	// Invoke a general block creation fn passing an enum EBlockType var.
	EBlockType e_block_type = eSubsysBlock;		
	ConstructBlock(e_block_type);
	// DiagramEng (end)
}

void CDiagramEngDoc::OnBlockSubsysSubsysin() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	//AfxMessageBox("\n CDiagramEngDoc::OnBlockSubsysSubsysin()\n", MB_OK, 0);

	// Invoke a general block creation fn passing an enum EBlockType var.
	EBlockType e_block_type = eSubsysInBlock;	
	ConstructBlock(e_block_type);
	// DiagramEng (end)
}

void CDiagramEngDoc::OnBlockSubsysSubsysout() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	//AfxMessageBox("\n CDiagramEngDoc::OnBlockSubsysSubsysout()\n", MB_OK, 0);

	// Invoke a general block creation fn passing an enum EBlockType var.
	EBlockType e_block_type = eSubsysOutBlock;		
	ConstructBlock(e_block_type);
	// DiagramEng (end)
}

int CDiagramEngDoc::ConstructBlock(EBlockType e_block_type)
{
	// General ConstructBlock() fn which calls specific block construction fns.
	CString	sMsg;			// string to be displayed
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.

	// -- Display a msg. box 
	sMsg.Format("\n CDiagramEngDoc::ConstructBlock()\n\n");
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// -- Filter out the approp. partic. block consturction fn. to be called

	switch(e_block_type)		
	{
	case eChirpSignal:
		ConstructChirpSignalBlock();
		break;

	case eConstBlock:
		ConstructConstantBlock();
		break;

	case eDerivativeBlock:
		ConstructDerivativeBlock();
		break;

	case eDivideBlock:
		ConstructDivideBlock();
		break;

	case eGainBlock:
		ConstructGainBlock();
		break;

	case eIntegratorBlock:
		ConstructIntegratorBlock();
		break;

	case eLinearFnBlock:
		ConstructLinearFnBlock();
		break;

	case eOutputBlock:
		ConstructOutputBlock();
		break;

	case eSignalGenBlock:
		ConstructSignalGeneratorBlock();
		break;

	case eSubsysBlock:
		ConstructSubsystemBlock();
		break;

	case eSubsysInBlock:
		ConstructSubsystemInBlock();
		break;

	case eSubsysOutBlock:
		ConstructSubsystemOutBlock();
		break;

	case eSumBlock:
		ConstructSumBlock();
		break;

	case eTransferFnBlock:
		ConstructTransferFnBlock();
		break;

	default:
		// Inform user of switch default
		sMsg.Format("\n CDiagramEngDoc::ConstructBlock(): switch default\n\n");
		AfxMessageBox(sMsg, nType, nIDhelp);
		break;
	}

	SetModifiedFlag(TRUE);		// prompt the user to save after adding a block to the system model
	CheckSystemModel();
	UpdateAllViews(NULL);		// this fn calls OnDraw to redraw the system model.

	return 0;	// return int for error checking
}

/*CDiagramEngDoc* GetDocumentGlobalFn(void)
{
	// GetDocumentGlobalFn() returns a ptr to the current CDiagramEngDoc doc.

	//AfxMessageBox("\n GetDocumentGlobalFn()\n", MB_OK, 0);

	// -- NOTES (start)

	// - CDocument - a class that provides the basic fnality for programmer-defined document classes. 
	// A document represents the unit of data that the user typically opens with the Open command on the File menu 
	// & saves with the Save command on the File menu.

	// - POSITION is a value used to denote the position of an element in a collection; used by MFC collection classes.

	// - theApp (CDiagramEngApp theApp;) 
	// 1) The one & only CDiagramEngApp object.
	// 2) The App object is a global object accessed with the App keyword. It determines or specifies info about 
	//    the app’s title, version info, the path & name of its executable file & Help files, & whether or not 
	//    a previous instance of the app is running.

	// - GetFirstDocTemplatePosition()
	// Gets the posn of the first document template in the app. 
	// Use the POSITION value returned in a call to GetNextDocTemplate to get the first CDocTemplate object. 
	// The POSITION value is returned & can be used for iteration or object pointer retrieval; 
	// NULL is returned if the list is empty.

	// - GetNextDocTemplate()
	// 1) Gets the document template identified by pos, then sets pos to the POSITION value. 
	//    You can use GetNextDocTemplate in a forward iteration loop if you establish the initial position with a call 
	//    to GetFirstDocTemplatePosition.
	// 2) You must ensure that your POSITION value is valid. If it is invalid, then the Debug version of the MFC Lib asserts.
	// 3) If the retrieved document template is the last available, then the new value of pos is set to NULL.

	// - GetDocString()
	// 1) Return val - nonzero if the specified substring was found; otherwise 0.
	// 2) Paras
	//	  2.1)	rString - a reference to a CString object that will contain the string when the function returns.
	//    2.2) index - an index of the substring being retrieved from the string that describes the document type.
	// 3) Call this fn to retrieve a specific substring describing the doc type. The string containing these substrings 
	//	  is stored in the doc template & is derived from a string in the resource file for the app. The frwk calls this 
	//	  fn to get the strings it needs for the app’s UI. If you have specified a filename extension for your app’s docs,
	//    the frwk also calls this fn when adding an entry to the Windows registration database; 
	//    this allows docs to be opened from the Windows File Manager.	
	// 4) Call this function only if you are deriving your own class from CDocTemplate.

	// - _T 
	//	A data type mapping L (converts following character or string to its Unicode counterpart (if UNICODE defined) 
	//	(correct?). "Diagra" is the doc name string, and is found by going to the Resources Tab --> String Table, 
	//  IDR_DIAGRATYPE, & finding the string "Diagra".
	
	// - CDocTemplate
	// 1) Is an abstract base class that defines the basic functionality for document templates.
	// 2) To manage the complex process of creating documents with their associated views and frame windows, 
	//    the framework uses two document template classes: CSingleDocTemplate for SDI applications and 
	//    CMultiDocTemplate for MDI applications. A CSingleDocTemplate can create and store one document of one type 
	//    at a time. A CMultiDocTemplate keeps a list of many open documents of one type.

	// - GetFirstDocPosition()
	// 1) Return val - a POSITION val that can be used to iterate through the list of docs associated with this doc template; or NULL if the list is empty.
	// 2) Use this function to get the posn of the first doc in the list of docs associated with this template. Use the POSITION val as an arg to CDocTemplate::GetNextDoc to iterate through the list of docs associated with the template.

	// - GetNextDoc()
	// 1) Return val - a pointer to the next document in the list of documents associated with this template.
	// 2) Parameters - rPos: a ref to a POSITION val returned by a prev call to GetFirstDocPosition or GetNextDoc.
	// 3) Retrieves the list element identified by rPos, then sets rrPos to the POSITION value of the next entry in the list. 
	//    If the retrieved element is the last in the list, then the new value of rPos is set to NULL.
	// 4) You can use GetNextDoc in a forward iteration loop if you establish the init posn with a call to GetFirstDocPosition.
	// 5) You must ensure that your POSITION value represents a valid position in the list. 
	//    If it is invalid, then the Debug version of the Microsoft Foundation Class Library asserts.

	// -- NOTES (end)

	POSITION pos = theApp.GetFirstDocTemplatePosition();

	while(pos)
	{
		CDocTemplate *pTemplate = theApp.GetNextDocTemplate(pos);

		CString sDocName;
		pTemplate->GetDocString(sDocName, CDocTemplate::docName);

		if(sDocName == _T("DiaEng"))
		{
			POSITION posDoc = pTemplate->GetFirstDocPosition();

			while(posDoc)
			{
				// NOTE: If there is a first doc, then posDoc is not NULL.
				// If there is no next doc, then posDoc is set to NULL. 
				// But a pDoc is still constructed, since posDoc is valid to enter this while loop in the first place.
				// pDoc is returned (if it's not NULL) and the fn exits.
				// The net result is that a ptr-to-the-first-doc pDoc is returned successfully.
				CDiagramEngDoc* pDoc = (CDiagramEngDoc*)pTemplate->GetNextDoc(posDoc);
			
				if(pDoc != NULL)	
				{
					return pDoc;
				}
			}// end while (posDoc)

		}// end if(sDocName)

	}// end while (pos)

	return NULL;	// if pDoc != NULL then it would have already been returned, o/wise return NULL.
}*/

CDiagramEngDoc* GetDocumentGlobalFn(void)
{
	// Get a pointer to the main frame window
	CMDIFrameWnd *pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;

	// Get the active MDI child window.
	CMDIChildWnd *pChild = (CMDIChildWnd*)pFrame->GetActiveFrame();

	// Get the active document associated with the active child window
	CDiagramEngDoc *pDoc = (CDiagramEngDoc*)pChild->GetActiveDocument();

	// Return the pDoc
	if(pDoc != NULL)
	{
		return pDoc;
	}

	// If pDoc != NULL then it would have already been returned, o/wise return NULL.
	return NULL;	
}

void CDiagramEngDoc::ConstructConstantBlock()
{
	// NOTE
	// CConstantBlock : public CBlock
	// CBlock contains a m_BlockShape obj. of type CBlockShape
	// Hence rqe. constructor args for CConstantBlock and CBlock.

	CPoint block_posn;
	
	//AfxMessageBox("\n CDiagramEngDoc::ConstructConstantBlock()\n", MB_OK, 0);

	// Constructor args. for CBlock
	AssignBlockPosition(block_posn);

	// Constructor args. for CBlockShape
	EBlockShape e_block_shape = e_rectangle;

	// Construct a new constant block
	//CBlock *p_block = new CConstantBlock();
	CBlock *p_block = new CConstantBlock(&(GetSystemModel()), block_posn, e_block_shape);
	
	// Add the new block to the system model block list.
	GetSystemModel().GetBlockList().push_back(p_block);
}

CSystemModel &CDiagramEngDoc::GetSystemModel()
{
	return m_SystemModel;
}

void CDiagramEngDoc::CheckSystemModel()
{
	int n_in_ports = 0;
	int n_out_ports = 0;

	// -- Message box vars.
	CPoint blk_posn;	// local block position var.
	CString sMsg;		// main msg string
	CString sMsgTemp;	// temp msg string
	UINT nType = MB_OK;	// style of msg. box
	UINT nIDhelp = 0;	// help context ID for the msg.

	sMsgTemp.Format("\n CDiagramEngDoc::CheckSystemModel()\n");
	sMsg += sMsgTemp;
	
	// -- Block list vars.
	list<CBlock*> blk_list;
	list<CBlock*>::iterator it_blk;			// iterator for list of CBlock-ptrs

	blk_list = GetSystemModel().GetBlockList();

	// -- Check Block List
	for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
	{
		sMsgTemp.Format(" %s: ",(*it_blk)->GetBlockName());
		sMsg += sMsgTemp;

		// Check block ports
		//(*it_blk)->CheckBlockPorts();

		// Get the vectors of input and output ports and determine their sizes.
		n_in_ports = (*it_blk)->GetVectorOfInputPorts().size();
		n_out_ports = (*it_blk)->GetVectorOfOutputPorts().size();

		sMsgTemp.Format(" n_in_ports = %d, n_out_ports = %d ", n_in_ports, n_out_ports);
		sMsg += sMsgTemp;

		// Get the location of the block
		blk_posn = (*it_blk)->GetBlockPosition();
		sMsgTemp.Format(" (x,y) = (%d,%d)\n", blk_posn.x, blk_posn.y);
		sMsg += sMsgTemp;
	}

	// Print a system model check via a msg. box.
	//AfxMessageBox(sMsg, nType, nIDhelp);
}

void CDiagramEngDoc::ConstructGainBlock()
{
	CPoint block_posn;

	//AfxMessageBox("\n CDiagramEngDoc::ConstructGainBlock()\n", MB_OK, 0);

	// Constructor args. for CBlock
	AssignBlockPosition(block_posn);

	// Constructor args. for CBlockShape
	EBlockShape e_block_shape = e_triangle;

	// Construct a new constant block
	//CBlock *p_block = new CGainBlock();
	CBlock *p_block = new CGainBlock(&(GetSystemModel()), block_posn, e_block_shape);
	
	// Add the new block to the system model block list.
	GetSystemModel().GetBlockList().push_back(p_block);
}

void CDiagramEngDoc::AssignBlockPosition(CPoint &block_posn)
{
	int		i;
	int		large_no_of_blocks = 500;
	int		n_blocks;
	int		n_blks_in_row = 10;
	int		posn_found = 0;
	int		too_close = 0;
	double	dist = 0.0;
	double	dist_crit;
	double	height = 2.0*m_dDeltaLength;
	double	width = 2.0*m_dDeltaLength;
	double	left_spacing = 0.2*width;
	double	blk_spacing = 0.2*width;
	double	top_spacing = 0.2*width;
	//static int n_blks_static = 0;
	CPoint	blk_posn;
	CPoint	trial_blk_posn;
	list<CBlock*>::iterator it_blk;
	CString sMsg;		// main msg string
	UINT nType = MB_OK;	// style of msg. box
	UINT nIDhelp = 0;	// help context ID for the msg.

	//sMsg.Format("\n CDiagramEngDoc::AssignBlockPosition(), i = %d\n", i);
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// -- ASSIGN BLOCK POSITION IN THE NEXT ROW/COL POSN.
	//n_blocks = GetSystemModel().GetBlockList().size();
	/*n_blocks = n_blks_static;
	n_blks_static++;

	block_posn.x = left_spacing + (n_blocks%n_blks_in_row)*(width + blk_spacing) + 0.5*width;	// CofM of block at width*0.5
	block_posn.y = top_spacing + (n_blocks/n_blks_in_row)*(height + blk_spacing) + 0.5*height;	// CofM of block at height*0.5 
	*/

	// -- ASSIGN BLOCK POSITION FLEXIBLY (TO NOT OVERLAP EXISTING BLOCKS) IN THE EARLIEST AVAILABLE ROW/COL POSN.
	
	// Get a copy of the blk_list in the system model
	list<CBlock*> &blk_list = GetSystemModel().GetBlockList();	// MUST BE A REFERENCE!

	dist_crit = width + blk_spacing;
	n_blocks = blk_list.size();

	// Check to see whether there are no blocks: if so, set the first block and return.
	if(n_blocks == 0)
	{
		block_posn.x = left_spacing + (n_blocks%n_blks_in_row)*(width + blk_spacing) + 0.5*width;	// CofM of block at width*0.5
		block_posn.y = top_spacing + (n_blocks/n_blks_in_row)*(height + blk_spacing) + 0.5*height;	// CofM of block at height*0.5 
		return;
	}

	// While not suitable row/col position has been found
	while(posn_found == 0)		
	{
		// Iterate through the potential row/col positions, trying each one against existing block positions
		for(i=0; i<large_no_of_blocks; i++)
		{
			trial_blk_posn.x = left_spacing + (i%n_blks_in_row)*(width + blk_spacing) + 0.5*width;	// CofM of block at width*0.5
			trial_blk_posn.y = top_spacing + (i/n_blks_in_row)*(height + blk_spacing) + 0.5*height;	// CofM of block at height*0.5 

			// Iterate through the existing blocks to get the distance from the trial temp_block_posn.
			too_close = 0;
			for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
			{
				blk_posn = (*it_blk)->GetBlockPosition();

				// Check the distance bw. trial posn and block posn
				dist = sqrt(pow((blk_posn.x - trial_blk_posn.x),2) + pow((blk_posn.y - trial_blk_posn.y),2));

				if(dist < dist_crit)
				{
					too_close = 1;	// if too close, then get another trial temp_blk_posn
					break;
				}	
			}// end for it

			if(too_close == 0)
			{
				posn_found = 1;
				block_posn = trial_blk_posn;
				break;
			}

		}// end for i
	}// end while
}

void CDiagramEngDoc::ConstructSumBlock()
{
	CPoint block_posn;

	//AfxMessageBox("\n CDiagramEngDoc::ConstructSumBlock()\n", MB_OK, 0);

	// Constructor args. for CBlock
	AssignBlockPosition(block_posn);

	// Constructor args. for CBlockShape
	EBlockShape e_block_shape = e_ellipse;

	// Construct a new constant block
	//CBlock *p_block = new CSumBlock();
	CBlock *p_block = new CSumBlock(&(GetSystemModel()), block_posn, e_block_shape);
	
	// Add the new block to the system model block list.
	GetSystemModel().GetBlockList().push_back(p_block);
}

void CDiagramEngDoc::ConstructChirpSignalBlock()
{
	CPoint block_posn;

	//AfxMessageBox("\n CDiagramEngDoc::ConstructChirpSignalBlock()\n", MB_OK, 0);

	// Constructor args. for CBlock
	AssignBlockPosition(block_posn);

	// Constructor args. for CBlockShape
	EBlockShape e_block_shape = e_rectangle;

	// Construct a new chirp signal block
	//CBlock *p_block = new CChirpSignalBlock();
	CBlock *p_block = new CChirpSignalBlock(&(GetSystemModel()), block_posn, e_block_shape);
	
	// Add the new block to the system model block list.
	GetSystemModel().GetBlockList().push_back(p_block);
}

void CDiagramEngDoc::ConstructDerivativeBlock()
{
	CPoint block_posn;

	//AfxMessageBox("\n CDiagramEngDoc::ConstructDerivativeBlock()\n", MB_OK, 0);

	// Constructor args. for CBlock
	AssignBlockPosition(block_posn);

	// Constructor args. for CBlockShape
	EBlockShape e_block_shape = e_rectangle;

	// Construct a new derivative block
	//CBlock *p_block = new CDerivativeBlock();
	CBlock *p_block = new CDerivativeBlock(&(GetSystemModel()), block_posn, e_block_shape);
	
	// Add the new block to the system model block list.
	GetSystemModel().GetBlockList().push_back(p_block);
}

void CDiagramEngDoc::ConstructIntegratorBlock()
{
	CPoint block_posn;

	//AfxMessageBox("\n CDiagramEngDoc::ConstructIntegratorBlock()\n", MB_OK, 0);

	// Constructor args. for CBlock
	AssignBlockPosition(block_posn);

	// Constructor args. for CBlockShape
	EBlockShape e_block_shape = e_rectangle;

	// Construct a new integrator block
	//CBlock *p_block = new CIntegratorBlock();
	CBlock *p_block = new CIntegratorBlock(&(GetSystemModel()), block_posn, e_block_shape);
	
	// Add the new block to the system model block list.
	GetSystemModel().GetBlockList().push_back(p_block);
}

void CDiagramEngDoc::ConstructDivideBlock()
{
	CPoint block_posn;

	//AfxMessageBox("\n CDiagramEngDoc::ConstructDivideBlock()\n", MB_OK, 0);

	// Constructor args. for CBlock
	AssignBlockPosition(block_posn);

	// Constructor args. for CBlockShape
	EBlockShape e_block_shape = e_rectangle;

	// Construct a new divide block
	//CBlock *p_block = new CDivideBlock();
	CBlock *p_block = new CDivideBlock(&(GetSystemModel()), block_posn, e_block_shape);
	
	// Add the new block to the system model block list.
	GetSystemModel().GetBlockList().push_back(p_block);
}

void CDiagramEngDoc::ConstructLinearFnBlock()
{
	CPoint block_posn;

	//AfxMessageBox("\n CDiagramEngDoc::ConstructLinearFnBlock()\n", MB_OK, 0);

	// Constructor args. for CBlock
	AssignBlockPosition(block_posn);

	// Constructor args. for CBlockShape
	EBlockShape e_block_shape = e_rectangle;

	// Construct a new linear fn block
	//CBlock *p_block = new CLinearFnBlock();
	CBlock *p_block = new CLinearFnBlock(&(GetSystemModel()), block_posn, e_block_shape);
	
	// Add the new block to the system model block list.
	GetSystemModel().GetBlockList().push_back(p_block);
}

void CDiagramEngDoc::ConstructOutputBlock()
{
	CPoint block_posn;

	//AfxMessageBox("\n CDiagramEngDoc::ConstructOutputBlock()\n", MB_OK, 0);

	// Constructor args. for CBlock
	AssignBlockPosition(block_posn);

	// Constructor args. for CBlockShape
	EBlockShape e_block_shape = e_rectangle;

	// Construct a new output block
	//CBlock *p_block = new COutputBlock();
	CBlock *p_block = new COutputBlock(&(GetSystemModel()), block_posn, e_block_shape);
	
	// Add the new block to the system model block list.
	GetSystemModel().GetBlockList().push_back(p_block);
}

void CDiagramEngDoc::ConstructSignalGeneratorBlock()
{
	CPoint block_posn;

	//AfxMessageBox("\n CDiagramEngDoc::ConstructSignalGeneratorBlock()\n", MB_OK, 0);

	// Constructor args. for CBlock
	AssignBlockPosition(block_posn);

	// Constructor args. for CBlockShape
	EBlockShape e_block_shape = e_rectangle;

	// Construct a new signal generator block
	//CBlock *p_block = new CSignalGeneratorBlock();
	CBlock *p_block = new CSignalGeneratorBlock(&(GetSystemModel()), block_posn, e_block_shape);
	
	// Add the new block to the system model block list.
	GetSystemModel().GetBlockList().push_back(p_block);
}

void CDiagramEngDoc::ConstructSubsystemBlock()
{
	CPoint block_posn;

	//AfxMessageBox("\n CDiagramEngDoc::ConstructSubsystemBlock()\n", MB_OK, 0);

	// Constructor args. for CBlock
	AssignBlockPosition(block_posn);

	// Constructor args. for CBlockShape
	EBlockShape e_block_shape = e_rectangle;

	// Construct a new subsystem block
	//CBlock *p_block = new CSubsystemBlock();
	CBlock *p_block = new CSubsystemBlock(&(GetSystemModel()), block_posn, e_block_shape);
	
	// Add the new block to the system model block list.
	GetSystemModel().GetBlockList().push_back(p_block);
}

void CDiagramEngDoc::ConstructSubsystemInBlock()
{
	CPoint block_posn;

	//AfxMessageBox("\n CDiagramEngDoc::ConstructSubsystemInBlock()\n", MB_OK, 0);

	// Constructor args. for CBlock
	AssignBlockPosition(block_posn);

	// Constructor args. for CBlockShape
	EBlockShape e_block_shape = e_rectangle;

	// Construct a new subsystem in block
	//CBlock *p_block = new CSubsystemInBlock();
	CBlock *p_block = new CSubsystemInBlock(&(GetSystemModel()), block_posn, e_block_shape);
	
	// Add the new block to the system model block list.
	GetSystemModel().GetBlockList().push_back(p_block);
}

void CDiagramEngDoc::ConstructSubsystemOutBlock()
{
	CPoint block_posn;

	//AfxMessageBox("\n CDiagramEngDoc::ConstructSubsystemOutBlock()\n", MB_OK, 0);

	// Constructor args. for CBlock
	AssignBlockPosition(block_posn);

	// Constructor args. for CBlockShape
	EBlockShape e_block_shape = e_rectangle;

	// Construct a new subsystem out block
	//CBlock *p_block = new CSubsystemOutBlock();
	CBlock *p_block = new CSubsystemOutBlock(&(GetSystemModel()), block_posn, e_block_shape);
	
	// Add the new block to the system model block list.
	GetSystemModel().GetBlockList().push_back(p_block);
}

void CDiagramEngDoc::ConstructTransferFnBlock()
{
	CPoint block_posn;

	//AfxMessageBox("\n CDiagramEngDoc::ConstructTransferFnBlock()\n", MB_OK, 0);

	// Constructor args. for CBlock
	AssignBlockPosition(block_posn);

	// Constructor args. for CBlockShape
	EBlockShape e_block_shape = e_rectangle;

	// Construct a new transfer fn block
	//CBlock *p_block = new CTransferFnBlock();
	CBlock *p_block = new CTransferFnBlock(&(GetSystemModel()), block_posn, e_block_shape);
	
	// Add the new block to the system model block list.
	GetSystemModel().GetBlockList().push_back(p_block);
}

void CDiagramEngDoc::OnDeleteItem() 
{
	// TODO: Add your command handler code here

	// DiagramEng (start)
	int item_deleted = 0;

	// Delete a block
	item_deleted = DeleteBlock();
	if(item_deleted == 1)
	{
		return;
	}

	// Delete a block port
	item_deleted = DeletePort();
	if(item_deleted == 1)
	{
		return;
	}

	// Delete a connection bend point
	item_deleted = DeleteConnectionBendPoint();
	if(item_deleted == 1)
	{
		return;
	}

	// Delete a connection line
	item_deleted = DeleteConnection();
	if(item_deleted == 1)
	{
		return;
	}

	// Delete an annotation
	item_deleted = DeleteAnnotation();
	if(item_deleted == 1)
	{
		return;
	}

	// DiagramEng (end)
}

void CDiagramEngDoc::SetContextMenuPoint(CPoint point)
{
	// Assign the View's point at which the context menu is invoked, to the Doc's copy.
	m_ptContextMenu = point;
}

CRectTracker CDiagramEngDoc::GetRectTracker() const
{
	// Return the CRectTracker member var.
	return m_RectTracker;
}

int CDiagramEngDoc::TrackItem(CPoint point, CWnd *pWnd)
{
	int tracker_flag = 0;

	// Track block
	tracker_flag = TrackBlock(point, pWnd);

	if(tracker_flag != 0)
	{
		GetSystemModel().NullifyNegativeCoordinates();	// CScrollView-related change
		return tracker_flag;							// return since an item was tracked
	}

	// Track connection end point
	tracker_flag = TrackConnectionEndPoint(point, pWnd);
	if(tracker_flag != 0)
	{
		GetSystemModel().NullifyNegativeCoordinates();	// CScrollView-related change
		return tracker_flag;							// return since an item was tracked
	}

	// Track connection bend point
	tracker_flag = TrackConnectionBendPoint(point, pWnd);
	if(tracker_flag != 0)
	{
		GetSystemModel().NullifyNegativeCoordinates();	// CScrollView-related change
		return tracker_flag;							// return since an item was tracked
	}

	// Track annotation
	tracker_flag = TrackAnnotation(point, pWnd);
	if(tracker_flag != 0)
	{
		GetSystemModel().NullifyNegativeCoordinates();	// CScrollView-related change
		return tracker_flag;							// return since an item was tracked
	}

	// Track multiple items
	if(m_iKeyFlagTrack == 1)
	{
		tracker_flag = TrackMultipleItems(point, pWnd);

		if(tracker_flag !=0)
		{
			GetSystemModel().NullifyNegativeCoordinates();	// CScrollView-related change
			return tracker_flag;							// return since an item was tracked
		}
	}
	else
	{
		tracker_flag = 0;
	}

	// Return flag indicating if an item was tracked: 0 => not tracked, 1 => tracked.
	return tracker_flag;
}

int CDiagramEngDoc::TrackBlock(CPoint point, CWnd *pWnd)
{
	int delta;				// integer increment
	int tracker_flag = 0;
	double blk_width;
	double dist;
	CPoint bottom_right;
	CPoint top_left;
	CPoint blk_posn;
	list<CBlock*>::iterator it_blk;
	list<CBlock*> blk_list = GetSystemModel().GetBlockList();

	// CScrollView-related change
	CPoint ptLogical = point;				// local point to record the point in logical units
	CClientDC dc(pWnd);						// create a device context using the pointer-to-CWnd pWnd.
	((CScrollView*)pWnd)->OnPrepareDC(&dc);	// prepare the device context casting pWnd to ptr-to-CScrollView
	dc.DPtoLP(&ptLogical);					// use the dc to convert ptLogical from device to logical units

	// Iterate through block list
	for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
	{
		blk_posn = (*it_blk)->GetBlockPosition();
		blk_width = (*it_blk)->GetBlockShape().GetBlockWidth();
		//dist = sqrt(pow(blk_posn.x - point.x,2) + pow(blk_posn.y - point.y,2));
		dist = sqrt(pow(blk_posn.x - ptLogical.x,2) + pow(blk_posn.y - ptLogical.y,2));	// CScrollView-related change using ptLogical
		
		// If the block is clicked
		if(dist <= 0.5*blk_width*0.5)
		{
			delta = (int)(blk_width*0.5);
			top_left.x = blk_posn.x - delta;
			top_left.y = blk_posn.y - delta;
			bottom_right.x = blk_posn.x + delta;
			bottom_right.y = blk_posn.y + delta;

			// CScrollView-related change
			dc.LPtoDP(&top_left);		// convert logical pt based on logical block posn to device pt
			dc.LPtoDP(&bottom_right);	// convert logical pt based on logical block posn to device pt

			// Set the rect tracker's position
			m_RectTracker.m_rect.SetRect(top_left.x, top_left.y, bottom_right.x, bottom_right.y);

			// Track the mouse’s position till left mouse button up
			tracker_flag = m_RectTracker.Track(pWnd, point, TRUE); // 0 => item not tracked, 1 => item was tracked
		
			// Get the new tracker position and update the new block position with the tracker position
			blk_posn = m_RectTracker.m_rect.CenterPoint();

			dc.DPtoLP(&blk_posn);		// CScrollView-related change: convert device units to logical units

			(*it_blk)->SetBlockPosition(blk_posn);

			// Set flags	
			SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
			UpdateAllViews(NULL); 		// indicate that sys. should redraw.
			break;
		}
	}
	
	// Return flag indicating if an item was tracked: 0 => not tracked, 1 => tracked.
	return tracker_flag;
}

int CDiagramEngDoc::TrackConnectionEndPoint(CPoint point, CWnd *pWnd)
{
	int delta = (int)(0.25*m_dDeltaLength);		// integer increment
	int tracker_flag = 0;
	double disc_r = 0.1*m_dDeltaLength;	
	double dist_to_tail;
	double dist_to_head;
	CPoint bottom_right;
	CPoint *from_point = NULL;
	CPoint head_pt;
	CPoint tail_pt;
	CPoint top_left;
	CPort *from_port = NULL;
	CPort *to_port = NULL;
	list<CConnection*>::iterator it_con;
	list<CConnection*> con_list = GetSystemModel().GetConnectionList();

	// CScrollView-related change
	CPoint ptLogical = point;				// local point to record the point in logical units
	CClientDC dc(pWnd);						// create a device context using the pointer-to-CWnd pWnd.
	((CScrollView*)pWnd)->OnPrepareDC(&dc);	// prepare the device context casting pWnd to ptr-to-CScrollView
	dc.DPtoLP(&ptLogical);					// use the dc to convert ptLogical from device to logical units

	// Iterate through connection list
	for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
	{
		// Get tail and head points of connection
		tail_pt = (*it_con)->GetConnectionPointTail();
		head_pt = (*it_con)->GetConnectionPointHead();

		// Determine Euclidean dist bw point and tail and head pts
		//dist_to_tail = sqrt(pow(tail_pt.x - point.x,2) + pow(tail_pt.y - point.y,2));
		//dist_to_head = sqrt(pow(head_pt.x - point.x,2) + pow(head_pt.y - point.y,2));
		
		// CScrollView-related change
		dist_to_tail = sqrt(pow(tail_pt.x - ptLogical.x,2) + pow(tail_pt.y - ptLogical.y,2));
		dist_to_head = sqrt(pow(head_pt.x - ptLogical.x,2) + pow(head_pt.y - ptLogical.y,2));

		// If the tail pt is clicked
		if(dist_to_tail <= disc_r)
		{
			top_left.x = tail_pt.x - delta;
			top_left.y = tail_pt.y - delta;
			bottom_right.x = tail_pt.x + delta;
			bottom_right.y = tail_pt.y + delta;

			// CScrollView-related change
			dc.LPtoDP(&top_left);				// convert logical pt based on logical block position to device pt
			dc.LPtoDP(&bottom_right);			// convert logical pt based on logical block position to device pt

			// Set the rect tracker's position
			m_RectTracker.m_rect.SetRect(top_left.x, top_left.y, bottom_right.x, bottom_right.y);

			// Track the mouse’s position till left mouse button up
			tracker_flag = m_RectTracker.Track(pWnd, point, TRUE); // 0 => item not tracked, 1 => item was tracked
		
			// Get the new tracker position and update the new connection end pt position with the tracker position
			tail_pt = m_RectTracker.m_rect.CenterPoint();
			
			dc.DPtoLP(&tail_pt);			// CScrollView-related change convert device units to logical units

			// Check to snap connector to port
			if((*it_con)->GetRefFromPort() == NULL)		// If not already a ref_from port
			{
				from_port = SnapConnectionTailPointToPort(tail_pt); // tail_pt passed by ref. and may be updated.
				if(from_port != NULL)
				{
					(*it_con)->SetRefFromPort(from_port);
				}
			}
			else	// If already a ref-from-port, then the intention on moving the end pt. is to break the assoc. with the port. 
			{
				(*it_con)->SetRefFromPort(NULL);
			}
			(*it_con)->SetConnectionPointTail(tail_pt);


			// Check to snap connector to bend point
			if((*it_con)->GetRefFromPoint() == NULL)		// If not already a ref_from point
			{
				from_point = SnapConnectionTailPointToBendPoint(tail_pt); // tail_pt passed by ref. and may be updated.
				if(from_point != NULL)
				{
					(*it_con)->SetRefFromPoint(from_point);
				}
			}
			else	// If already a ref-from-point, then the intention on moving the end pt. is to break the assoc. with the point. 
			{
				(*it_con)->SetRefFromPoint(NULL);
			}
			(*it_con)->SetConnectionPointTail(tail_pt);
			

			// Set flags	
			SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
			UpdateAllViews(NULL); 		// indicate that sys. should redraw.
			break;
		}

		// If the head pt is clicked
		if(dist_to_head <= disc_r)
		{
			top_left.x = head_pt.x - delta;
			top_left.y = head_pt.y - delta;
			bottom_right.x = head_pt.x + delta;
			bottom_right.y = head_pt.y + delta;

			// CScrollView-related change
			dc.LPtoDP(&top_left);				// convert logical pt based on logical block position to device pt
			dc.LPtoDP(&bottom_right);			// convert logical pt based on logical block position to device pt

			// Set the rect tracker's position
			m_RectTracker.m_rect.SetRect(top_left.x, top_left.y, bottom_right.x, bottom_right.y);

			// Track the mouse’s position till left mouse button up
			tracker_flag = m_RectTracker.Track(pWnd, point, TRUE); // 0 => item not tracked, 1 => item was tracked
		
			// Get the new tracker position and update the new connection end pt position with the tracker position
			head_pt = m_RectTracker.m_rect.CenterPoint();
			
			dc.DPtoLP(&head_pt);			// CScrollView-related change convert device units to logical units

			// Check to snap connector to port
			if((*it_con)->GetRefToPort() == NULL)		// If not already a ref_to port
			{
				to_port = SnapConnectionHeadPointToPort(head_pt); 	// head_pt passed by ref. and may be updated.
				if(to_port != NULL)
				{
					(*it_con)->SetRefToPort(to_port);
				}
			}
			else	// If already a ref-to-port, then the intention on moving the end pt. is to break the assoc. with the port. 
			{
				(*it_con)->SetRefToPort(NULL);
			}
			(*it_con)->SetConnectionPointHead(head_pt);

			// Set flags	
			SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
			UpdateAllViews(NULL); 		// indicate that sys. should redraw.
			break;
		}

	}// end for
	
	// Return flag indicating if an item was tracked: 0 => not tracked, 1 => tracked.
	return tracker_flag;
}

CPort* CDiagramEngDoc::SnapConnectionTailPointToPort(CPoint &tail_pt)
{
	// Passing in a point by reference, and returning a prt-to-CPort "m_pRefFromPort" .	
	int port_connected;
	double disc_r = 0.1*m_dDeltaLength;
	double dist_to_port;
	CPoint port_posn;
	CString sMsg;			// main msg string
	CString sMsgTemp;		// temp msg string
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg
	list<CBlock*>::iterator it_blk;
	list<CBlock*> blk_list = GetSystemModel().GetBlockList();
	vector<CPort*> vec_output_ports;
	vector<CPort*>::iterator it_port;

	// Iterate through block list
	for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
	{
		vec_output_ports = (*it_blk)->GetVectorOfOutputPorts();
		for(it_port = vec_output_ports.begin(); it_port != vec_output_ports.end(); it_port++)	
		{
			port_posn = (*it_port)->GetPortPosition();
			dist_to_port = sqrt(pow(tail_pt.x - port_posn.x,2) + pow(tail_pt.y - port_posn.y,2));

			if(dist_to_port <= disc_r)
			{		
				// Check whether port is connected: (0) disconnected, (1) connected
				port_connected = (*it_blk)->CheckPortConnection(*it_port);
				if(port_connected == 0) // if no connection to this port
				{
					tail_pt = port_posn;	
					return (*it_port); 		// return CPort*, i.e. a from_port			
				}
				else if(port_connected == 1)
				{	
					// Error Msg.
					sMsgTemp.Format("\n WARNING! Connection Failure.\n");
					sMsg+=sMsgTemp;
					sMsgTemp.Format("\n Existing connection attached to this port.");
					sMsg+=sMsgTemp;
					sMsgTemp.Format("\n Please attach connection to an alternative port. \n");
					sMsg+=sMsgTemp;
					AfxMessageBox(sMsg, nType, nIDhelp);
				}
			}
		}
	}
	
	// Return 0 if no "m_pRefFromPort" returned earlier
	return 0;
}

CPort* CDiagramEngDoc::SnapConnectionHeadPointToPort(CPoint &head_pt)
{
	// Passing in a point by reference, and returning a prt-to-CPort "m_pRefToPort".
	int port_connected = 0;
	double disc_r = 0.1*m_dDeltaLength;
	double dist_to_port;
	CPoint port_posn;
	CString sMsg;			// main msg string
	CString sMsgTemp;		// temp msg string
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg
	list<CBlock*>::iterator it_blk;
	list<CBlock*> blk_list = GetSystemModel().GetBlockList();
	vector<CPort*> vec_input_ports;
	vector<CPort*>::iterator it_port;

	// Iterate through block list
	for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
	{
		vec_input_ports = (*it_blk)->GetVectorOfInputPorts();
		for(it_port = vec_input_ports.begin(); it_port != vec_input_ports.end(); it_port++)	
		{
			port_posn = (*it_port)->GetPortPosition();
			dist_to_port = sqrt(pow(head_pt.x - port_posn.x,2) + pow(head_pt.y - port_posn.y,2));

			if(dist_to_port <= disc_r)
			{	
				// Check whether port is connected: (0) disconnected, (1) connected
				port_connected = (*it_blk)->CheckPortConnection(*it_port);
				if(port_connected == 0) // if no connection to this port
				{
					head_pt = port_posn;
					return (*it_port); 	// return CPort*, i.e. a to_port		
				}
				else if(port_connected == 1)
				{	
					// Error Msg.
					sMsgTemp.Format("\n WARNING! Connection Failure.\n");
					sMsg+=sMsgTemp;
					sMsgTemp.Format("\n Existing connection attached to this port.");
					sMsg+=sMsgTemp;
					sMsgTemp.Format("\n Please attach connection to an alternative port. \n");
					sMsg+=sMsgTemp;
					AfxMessageBox(sMsg, nType, nIDhelp);
				}
			}
		}
	}
	
	// Return 0 if no "m_pRefToPort" returned earlier
	return 0;
}


void CDiagramEngDoc::OnInsertBendPoint() 
{
	// TODO: Add your command handler code here

	// DiagramEng (start)
	int inserted;
	int point_on_line;
	CPoint current_pt;
	CPoint head_pt;
	CPoint point = m_ptContextMenu;
	CPoint prev_pt;
	CPoint tail_pt;
	list<CConnection*> &con_list = GetSystemModel().GetConnectionList();
	list<CConnection*>::iterator it_con;
	list<CPoint>::iterator it_pt;
	

	// -- ITERATE THROUGH ALL CONNECTIONS
	for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
	{
		inserted = 0;
		// Get the bend points list
		list<CPoint> &bend_pts_list = (*it_con)->GetConnectionBendPointsList();

		// Get the Connection's tail_pt and head_pt.
		head_pt = (*it_con)->GetConnectionPointHead();
		tail_pt = (*it_con)->GetConnectionPointTail();
		prev_pt = tail_pt;

		// -- IF THERE ARE NO BEND POINTS ON THIS CONNECTION
		if(bend_pts_list.size() == 0)
		{
			current_pt = head_pt;
			point_on_line = 0;
			point_on_line = CheckPointOnLineSegment(prev_pt, current_pt, point);

			if(point_on_line == 1)
			{
				// Create a bend pt. at the front of the list.
				bend_pts_list.push_front(point);
				inserted = 1;
				break; // THIS BREAK EXITS THE it_con LOOP
			}
		}
		else // -- IF THERE ARE BEND POINTS ON THIS CONNECTION
		{
			prev_pt = tail_pt;

			// -- ITERATE THROUGH THIS CONNECTION'S bend_pts_list
			for(it_pt = bend_pts_list.begin(); it_pt != bend_pts_list.end(); it_pt++)
			{
				current_pt = *it_pt;
				point_on_line = 0;
				point_on_line = CheckPointOnLineSegment(prev_pt, current_pt, point);

				if(point_on_line == 1)
				{
					// Create a bend pt. prior to it_pt
					bend_pts_list.insert(it_pt, point);
					inserted = 1;
					break; // THIS BREAK EXITS THE it_pt LOOP
				}

				// Update the previous pt.
				prev_pt = *it_pt;

			}// end for it_pt

			// If a bend point was inserted, then break from the it_con loop
			if(inserted == 1)
			{
				break;	
			}

			// -- NOW CHECK IF BEND POINT IS TO BE INSERTED BW FINAL BEND POINT AND HEAD POINT.
			current_pt = head_pt;
			point_on_line = 0;
			point_on_line = CheckPointOnLineSegment(prev_pt, current_pt, point);

			if(point_on_line == 1)
			{
				// Create a new bend pt. after the (thus far) final bend_pt in the list.
				bend_pts_list.push_back(point);
				inserted = 1;
				break; // THIS BREAK EXITS THE it_con LOOP
			}

		}// end size() != 0
	}// end for it_con
	
	// Set as modified and redraw the doc. (since the connections have been modified)
	SetModifiedFlag(TRUE); 	// set the doc. as having been modified to prompt user to save
	UpdateAllViews(NULL);	// redraw the doc.	

	// DiagramEng (end)
}

int CDiagramEngDoc::TrackConnectionBendPoint(CPoint point, CWnd *pWnd)
{
	int delta = (int)(0.25*m_dDeltaLength);	// integer increment
	int tracker_flag = 0;
	double disc_r = 0.1*m_dDeltaLength;
	double dist;
	CPoint bend_pt;
	CPoint bottom_right;
	CPoint top_left;
	list<CConnection*>::iterator it_con;
	list<CConnection*> con_list = GetSystemModel().GetConnectionList();
	list<CPoint>::iterator it_pt;

	// CScrollView-related change
	CPoint ptLogical = point;				// local point to record the point in logical units
	CClientDC dc(pWnd);						// create a device context using the pointer-to-CWnd pWnd.
	((CScrollView*)pWnd)->OnPrepareDC(&dc);	// prepare the device context casting pWnd to ptr-to-CScrollView
	dc.DPtoLP(&ptLogical);					// use the dc to convert ptLogical from device to logical units

	// Iterate through connection list
	for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
	{
		// Get connection bend pts.
		list<CPoint> &bend_pts_list = (*it_con)->GetConnectionBendPointsList();

		// Iterate through all bend pts. for this conection
		for(it_pt = bend_pts_list.begin(); it_pt != bend_pts_list.end(); it_pt++)
		{
			bend_pt = *it_pt;

			// Determine Euclidean dist bw mouse point and connection bend pt.
			//dist = sqrt(pow(bend_pt.x - point.x,2) + pow(bend_pt.y - point.y,2));
			dist = sqrt(pow(bend_pt.x - ptLogical.x,2) + pow(bend_pt.y - ptLogical.y,2));	// CScrollView-relate change using ptLogical
			
			// If the bend pt is clicked
			if(dist <= disc_r)
			{
				// Assign rectangle setting values
				top_left.x = bend_pt.x - delta;
				top_left.y = bend_pt.y - delta;
				bottom_right.x = bend_pt.x + delta;
				bottom_right.y = bend_pt.y + delta;

				// CScrollView-related change
				dc.LPtoDP(&top_left);		// convert logical pt based on logical block posn to device pt
				dc.LPtoDP(&bottom_right);	// convert logical pt based on logical block posn to device pt

				// Set the rect tracker's position
				m_RectTracker.m_rect.SetRect(top_left.x, top_left.y, bottom_right.x, bottom_right.y);

				// Track the mouse’s position till left mouse button up
				tracker_flag = m_RectTracker.Track(pWnd, point, TRUE); // 0 => item not tracked, 1 => item was tracked
			
				// Get the new tracker position and update the connection bend pt position with the tracker position
				*it_pt = m_RectTracker.m_rect.CenterPoint();

				dc.DPtoLP(&(*it_pt));		// CScrollView-related change convert device units to logical units

				// Update any connection's tail point if it was connected to this bend point
				m_SystemModel.UpdateConnectionPointTailToBendPoint(&(*it_pt));

				// Set flags	
				SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
				UpdateAllViews(NULL); 		// indicate that sys. should redraw.
				break;

			}// end if dist
		}// end for it_pt
	}// end for it_con
		
	return tracker_flag;
}

int CDiagramEngDoc::DeleteBlock()
{
	int		count = 0;		// counter
	int		delete_blk = 0;	// blk deletion flag
	double	dist;			// Euclidean dist bw. block posn and point posn.
	double	width;			// block width
	CPoint	blk_posn;		// block posn.
	CPoint	point;			// local point var.
	CString sMsg;			// main msg string
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.
	list<CBlock*>::iterator it_blk;			// iterator
	list<CBlock*>::iterator it_er = NULL;	// element to erase

	// Get the point at which the context menu was invoked
	point = m_ptContextMenu;	// init a local copy.

	// Print msg.
	//sMsg.Format("\n CDiagramEngDoc::DeleteBlock(), point (x,y) = (%d,%d)\n", point.x, point.y);
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// Get a copy of the blk_list in the system model
	list<CBlock*> &blk_list = GetSystemModel().GetBlockList();	// MUST BE A REFERENCE!

	// Iterate through the list to find which item to delete.
	for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
	{
		blk_posn = (*it_blk)->GetBlockPosition();
		width = (*it_blk)->GetBlockShape().GetBlockWidth();

		dist = sqrt(pow((blk_posn.x - point.x),2) + pow((blk_posn.y - point.y),2));
		if(dist <= 0.5*width*0.5)
		{
			//sMsg.Format("\n CDiagramEngDoc::DeleteBlock(), blk(x,y) = (%d,%d), point(x,y) = (%d,%d)\n", blk_posn.x, blk_posn.y, point.x, point.y);
			//AfxMessageBox(sMsg, nType, nIDhelp);
			
			// Record which block to erase
			delete_blk = 1;
			it_er = it_blk;
			break;
		}
	}

	// Delete the item in the list
	if(delete_blk == 1)
	{
		// Dereference block's ports (allowing previously connected connections to be reassigned to new ports)
		DisconnectEndPointsFromPorts(*it_er);		

		// Delete Output Block View
		DeleteOutputBlockView(*it_er);

		// Delete block
		delete *it_er;				// delete actual block pointed to by it_er
		blk_list.erase(it_er);		// delete element at offset it_er in list (that held the block)
		
		count = m_SystemModel.GetBlockList().size();
		//sMsg.Format("\n CDiagramEngDoc::DeleteBlock(), size = %d\n", count);
		//AfxMessageBox(sMsg, nType, nIDhelp);
	}

	// Set as modified and redraw the doc.
	SetModifiedFlag(TRUE);	// set the doc. as having been modified to prompt user to save
	UpdateAllViews(NULL);	// indicate that sys. should redraw.

	// Return a flag indicating whether an item was deleted
	return delete_blk;
}

int CDiagramEngDoc::DeleteConnectionBendPoint()
{
	int		count = 0;						// counter
	int		delete_bp = 0;					// bend pt. deletion flag
	double	dist;							// Euclidean dist bw. bend pt. posn and mouse point posn.
	double	disc_r = 0.8*m_dDeltaLength*0.5;// disc or radius r
	CPoint	bend_pt_posn;					// bend pt. posn.
	CPoint	point;							// local point var.
	CString sMsg;							// main msg string
	UINT nType = MB_OK;						// style of msg. box
	UINT nIDhelp = 0;						// help context ID for the msg.
	list<CConnection*>::iterator it_con;	// connection iterator
	list<CPoint>::iterator it_pt;			// bend pt iterator
	list<CPoint>::iterator it_er = NULL;	// erase iterator (element to erase)

	// Get the point at which the context menu was invoked
	point = m_ptContextMenu;	// init a local copy.

	// Print msg.
	//sMsg.Format("\n CDiagramEngDoc::DeleteConnectionBendPoint(), point (x,y) = (%d,%d)\n", point.x, point.y);
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// Get a copy of the bend_pts_list in the system model
	list<CConnection*> &con_list = GetSystemModel().GetConnectionList();	// MUST BE A REFERENCE!
	

	// Iterate through the connection list
	for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
	{
		// Get the bend points list
		list<CPoint> &bend_pts_list = (*it_con)->GetConnectionBendPointsList();

		// Iterate through the bend pts. list
		delete_bp = 0;	// init delete_bp = 0 since, at present no bend_pt is to be deleted
		for(it_pt = bend_pts_list.begin(); it_pt != bend_pts_list.end(); it_pt++)
		{
			bend_pt_posn = *it_pt;
		
			// Check whether mouse cursor within disc_r of bend pt.
			dist = sqrt(pow((bend_pt_posn.x - point.x),2) + pow((bend_pt_posn.y - point.y),2));
			if(dist <= disc_r)
			{
				//sMsg.Format("\n CDiagramEngDoc::DeleteConnectionBendPoint(), blk(x,y) = (%d,%d), point(x,y) = (%d,%d)\n", blk_posn.x, blk_posn.y, point.x, point.y);
				//AfxMessageBox(sMsg, nType, nIDhelp);
				
				// Record which block to erase
				delete_bp = 1;
				it_er = it_pt;	
				break;
			}
		}// end for it_pt

		// Delete the item in the list
		if(delete_bp == 1)
		{
			// Unsnap any tail points connected to this bend point prior to delete bend point
			DisconnectTailPointFromBendPoint(&(*it_er));	// pass address of the bend_pt
			
			// Delete element at offset it_er in list (that held the bend pt)
			bend_pts_list.erase(it_er);	
		
			count = (*it_con)->GetConnectionBendPointsList().size();
			//sMsg.Format("\n CDiagramEngDoc::DeleteConnectionBendPoint(), size = %d\n", count);
			//AfxMessageBox(sMsg, nType, nIDhelp);

			// Bend pt deleted, so break out of connector loop.
			break;
		}

	}// end for it_con

	// Set as modfied and redraw the doc.
	SetModifiedFlag(TRUE);	// set the doc. as having been modified to prompt user to save
	UpdateAllViews(NULL);	// indicate that sys. should redraw.

	// Return a flag indicating whether an item was deleted
	return delete_bp;
}

int CDiagramEngDoc::DeleteConnection()
{
	int delete_con = 0;
	double disc_r = 0.2*m_dDeltaLength;
	double dist;
	CPoint current_pt;
	CPoint head_pt;
	CPoint midpoint;
	CPoint point = m_ptContextMenu;
	CPoint prev_pt;
	CPoint tail_pt;
	list<CConnection*> &con_list = GetSystemModel().GetConnectionList();
	list<CConnection*>::iterator it_con;
	list<CConnection*>::iterator it_er;
	list<CPoint> local_pts_list;
	list<CPoint>::iterator it_pt;
	list<CPoint>::iterator temp_it;
	CString	sMsg;				// string to be displayed
	UINT nType = MB_OK;			// style of msg. box
	UINT nIDhelp = 0;			// help context ID for the msg.
	

	// Iterate through all connections
	for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
	{
		// Get the bend points list
		list<CPoint> &bend_pts_list = (*it_con)->GetConnectionBendPointsList();
		local_pts_list = bend_pts_list;

		// Augment the bend points list with the Connection's tail_pt and head_pt.
		head_pt = (*it_con)->GetConnectionPointHead();
		tail_pt = (*it_con)->GetConnectionPointTail();
		local_pts_list.push_front(tail_pt);
		local_pts_list.push_back(head_pt);

		//sMsg.Format("\n CDiagramEngDoc::DeleteConnection(), local_pts_list.size() = %d\n", local_pts_list.size());
		//AfxMessageBox(sMsg, nType, nIDhelp);

		// Iterate through points making up this connection after the tail pt.
		prev_pt = tail_pt;
		temp_it = local_pts_list.begin();
		temp_it++;

		// -- Delete connection object based on clicking the midpoint of a line segment
		/*for(it_pt = temp_it; it_pt != local_pts_list.end(); it_pt++)
		{
			current_pt = *it_pt;
			
			// Calculate the midpoint of the line segment bounded by connection line pts.
			midpoint.x = (current_pt.x + prev_pt.x)/2;
			midpoint.y = (current_pt.y + prev_pt.y)/2;

			// Calculate dist of mouse cursor pt. from midpoint
			dist = sqrt(pow((midpoint.x - point.x),2) + pow((midpoint.y - point.y),2));
		
			// If mouse cursor is in the ngbd of the midpoint
			if(dist <= disc_r)
			{
				// Record which connection to erase
				delete_con = 1;
				it_er = it_con;
				break;
			}

			// Update the previous pt.
			prev_pt = *it_pt;

		}// end for it_pt
		*/

		// -- Delete connection object based on clicking on the head point only.
		// Calculate dist of mouse cursor pt. from head point
		dist = sqrt(pow((head_pt.x - point.x),2) + pow((head_pt.y - point.y),2));

		// If mouse cursor is in the ngbd of the head point
		if(dist <= disc_r)
		{
			// Record which connection to erase
			delete_con = 1;
			it_er = it_con;
			break;
		}

	}// end for it_con

	// Delete the connection in the list
	if(delete_con == 1)
	{
		// Disconnect all tail points from all the bend points on this connection.
		DisconnectTailPointsFromBendPoints(*it_er);

		delete *it_er;				// delete actual connection pointed to by it_er
		con_list.erase(it_er);		// delete element at offset it_er in list (that held the connection)
		
		//count = m_SystemModel.GetBlockList().size();
		//sMsg.Format("\n CDiagramEngDoc::DeleteConnection(), size = %d\n", count);
		//AfxMessageBox(sMsg, nType, nIDhelp);
	}

	// Set as modfied and redraw the doc.
	SetModifiedFlag(TRUE);	// set the doc. as having been modified to prompt user to save
	UpdateAllViews(NULL);	// indicate that sys. should redraw.

	// Return a flag indicating whether an item was deleted
	return delete_con;
}

CPoint* CDiagramEngDoc::SnapConnectionTailPointToBendPoint(CPoint &tail_pt)
{
	// Passing in a point by reference, and returning a prt-to-CPoint "m_pRefFromPoint".	
	double disc_r = 0.25*m_dDeltaLength;	
	double dist_to_bend_pt;
	CPoint bend_pt;
	list<CConnection*>::iterator it_con;
	list<CConnection*> con_list = GetSystemModel().GetConnectionList();
	list<CPoint>::iterator it_pt;
	

	// Iterate through connection list
	for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
	{
		// Get the bend points list for this partic. connection obj.
		list<CPoint> &bend_pts_list = (*it_con)->GetConnectionBendPointsList();

		// Iterate through the bend points list
		for(it_pt = bend_pts_list.begin(); it_pt != bend_pts_list.end(); it_pt++)	
		{
			bend_pt = *it_pt;
			dist_to_bend_pt = sqrt(pow(tail_pt.x - bend_pt.x,2) + pow(tail_pt.y - bend_pt.y,2));

			if(dist_to_bend_pt <= disc_r)
			{		
				tail_pt = bend_pt;	
				return &(*it_pt); 		// return the address of the bend_pt			
			}
		}
	}
	
	// Return 0 if no "m_pRefFromPoint" returned earlier
	return 0;
}

void CDiagramEngDoc::DisconnectTailPointFromBendPoint(CPoint *bend_pt)
{
	list<CConnection*>::iterator it_con;

	// Get the connection list
	list<CConnection*> &con_list = GetSystemModel().GetConnectionList();	// MUST BE A REFERENCE!

	// Iterate through the connections 
	for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
	{
		// If the connection tail pt. was attached to the bend pt.
		if((*it_con)->GetRefFromPoint() == bend_pt)
		{
			// Set the tail pt. to NULL since the bend pt. will be deleted.
			(*it_con)->SetRefFromPoint(NULL);
		}
	}
}

void CDiagramEngDoc::DisconnectTailPointsFromBendPoints(CConnection *con)
{
	list<CPoint>::iterator it_pt;

	list<CPoint> &bend_pts_list = con->GetConnectionBendPointsList();

	for(it_pt = bend_pts_list.begin(); it_pt != bend_pts_list.end(); it_pt++)
	{	
		// Unsnap any tail points connected to this bend point
		DisconnectTailPointFromBendPoint(&(*it_pt));	// pass address of the bend_pt
	}
}

void CDiagramEngDoc::DisconnectEndPointFromPort(CPort *port)
{
	list<CConnection*>::iterator it_con;

	// Get the connection list
	list<CConnection*> &con_list = GetSystemModel().GetConnectionList();	// MUST BE A REFERENCE!

	// Iterate through the connections 
	for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
	{
		// If the connection end point was attached to the block's port.
		if((*it_con)->GetRefFromPort() == port)
		{
			// Set the ref from port to NULL 
			(*it_con)->SetRefFromPort(NULL);
		}

		// If the connection end point was attached to the block's port.
		if((*it_con)->GetRefToPort() == port)
		{
			// Set the ref to port to NULL 
			(*it_con)->SetRefToPort(NULL);
		}
	}
}

int CDiagramEngDoc::DoubleLeftClickBlock(CPoint point)
{
	int dbl_click_flag = 0;	// if block double-clicked flag = 1, else flag = 0.
	double blk_width;		// block width
	double dist;			// dist bw mouse click and CM of block.
	CPoint blk_posn;		// block posn.
	list<CBlock*>::iterator it_blk;

	// Get a copy of the blk_list in the system model
	list<CBlock*> &blk_list = GetSystemModel().GetBlockList();	// MUST BE A REFERENCE!

	// Iterate through block list
	for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
	{
		blk_posn = (*it_blk)->GetBlockPosition();
		blk_width = (*it_blk)->GetBlockShape().GetBlockWidth();
		dist = sqrt(pow(blk_posn.x - point.x,2) + pow(blk_posn.y - point.y,2));
		
		// If the block is clicked
		dbl_click_flag = 0;				// reset to zero
		if(dist <= 0.3*blk_width*0.5)
		{
			// Print a msg. that block double-clicked
			//AfxMessageBox("\n CDiagramEngDoc::DoubleLeftClickBlock()\n", MB_OK, 0);

			// Invoke the block-related dialog wnd para setting fn
			(*it_blk)->BlockDlgWndParameterInput();

			// Since this block has been double-clicked, then break
			dbl_click_flag = 1;
			break;
		}
	}// end for

	return dbl_click_flag;
}

int CDiagramEngDoc::CheckPointOnLineSegment(CPoint prev_pt, CPoint current_pt, CPoint point)
{
	int point_on_line = 0;
	double delta_x;
	double delta_y;
	double eps = 0.20*m_dDeltaLength;
	double gradient;
	double y_val;
	CString sMsg;			// main msg string
	CString sMsg1;			// substring
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.
	
	// Message
	/*sMsg.Format("CheckPointOnLineSegment, eps = %lf", eps);
	sMsg1.Format("\n prev_pt = (%d,%d), point = (%d,%d), current_pt = (%d,%d)\n", prev_pt.x, prev_pt.y, point.x, point.y, current_pt.x, current_pt.y);
	sMsg += sMsg1;
	AfxMessageBox(sMsg, nType, nIDhelp);
	*/

	// -- CONSIDER A VERTICAL OR NEAR VERTICAL CONNECTION LINE SEGMENT
	if(abs(current_pt.x - prev_pt.x) <= eps)
	{
		// Check to see if mouse point is on the vertical connection line segment in the x domain
		if((point.x >= (min(prev_pt.x, current_pt.x) - 0.5*eps)) && (point.x <= (max(prev_pt.x, current_pt.x) + 0.5*eps)))
		{
			// Check to see if mouse point is on the vertical connection line segment in the y domain
			if((point.y >= min(prev_pt.y, current_pt.y)) && (point.y <= max(prev_pt.y, current_pt.y)))
			{
				// Point lies on line segment
				point_on_line = 1;
			}
		}
	}
	else // -- CONSIDER A NON-VERTICAL CONNECTION LINE SEGMENT: equ. of line: y - y0 = ((y1-y0)/(x1-x0))*(x - x0);
	{
		// Check to see if mouse point is on the connection line segment in the x domain
		if((point.x >= min(prev_pt.x, current_pt.x)) && (point.x <= max(prev_pt.x, current_pt.x)))
		{
			delta_x = (double)(current_pt.x - prev_pt.x);
			delta_y = (double)(current_pt.y - prev_pt.y);
			gradient = delta_y/delta_x;
			y_val = gradient*(point.x - prev_pt.x) + prev_pt.y;
			
			// Check to see if mouse point is close enough to the line in the y domain
			if(fabs(point.y - y_val) <= eps)
			{
				// Point lies on line segment
				point_on_line = 1;
			}
		}
	}// end if line segment

	// Return flag indicating whether point lies on line segment: (0) pt. not on line, (1) pt. on line
	return point_on_line;
}

int CDiagramEngDoc::TrackMultipleItems(CPoint point, CWnd *pWnd)
{
	int intersected = 0;
	int item_tracked = 0;
	int manual_rect = 1;
	int tracker_flag = 0;
	double blk_width;
	double delta = 0.25*m_dDeltaLength;
	static CPoint tracker_init;		// require tracker_init for else clause on re-entry of OnLButtonDown 
	CPoint an_posn;
	CPoint bend_pt;
	CPoint blk_posn;
	CPoint delta_posn;
	CPoint head_pt;
	CPoint tail_pt;
	CRectTracker temp_tracker;
	list<CAnnotation*> &annotation_list = GetSystemModel().GetAnnotationList();
	list<CAnnotation*>::iterator it_an;
	list<CBlock*> &blk_list = GetSystemModel().GetBlockList();
	list<CBlock*>::iterator it_blk;
	list<CConnection*>::iterator it_con;
	list<CConnection*> &con_list = GetSystemModel().GetConnectionList();
	list<CPoint>::iterator it_pt;
	CClientDC dc(pWnd);

	// CScrollView-related change
	CPoint ptLogical;						// logical point given change in scroll position
	((CScrollView*)pWnd)->OnPrepareDC(&dc);	// prepare the dc given the usage of the CScrollView base class
	ptLogical = point;						// make a copy of the device point
	dc.DPtoLP(&ptLogical);					// change point from device to logical coords
	

	// -- IF A RUBBER BAND HAS NOT BEEN CREATED, THEN CREATE IT.
	if(m_iRubberBandCreated == 0) 
	{
		// Create a rubber band
		m_RectTracker.TrackRubberBand(pWnd, point, TRUE);
		m_RectTracker.m_rect.NormalizeRect();
		dc.DPtoLP(&m_RectTracker.m_rect);						// CScrollView-related change: change to logical coords
		tracker_init = m_RectTracker.m_rect.CenterPoint();

		// Create a temporary rectangle indicating where the initial CRectTracker rectangle was.
		// This is done, since the temporary CRectTracker rectangle disappears upon fn exit.	
		if(manual_rect)	// Manually create a red, dotted lined rectangle
		{
			CPen lpen(PS_DOT, 1, RGB(255,0,0));			// create a pen
			CPen *pOldPen = dc.SelectObject(&lpen);		// create a copy
			dc.SetROP2(R2_NOTXORPEN);					// make the rectangle transparent
			dc.Rectangle(m_RectTracker.m_rect.TopLeft().x , m_RectTracker.m_rect.TopLeft().y, m_RectTracker.m_rect.BottomRight().x, m_RectTracker.m_rect.BottomRight().y); // create the rectangle using the m_RectTracker object coords.
			dc.SelectObject(pOldPen);					// reset the old pen
		}
		else			// Use the default black dotted lined rectangle
		{
			m_RectTracker.m_nStyle = CRectTracker::dottedLine;
			m_RectTracker.Draw(&dc);
		}
		
		// Set flags.
		m_iRubberBandCreated = 1;					// rubber band state is active
		tracker_flag = 1;							// tracking of multiple items has commenced
		return tracker_flag;
	}
	// -- IF A RUBBER BAND HAS BEEN CREATED, BUT LEFT-CLICK OUTSIDE OF ENCLOSING REGION, THEN END TRACK ACTION.
	// CScrollView-related change: use the logical point "ptLogical" rather than the device point "point"
	//else if((m_RectTracker.HitTest(point) == CRectTracker::hitNothing) && (m_iRubberBandCreated == 1)) 
	else if((m_RectTracker.HitTest(ptLogical) == CRectTracker::hitNothing) && (m_iRubberBandCreated == 1))
	{
		m_iRubberBandCreated = 0;					// end the rubber band state
		SetKeyFlagTrack(0);							// reset the key-based track flag since tracking aborted
		tracker_flag = 0;							// tracking did not occur
		return tracker_flag;
	}
	// -- IF A RUBBER BAND HAS BEEN CREATED, AND LEFT-CLICK INSIDE ENCLOSING REGION, THEN POSSIBLY MOVE ITEMS.
	// CScrollView-related change: use the logical point "ptLogical" rather than the device point "point"
	//else if((m_RectTracker.HitTest(point) != CRectTracker::hitNothing) && (m_iRubberBandCreated == 1)) 
	else if((m_RectTracker.HitTest(ptLogical) != CRectTracker::hitNothing) && (m_iRubberBandCreated == 1))
	{
		// Create a temp tracker since m_RectTracker will have its coords updated when the rubber band is moved.
		temp_tracker = m_RectTracker;
		
		// -- ITERATE THROUGH BLOCKS
		for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
		{
			// BLOCK
			blk_posn = (*it_blk)->GetBlockPosition();
			blk_width = (*it_blk)->GetBlockShape().GetBlockWidth();

			// Determine if item lies within rubber band
			intersected = DetermineCurrentAndIntersectRects(temp_tracker, blk_posn, (blk_width*0.5));
			if(intersected) 
			{
				if(item_tracked == 0) 
				{
					// Determine the tracker's change in position
					// CScrollView-related change: use the logical point "ptLogical" rather than the device point "point"
					//item_tracked = DetermineTrackerDeltaPosition(pWnd, point, tracker_init, delta_posn);
					item_tracked = DetermineTrackerDeltaPosition(pWnd, ptLogical, tracker_init, delta_posn);
				}

				// Update the block posn.
				(*it_blk)->SetBlockPosition(blk_posn + delta_posn);

				// Set flags	
				SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
				UpdateAllViews (NULL); 		// indicate that sys. should redraw.
			}
		}// end for it_blk

		
		// -- ITERATE THROUGH ALL CONNECTIONS (of this model) 
		for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
		{
			// TAIL POINT
			tail_pt = (*it_con)->GetConnectionPointTail();
		
			// Determine if item lies within rubber band
			intersected = DetermineCurrentAndIntersectRects(temp_tracker, tail_pt, delta);
			if(intersected) 
			{
				if(item_tracked == 0) 
				{
					// Determine the tracker's change in position
					// CScrollView-related change: use the logical point "ptLogical" rather than the device point "point"
					//item_tracked = DetermineTrackerDeltaPosition(pWnd, point, tracker_init, delta_posn);
					item_tracked = DetermineTrackerDeltaPosition(pWnd, ptLogical, tracker_init, delta_posn);
				}

				// Check if the TAIL POINT is NOT CONNECTED to a PORT or a BEND POINT: if so move it.
				if(((*it_con)->GetRefFromPort() == NULL) && ((*it_con)->GetRefFromPoint() == NULL))
				{
					// Update tail pt. posn.
					(*it_con)->SetConnectionPointTail(tail_pt + delta_posn);

					// Set flags
					SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
					UpdateAllViews(NULL); 		// indicate that sys. should redraw.
				}
			}

			// HEAD POINT
			head_pt = (*it_con)->GetConnectionPointHead();

			// Determine if item lies within rubber band
			intersected = DetermineCurrentAndIntersectRects(temp_tracker, head_pt, delta);
			if(intersected) 
			{
				if(item_tracked == 0) 
				{
					// Determine the tracker's change in position
					// CScrollView-related change: use the logical point "ptLogical" rather than the device point "point"
					//item_tracked = DetermineTrackerDeltaPosition(pWnd, point, tracker_init, delta_posn);
					item_tracked = DetermineTrackerDeltaPosition(pWnd, ptLogical, tracker_init, delta_posn);
				}

				// Check if the HEAD POINT is NOT CONNECTED to a PORT: if so move it.
				if((*it_con)->GetRefToPort() == NULL)
				{
					// Update head pt. posn.
					(*it_con)->SetConnectionPointHead(head_pt + delta_posn);

					// Set flags
					SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
					UpdateAllViews(NULL); 		// indicate that sys. should redraw.
				}
			}
			

			// -- ITERATE THROUGH ALL BEND POINTS FOR THIS CONNECTION

			list<CPoint> &bend_pts_list = (*it_con)->GetConnectionBendPointsList();

			for(it_pt = bend_pts_list.begin(); it_pt != bend_pts_list.end(); it_pt++)
			{
				// BEND POINT
				bend_pt = *it_pt;

				// Determine if item lies within rubber band
				intersected = DetermineCurrentAndIntersectRects(temp_tracker, bend_pt, delta);
				if(intersected) 
				{
					if(item_tracked == 0) 
					{
						// Determine the tracker's change in position
						// CScrollView-related change: use the logical point "ptLogical" rather than the device point "point"
						//item_tracked = DetermineTrackerDeltaPosition(pWnd, point, tracker_init, delta_posn);
						item_tracked = DetermineTrackerDeltaPosition(pWnd, ptLogical, tracker_init, delta_posn);
					}
				
					// Update the connection bend pt posn.
					*it_pt = *it_pt + delta_posn;

					// Update any connection's tail point if it was connected to this bend point
					m_SystemModel.UpdateConnectionPointTailToBendPoint(&(*it_pt));

					// Set flags	
					SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
					UpdateAllViews(NULL); 		// indicate that sys. should redraw.
				}
			}// end for it_pt
		}// end for it_con
	
		
		// -- ITERATE THROUGH ANNOTATIONS
		for(it_an = annotation_list.begin(); it_an != annotation_list.end(); it_an++)
		{
			// ANNOTATION
			an_posn = (*it_an)->GetPosition();
		
			// Determine if item lies within rubber band
			intersected = DetermineCurrentAndIntersectRects(temp_tracker, an_posn, delta);
			if(intersected) 
			{
				if(item_tracked == 0) 
				{
					// Determine the tracker's change in position
					// CScrollView-related change: use the logical point "ptLogical" rather than the device point "point"
					//item_tracked = DetermineTrackerDeltaPosition(pWnd, point, tracker_init, delta_posn);
					item_tracked = DetermineTrackerDeltaPosition(pWnd, ptLogical, tracker_init, delta_posn);
				}

				// Update annotation posn.
				(*it_an)->SetPosition(an_posn + delta_posn);

				// Set flags	
				SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
				UpdateAllViews (NULL); 		// indicate that sys. should redraw.
			}
		}// end for it_an


		// Set flags
		m_iRubberBandCreated = 0;				// end the rubber band state
		SetKeyFlagTrack(0);						// reset the key-based track flag since tracking aborted	

		if(item_tracked == 0)					// if no item was tracked
		{
			tracker_flag = 0;
			return tracker_flag;
		}
		else									// if an item was tracked
		{
			tracker_flag = 1;
			return tracker_flag;
		}	
	}

	// Return the tracker_flag
	return tracker_flag;
}

void CDiagramEngDoc::SetKeyFlagTrack(int key_flag)
{
	// Set the member variable.
	m_iKeyFlagTrack = key_flag;
}

int CDiagramEngDoc::DetermineTrackerDeltaPosition(CWnd *pWnd, CPoint point, CPoint tracker_init, CPoint &delta_posn)
{
	int item_tracked = 0;
	CPoint tracker_final;

	// CScrollView-related change: create a dc, then prepare it for subsequent DPtoLP() usage.
	CClientDC dc(pWnd);
	((CScrollView*)pWnd)->OnPrepareDC(&dc);

	// Track the rectangle and determine its change in position.
	m_RectTracker.Track(pWnd, point, TRUE); // 0 => item not tracked, 1 => item was tracked

	// CScrollView-related change: convert device point to logical point
	dc.DPtoLP(&m_RectTracker.m_rect);

	tracker_final = m_RectTracker.m_rect.CenterPoint();
	
	delta_posn = tracker_final - tracker_init;

	// Set the item_tracked flag
	if((delta_posn.x == 0) && (delta_posn.y == 0))
	{
		item_tracked = 0;
	}
	else
	{
		item_tracked = 1;
	}

	// Return a flag indicating a tracking event
	return item_tracked;
}

int CDiagramEngDoc::DetermineCurrentAndIntersectRects(CRectTracker temp_tracker, CPoint item_posn, double delta)
{
	int intersected = 0;
	CPoint bottom_right;
	CPoint top_left;
	CRect current_rect;
	CRect intersect_rect;

	// Determine the coordinates of the item
	top_left.x = (int)(item_posn.x - delta);
	top_left.y = (int)(item_posn.y - delta);
	bottom_right.x = (int)(item_posn.x + delta);
	bottom_right.y = (int)(item_posn.y + delta);

	// Determine the current and intersect rectangles
	current_rect.SetRect(top_left.x, top_left.y, bottom_right.x, bottom_right.y);
	intersect_rect.IntersectRect(temp_tracker.m_rect, current_rect);

	// Check whether the item lies within the rubber band region, i.e. it intersects.
	if(intersect_rect == current_rect)
	{
		intersected = 1;
	}
	else
	{
		intersected = 0;
	}

	// Return the intersected flag.
	return intersected;
}

void CDiagramEngDoc::DisconnectEndPointsFromPorts(CBlock *block)
{	
	vector<CPort*>::iterator it_port;

	// Get vector of input ports
	vector<CPort*> &vec_of_input_ports = block->GetVectorOfInputPorts();	

	// Get vector of output ports
	vector<CPort*> &vec_of_output_ports = block->GetVectorOfOutputPorts();	

	// Iterate through the vector of output ports
	for(it_port = vec_of_output_ports.begin(); it_port != vec_of_output_ports.end(); it_port++)
	{
		DisconnectEndPointFromPort(*it_port);
	}

	// Iterate through the vector of input ports
	for(it_port = vec_of_input_ports.begin(); it_port != vec_of_input_ports.end(); it_port++)
	{
		DisconnectEndPointFromPort(*it_port);
	}
}

void CDiagramEngDoc::OnInitTrackMultipleItems() 
{
	// TODO: Add your command handler code here

	// DiagramEng (start)
	int key_flag = 1;	// 0 => can't call TrackMultipleItems(), 1 => can call TrackMultipleItems.

	// Reset select-all action
	ResetSelectAll();

	// Set the m_iKeyFlagTrack member variable using SetKeyFlagTrack(1)
	SetKeyFlagTrack(key_flag);

	// DiagramEng (end)
}

void CDiagramEngDoc::OnSimNumericalSolver() 
{
	// TODO: Add your command handler code here

	// DiagramEng (start)
	//AfxMessageBox("\n CDiagramEngDoc::OnSimNumericalSolver()\n", MB_OK, 0);

	// Transfer values bw CNumericalSolverDialog and CSystemModel
	m_SystemModel.DlgWndParameterInput();

	// DiagramEng (end)
}

void CDiagramEngDoc::OnEditAddMultipleBlocks() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)

	// Local var declaration
	int display_item = 1;	// used to display msg box or dlg wnd.

	// Display a msg. 
	if(display_item == 0)
	{
		AfxMessageBox("\n CDiagramEngDoc::OnEditAddMultipleBlocks()\n", MB_OK, 0);
	}
	else if(display_item == 1)
	{
		CBlockLibDlg oDlg;		// create a dlg obj. of class CBlockLibDlg : public CDialog

		// Return val of DoModal() fn of ancestor class CDialog is checked to determine which btn was clicked.
		if(oDlg.DoModal() == IDOK)
		{	
			//AfxMessageBox("\n CDiagramEngDoc::OnEditAddMultipleBlocks()\n", MB_OK, 0);
		}	
	}
	else if(display_item == 2)
	{
		/*CTreeDialog oDlg;		// create a dlg obj. of class CTreeDialog : public CDialog

		// Return val of DoModal() fn of ancestor class CDialog is checked to determine which btn was clicked.
		if(oDlg.DoModal() == IDOK) 
		{	
			AfxMessageBox("\n CDiagramEngDoc::OnEditAddMultipleBlocks()\n", MB_OK, 0);
		}*/
	}

	// DiagramEng (end)
}

void CDiagramEngDoc::OnEditDeleteGroupedItems() 
{
	// TODO: Add your command handler code here
	int intersected = 0;
	int intersected_head = 0;
	int intersected_tail = 0;
	double blk_width;
	double delta = 0.25*m_dDeltaLength;
	CPoint an_posn;
	CPoint bend_pt;
	CPoint blk_posn;
	CPoint head_pt;
	CPoint tail_pt;
	CRectTracker temp_tracker;
	list<CAnnotation*> &annotation_list = GetSystemModel().GetAnnotationList();
	list<CAnnotation*>::iterator it_an;
	list<CBlock*> &blk_list = GetSystemModel().GetBlockList();
	list<CBlock*>::iterator it_blk;
	list<CConnection*>::iterator it_con;
	list<CConnection*> &con_list = GetSystemModel().GetConnectionList();
	list<CPoint>::iterator it_pt;

	
	// -- IF A RUBBER BAND HAS BEEN CREATED
	if(m_iRubberBandCreated == 1)
	{
		// Create a temp tracker since m_RectTracker will have its coords updated when the rubber band is moved.
		temp_tracker = m_RectTracker;
		
		// -- ITERATE THROUGH BLOCKS
		it_blk = blk_list.begin();
		while(it_blk != blk_list.end())
		{
			// Get block properties
			blk_posn = (*it_blk)->GetBlockPosition();
			blk_width = (*it_blk)->GetBlockShape().GetBlockWidth();

			// Determine if item lies within rubber band
			intersected = DetermineCurrentAndIntersectRects(temp_tracker, blk_posn, (blk_width*0.5));
			if(intersected) 
			{
				// Delete Output Block View
				DeleteOutputBlockView(*it_blk);

				// Delete block
				delete *it_blk;				     // delete actual block pointed to by it_blk
				it_blk = blk_list.erase(it_blk); // delete element at offset it_blk in list (that held the block)
			}
			else // only increment the iterator if there were no intersection
			{
				it_blk++;
			}
			
		}// end for it_blk

		
		// -- ITERATE THROUGH ALL CONNECTIONS (of this model)
		it_con = con_list.begin();
		while(it_con != con_list.end())
		{
			// -- ITERATE THROUGH ALL BEND POINTS FOR THIS CONNECTION

			list<CPoint> &bend_pts_list = (*it_con)->GetConnectionBendPointsList();

			it_pt = bend_pts_list.begin();
			while(it_pt != bend_pts_list.end())
			{
				// BEND POINT
				bend_pt = *it_pt;

				// Determine if item lies within rubber band
				intersected = DetermineCurrentAndIntersectRects(temp_tracker, bend_pt, delta);
				if(intersected) 
				{
					// Unsnap any tail points connected to this bend point prior to delete bend point
					DisconnectTailPointFromBendPoint(&(*it_pt));	// pass address of the bend_pt
			
					// Delete element at offset it_er in list (that held the bend pt)
					it_pt = bend_pts_list.erase(it_pt);
				}
				else
				{
					it_pt++;
				}
			}// end for it_pt


			// -- DELETE WHOLE CONNECTION OJBJECT

			// -- Get Tail Point
			tail_pt = (*it_con)->GetConnectionPointTail();

			// Determine if item lies within rubber band
			intersected_tail = DetermineCurrentAndIntersectRects(temp_tracker, tail_pt, delta);

			// -- Get Head Point
			head_pt = (*it_con)->GetConnectionPointHead();

			// Determine if item lies within rubber band
			intersected_head = DetermineCurrentAndIntersectRects(temp_tracker, head_pt, delta);

			// -- Check if head or tail points circumscribed: if so delete whole connection
			if(intersected_tail || intersected_head) 
			{
				// Disconnect all tail points from all the bend points on this connection.
				DisconnectTailPointsFromBendPoints(*it_con);

				// Delete connection
				delete *it_con;					 // delete actual connection pointed to by it_con
				it_con = con_list.erase(it_con); // delete element at offset it_con in list (that held the connection)
			}
			else
			{
				it_con++;
			}
		}// end for it_con		

		// -- ITERATE THROUGH ANNOTATIONS
		it_an = annotation_list.begin();
		while(it_an != annotation_list.end())
		{
			// Get annotation position
			an_posn = (*it_an)->GetPosition();

			// Determine if item lies within rubber band
			intersected = DetermineCurrentAndIntersectRects(temp_tracker, an_posn, delta);
			if(intersected) 
			{
				// Delete annotation
				delete *it_an;							// delete actual annotation pointed to by it_an
				it_an = annotation_list.erase(it_an);	// delete element at offset it_an in list
			}
			else // only increment the iterator if there were no intersection
			{
				it_an++;
			}
			
		}// end for it_an

		// Reset member vars.
		m_iRubberBandCreated = 0;				// end the rubber band state
		SetKeyFlagTrack(0);						// reset the key-based track flag since tracking aborted	

		// Set flags	
		SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
		UpdateAllViews (NULL); 		// indicate that sys. should redraw.

	}// end if m_iRubberBandCreated
}

void CDiagramEngDoc::OnSetItemProperties() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	int item_clicked = 0;

	// Set properties for blocks
	item_clicked = DoubleLeftClickBlock(m_ptContextMenu);

	// Set properties for block ports
	if(item_clicked == 0)
	{
		item_clicked = SetBlockPortProperties(m_ptContextMenu);
	}

	// Set properties for connections and connection bend points
	if(item_clicked == 0)
	{
		item_clicked = SetConnectionProperties(m_ptContextMenu);
	}

	// Set properties for the Numerical Solver
	if(item_clicked == 0)
	{
		OnSimNumericalSolver();
	}

	// DiagramEng (end)
}

int CDiagramEngDoc::SetConnectionProperties(CPoint point)
{
	int click_flag = 0;						// (0) item not clicked, (1) item clicked
	double disc_r = 0.1*m_dDeltaLength;		// disc or radius r
	double dist;							// dist bw mouse click pt and connection head pt. or bend pt.
	CPoint bend_pt;							// connection bend pt.
	CPoint head_pt;							// connection head pt.
	list<CConnection*>::iterator it_con;	// connection iterator
	list<CConnection*> con_list = GetSystemModel().GetConnectionList();
	list<CPoint>::iterator it_pt;


	// ITERATE THROUGH CONNECTION LIST 

	for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
	{
		// CHECK IF CLICK ON CONNECTION HEAD POINT
		
		// Get head point of connection
		head_pt = (*it_con)->GetConnectionPointHead();

		// Determine Euclidean dist bw point and head pt
		dist = sqrt(pow(head_pt.x - point.x,2) + pow(head_pt.y - point.y,2));
		
		// If the head pt is clicked
		if(dist <= disc_r)
		{
			//AfxMessageBox("\n CDiagramEngDoc::SetConnectionProperties(): connection\n", MB_OK, 0);
			click_flag = 1;
			break;
		}

		// CHECK IF CLICK ON CONNECTION BEND POINT

		// Get connection bend pts.
		list<CPoint> &bend_pts_list = (*it_con)->GetConnectionBendPointsList();

		// Iterate through all bend pts. for this conection
		for(it_pt = bend_pts_list.begin(); it_pt != bend_pts_list.end(); it_pt++)
		{
			bend_pt = *it_pt;

			// Determine Euclidean dist bw mouse point and connection bend pt.
			dist = sqrt(pow(bend_pt.x - point.x,2) + pow(bend_pt.y - point.y,2));
			
			// If the bend pt is clicked
			if(dist <= disc_r)
			{
				//AfxMessageBox("\n CDiagramEngDoc::SetConnectionProperties(): bend_pt\n", MB_OK, 0);
				click_flag = 1;
				break;
			}
		}
	}
		
	return click_flag;
}

int CDiagramEngDoc::SetBlockPortProperties(CPoint point)
{
	int click_flag = 0;				// if port upon block clicked, flag = 1, else flag = 0.
	double blk_height;				// block height
	double blk_width;				// block width
	double dist;					// Euclidean dist bw mouse click and CM of block.
	double hyp;						// hypotenuse of subtriangle within equilateral triangle, used as a block-circumscribing disc radius
	CPoint blk_posn;				// block posn.
	EBlockShape e_blk_shape;		// block shape = e_ellipse, e_rectangle, e_triangle.
	list<CBlock*>::iterator it_blk;	// iterator

	// Get a copy of the blk_list in the system model
	list<CBlock*> &blk_list = GetSystemModel().GetBlockList();	// MUST BE A REFERENCE!

	// Iterate through the list to find which block was selected.
	for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
	{
		// Get block properties
		blk_posn = (*it_blk)->GetBlockPosition();
		blk_width = (*it_blk)->GetBlockShape().GetBlockWidth();
		blk_height = (*it_blk)->GetBlockShape().GetBlockHeight();
		e_blk_shape = (*it_blk)->GetBlockShape().GetBlockShapeType();

		if(e_blk_shape == e_ellipse)
		{
			// Get Euclidean distance between point and block
			dist = sqrt(pow((blk_posn.x - point.x),2) + pow((blk_posn.y - point.y),2));

			if(dist <= blk_width*0.5)
			{
				//AfxMessageBox("\n CDiagramEngDoc::SetBlockPortProperties()\n", MB_OK, 0);

				// Set port properties for this partic. block. if so port_click_flag = 1
				click_flag = (*it_blk)->SetPortProperties(point);
	
				break;
			}
		}
		else if(e_blk_shape == e_rectangle)
		{
			if((abs(point.x - blk_posn.x) <= blk_width*0.5) && (abs(point.y - blk_posn.y) <= blk_height*0.5))
			{
				//AfxMessageBox("\n CDiagramEngDoc::SetBlockPortProperties()\n", MB_OK, 0);

				// Set port properties for this partic. block. if so port_click_flag = 1
				click_flag = (*it_blk)->SetPortProperties(point);

				break;
			}	
		}
		else if(e_blk_shape == e_triangle)
		{
			// hypotenuse of subtriangle within equilateral triangle, used as a block-circumscribing disc radius
			hyp = blk_width/sqrt(3);	
			
			dist = sqrt(pow((blk_posn.x - point.x),2) + pow((blk_posn.y - point.y),2));

			if(dist <= hyp)
			{
				//AfxMessageBox("\n CDiagramEngDoc::SetBlockPortProperties()\n", MB_OK, 0);

				// Set port properties for this partic. block. if so port_click_flag = 1
				click_flag = (*it_blk)->SetPortProperties(point);

				break;
			}
		}
	}

	// Return block click status
	return click_flag;
}

int CDiagramEngDoc::DeletePort()
{
	int port_flag = 0;					// flag indicating whether port properties were changed
	double blk_width;					// block width
	double sign_width;					// port sign width
	CPoint point = m_ptContextMenu;		// point at which context menu was activated
	CPoint sign_posn;					// sign position
	CString sMsg;						// main msg string
	UINT nType = MB_OK;					// style of msg. box
	UINT nIDhelp = 0;					// help context ID for the msg.
	list<CBlock*>::iterator it_blk;		// block iterator
	list<CConnection*>::iterator it_con;// connection iterator
	vector<CPort*>::iterator it_port;	// port iterator


	// Get connection list
	list<CConnection*> &con_list = GetSystemModel().GetConnectionList();

	// Get a copy of the blk_list in the system model
	list<CBlock*> &blk_list = GetSystemModel().GetBlockList();

	// -- ITERATE THROUGH BLOCK LIST
	for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
	{
		// Get block and sign properties
		blk_width = (*it_blk)->GetBlockShape().GetBlockWidth();
		sign_width = 0.1*blk_width; 

		// Get vec of input ports
		vector<CPort*> &vec_input_ports = (*it_blk)->GetVectorOfInputPorts();	

		// -- DELETE PORTS FROM BLOCKS WITH > 2 INPUT PORTS ONLY, i.e. SUM and DIVIDE blocks
		if(vec_input_ports.size() > 2)
		{
			// -- ITERATE THROUGH INPUT PORTS
			it_port = vec_input_ports.begin();
			while(it_port != vec_input_ports.end())
			{
				sign_posn = (*it_port)->GetPortSignPosition();

				// If the point is upon the current port sign
				if((abs(point.x - sign_posn.x) <= sign_width*0.5) && (abs(point.y - sign_posn.y) <= sign_width*0.5))
				{
					// Iterate through connection list
					for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
					{
						// If a connection is attached to this port
						if((*it_con)->GetRefToPort() == *it_port)
						{
							// Disconnect the connection object from the port
							DisconnectEndPointFromPort(*it_port);
						}		
					}

					// Delete port
					delete *it_port;								// delete actual port pointed to by it_port
					it_port = vec_input_ports.erase(it_port); 		// delete element at offset it_port in vec (that held the port)

					// Delete only one port per block then exit while loop
					port_flag = 1;
					break;
				}
				else
				{
					it_port++;
				}
			}
			
		}// end it_port
	
		// Check port_flag to exit it_blk loop
		if(port_flag == 1)
		{
			break;
		}

		// -- VECTOR OF OUTPUT PORTS
/*		vector<CPort*> &vec_output_ports = (*it_blk)->GetVectorOfOutputPorts();	// vector of output ports

		// Iterate through vec of input ports
		it_port = vec_output_ports.begin();
		while(it_port != vec_output_ports.end())
		{
			sign_posn = (*it_port)->GetPortSignPosition();

			// If the point is upon the current port sign
			if((abs(point.x - sign_posn.x) <= sign_width*0.5) && (abs(point.y - sign_posn.y) <= sign_width*0.5))
			{
				// Iterate through connection list
				for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
				{
					// If a connection is attached to this port
					if((*it_con)->GetRefFromPort() == *it_port)
					{
						// Disconnect the connection object from the port
						DisconnectEndPointFromPort(*it_port);
					}		
				}

				// Delete port
				delete *it_port;								// delete actual port pointed to by it_port
				it_port = vec_output_ports.erase(it_port); 		// delete element at offset it_port in vec (that held the port)

				// Delete only one port per block then exit while loop.
				port_flag = 1;
				break;
			}
			else
			{
				it_port++;
			}
			
		}// end it_port
*/
		// Check port_flag to exit it_blk loop
		if(port_flag == 1)
		{
			break;
		}
	
	}// end it_blk

	return port_flag;
}

void CDiagramEngDoc::OnFineMoveItem() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)

	// Set member var. to indicate movement initiated
	m_iFineMoveFlag = 1;

	// DiagramEng (end)
}

void CDiagramEngDoc::SetFineMoveFlag(int fine_move_flag)
{
	// Set the member var.
	m_iFineMoveFlag = fine_move_flag;
}

int CDiagramEngDoc::GetFineMoveFlag() const
{
	// Return the member var.
	return m_iFineMoveFlag;
}

void CDiagramEngDoc::FineMoveItem(int key_flag)
{
	int item_moved = 0;		// flag indicating entity movement

	// Check whether block was selected and moved
	item_moved = FineMoveBlock(key_flag);
	if(item_moved)
	{
		return;
	}	

	// Check whether connection bend point was selected and moved
	item_moved = FineMoveConnectionBendPoint(key_flag);
	if(item_moved)
	{
		return;
	}
	
	// Check whether annotation was selected and moved
	item_moved = FineMoveAnnotation(key_flag);
	if(item_moved)
	{
		return;
	}

	// End fine movement action by switching m_iFineMoveFlag = 0
	if(item_moved == 0)
	{
		SetFineMoveFlag(item_moved);	
	}
}

int CDiagramEngDoc::FineMoveBlock(int key_flag)
{
	int delta_x = 1;		// incr. in x direc. 
	int delta_y = 1;		// incr. in y direc. 
	int move_flag = 0;		// flag indicating entity movement			
	double	dist;			// Euclidean dist bw. block posn and point posn.
	double	width;			// block width
	CPoint	blk_posn;		// block posn.
	CPoint	point;			// local point var.
	CString sMsg;			// main msg string
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.
	list<CBlock*>::iterator it_blk;		// block iterator
	static CBlock *ptr_to_blk = NULL;	// ptr-to-block to hold the chosen block's address.		
	

	// Get the point at which the context menu was invoked
	point = m_ptContextMenu;	// init a local copy.

	// Print msg.
	//sMsg.Format("\n CDiagramEngDoc::FineMoveBlock(), point (x,y) = (%d,%d)\n", point.x, point.y);
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// -- IF THIS IS THE FIRST ENTRY INTO THIS FN THEN RECORD THE ADDRESS OF THE SELECTED BLOCK (also move the block)
	if(GetFineMoveFlag() == 1)
	{
		// Get a copy of the blk_list in the system model
		list<CBlock*> &blk_list = GetSystemModel().GetBlockList();

		// -- ITERATE THROUGH THE BLOCK LIST
		for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
		{
			move_flag = 0;
			blk_posn = (*it_blk)->GetBlockPosition();
			width = (*it_blk)->GetBlockShape().GetBlockWidth();
			dist = sqrt(pow((blk_posn.x - point.x),2) + pow((blk_posn.y - point.y),2));
			
			// -- IF USER CLICKED ON A BLOCK
			if(dist <= 0.5*width*0.5)
			{	

				// -- FILTER THE KEY/DIRECTION (screen (0,0) top left cnr., +ve x to right, +ve y downward
				if(key_flag == 37) 							// LEFT ARROW
				{	
					// Update block posn.
					blk_posn.x = blk_posn.x - delta_x;		
					(*it_blk)->SetBlockPosition(blk_posn);
					ptr_to_blk = *it_blk;					// record the address of the block for subsequent movt.
					move_flag = 1;
				}
				else if(key_flag == 38)						// UP ARROW
				{
					// Update block posn.
					blk_posn.y = blk_posn.y - delta_y;
					(*it_blk)->SetBlockPosition(blk_posn);
					ptr_to_blk = *it_blk;					// record the address of the block for subsequent movt.
					move_flag = 1;
				}
				else if(key_flag == 39)						// RIGHT ARROW
				{
					// Update block posn.
					blk_posn.x = blk_posn.x + delta_x;
					(*it_blk)->SetBlockPosition(blk_posn);
					ptr_to_blk = *it_blk;					// record the address of the block for subsequent movt.
					move_flag = 1;
				}
				else if(key_flag == 40)						// DOWN ARROW
				{
					// Update block posn.
					blk_posn.y = blk_posn.y + delta_y;
					(*it_blk)->SetBlockPosition(blk_posn);
					ptr_to_blk = *it_blk;					// record the address of the block for subsequent movt.
					move_flag = 1;
				}
		
				if(move_flag == 1)
				{
					// Set flag
					m_iFineMoveFlag = 2;

					// Mark the document as changed
					UpdateAllViews(NULL);		// this fn calls OnDraw to redraw the system model.
					SetModifiedFlag(TRUE);		// prompt the user to save 
					break;						// break out of for loop
				}
			}// end if dist
		}// end it_blk
	}
	else if(m_iFineMoveFlag == 2) // -- IF THIS IS NOT THE FIRST TIME INTO THE FN, THEN MOVE THE BLOCK USING IT'S ADDRESS
	{
		// -- FILTER THE KEY/DIRECTION (screen (0,0) top left cnr., +ve x to right, +ve y downward
		if(key_flag == 37) 							// LEFT ARROW
		{	
			// Update block posn.
			blk_posn = ptr_to_blk->GetBlockPosition();
			blk_posn.x = blk_posn.x - delta_x;		
			ptr_to_blk->SetBlockPosition(blk_posn);
			move_flag = 1;
		}
		else if(key_flag == 38)						// UP ARROW
		{
			// Update block posn.
			blk_posn = ptr_to_blk->GetBlockPosition();
			blk_posn.y = blk_posn.y - delta_y;
			ptr_to_blk->SetBlockPosition(blk_posn);
			move_flag = 1;
		}
		else if(key_flag == 39)						// RIGHT ARROW
		{
			// Update block posn.
			blk_posn = ptr_to_blk->GetBlockPosition();
			blk_posn.x = blk_posn.x + delta_x;
			ptr_to_blk->SetBlockPosition(blk_posn);
			move_flag = 1;
		}
		else if(key_flag == 40)						// DOWN ARROW
		{
			// Update block posn.
			blk_posn = ptr_to_blk->GetBlockPosition();
			blk_posn.y = blk_posn.y + delta_y;
			ptr_to_blk->SetBlockPosition(blk_posn);
			move_flag = 1;
		}

		if(move_flag == 1)
		{
			// Mark the document as changed
			UpdateAllViews(NULL);		// this fn calls OnDraw to redraw the system model.
			SetModifiedFlag(TRUE);		// prompt the user to save 
		}
	}

	return move_flag;
}

int CDiagramEngDoc::FineMoveConnectionBendPoint(int key_flag)
{
	int delta_x = 1;						// incr. in x direc. 
	int delta_y = 1;						// incr. in y direc. 
	int move_flag = 0;						// flag indicating entity movement			
	double disc_r = 0.25*m_dDeltaLength; 	// disc of radius r about center of connection bend point.
	double	dist;							// Euclidean dist bw. connection bend point posn and point posn.
	CPoint	bend_pt;						// bend point posn.
	CPoint	point;							// local point var.
	CString sMsg;							// main msg string
	UINT nType = MB_OK;						// style of msg. box
	UINT nIDhelp = 0;						// help context ID for the msg.
	list<CConnection*>::iterator it_con;	// connection iterator
	list<CPoint>::iterator it_pt;			// point iterator
	static CPoint *ptr_to_bend_pt = NULL;	// ptr-to-bend pt to hold the chosen bend point's address.		

	// Get the system model
	CSystemModel &sys_model = GetSystemModel();

	// Get the point at which the context menu was invoked
	point = m_ptContextMenu;	// init a local copy.

	// Print msg.
	//sMsg.Format("\n CDiagramEngDoc::FineMoveConnectionBendPoint(), point (x,y) = (%d,%d)\n", point.x, point.y);
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// -- IF THIS IS THE FIRST ENTRY INTO THIS FN THEN RECORD THE ADDRESS OF THE SELECTED BEND PT (also move the bend pt.)
	if(GetFineMoveFlag() == 1)
	{
		// Get a copy of the con_list in the system model
		list<CConnection*> con_list = sys_model.GetConnectionList();

		// -- ITERATE THROUGH THE BLOCK LIST
		for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
		{
			// Get the connection bend pts. list
			list<CPoint> &bend_pts_list = (*it_con)->GetConnectionBendPointsList();

			// -- ITERATE THROUGH ALL BEND POINTS for this conection
			for(it_pt = bend_pts_list.begin(); it_pt != bend_pts_list.end(); it_pt++)
			{
				bend_pt = *it_pt;

				// Determine Euclidean dist bw mouse point and connection bend pt.
				dist = sqrt(pow(bend_pt.x - point.x,2) + pow(bend_pt.y - point.y,2));
				
				// -- IF USER CLICKED ON A BEND POINT
				if(dist <= disc_r)
				{
					// -- FILTER THE KEY/DIRECTION (screen (0,0) top left cnr., +ve x to right, +ve y downward
					if(key_flag == 37)					// LEFT ARROW
					{	
						// Update bend point posn.
						bend_pt.x = bend_pt.x - delta_x;	
						*it_pt = bend_pt;
						ptr_to_bend_pt = &(*it_pt);		// record the address of the bend pt for subsequent movt.
						sys_model.UpdateConnectionPointTailToBendPoint(&(*it_pt));	// update any tail pt connected to bend pt.
						move_flag = 1;
					}
					else if(key_flag == 38)				// UP ARROW
					{
						// Update bend point posn.
						bend_pt.y = bend_pt.y - delta_y;
						*it_pt = bend_pt;
						ptr_to_bend_pt = &(*it_pt);		// record the address of the bend pt for subsequent movt.
						sys_model.UpdateConnectionPointTailToBendPoint(&(*it_pt));	// update any tail pt connected to bend pt.
						move_flag = 1;
					}
					else if(key_flag == 39)				// RIGHT ARROW
					{
						// Update bend point posn.
						bend_pt.x = bend_pt.x + delta_x;
						*it_pt = bend_pt;
						ptr_to_bend_pt = &(*it_pt);		// record the address of the bend pt for subsequent movt.
						sys_model.UpdateConnectionPointTailToBendPoint(&(*it_pt));	// update any tail pt connected to bend pt.
						move_flag = 1;
					}
					else if(key_flag == 40)				// DOWN ARROW
					{
						// Update bend point posn.
						bend_pt.y = bend_pt.y + delta_y;
						*it_pt = bend_pt;
						ptr_to_bend_pt = &(*it_pt);		// record the address of the bend pt for subsequent movt.
						sys_model.UpdateConnectionPointTailToBendPoint(&(*it_pt));	// update any tail pt connected to bend pt.
						move_flag = 1;
					}
			
					if(move_flag == 1)
					{
						// Set flag
						m_iFineMoveFlag = 3;

						// Mark the document as changed
						UpdateAllViews(NULL);		// this fn calls OnDraw to redraw the system model.
						SetModifiedFlag(TRUE);		// prompt the user to save 
						break;						// break out of for loop
					}
				}// end if dist
			}// end it_pt
		}// end it_con
	}
	else if(m_iFineMoveFlag == 3) // -- IF THIS IS NOT THE FIRST TIME INTO THE FN, THEN MOVE THE BEND POINT USING IT'S ADDRESS
	{
		// -- FILTER THE KEY/DIRECTION (screen (0,0) top left cnr., +ve x to right, +ve y downward
		if(key_flag == 37)											// LEFT ARROW
		{	
			// Update CPoint bend point posn by dereferencing the ptr.
			(*ptr_to_bend_pt).x = (*ptr_to_bend_pt).x - delta_x;	
			sys_model.UpdateConnectionPointTailToBendPoint(ptr_to_bend_pt);	// update any tail pt connected to bend pt.
			move_flag = 1;
		}
		else if(key_flag == 38)										// UP ARROW
		{
			// Update CPoint bend point posn. by dereferencing the ptr.
			(*ptr_to_bend_pt).y = (*ptr_to_bend_pt).y - delta_y;	
			sys_model.UpdateConnectionPointTailToBendPoint(ptr_to_bend_pt);	// update any tail pt connected to bend pt.
			move_flag = 1;
		}
		else if(key_flag == 39)										// RIGHT ARROW
		{	
			// Update CPoint bend point posn. by dereferencing the ptr.
			(*ptr_to_bend_pt).x = (*ptr_to_bend_pt).x + delta_x;	
			sys_model.UpdateConnectionPointTailToBendPoint(ptr_to_bend_pt);	// update any tail pt connected to bend pt.
			move_flag = 1;
		}
		else if(key_flag == 40)										// DOWN ARROW
		{
			// Update CPoint bend point posn. by dereferencing the ptr.
			(*ptr_to_bend_pt).y = (*ptr_to_bend_pt).y + delta_y;	
			sys_model.UpdateConnectionPointTailToBendPoint(ptr_to_bend_pt);	// update any tail pt connected to bend pt.
			move_flag = 1;
		}

		if(move_flag == 1)
		{
			// Mark the document as changed
			UpdateAllViews(NULL);		// this fn calls OnDraw to redraw the system model.
			SetModifiedFlag(TRUE);		// prompt the user to save 
		}
	}

	return move_flag;
}

void CDiagramEngDoc::OnReverseBlockDirection() 
{
	// TODO: Add your command handler code here

	// DiagramEng (start)
	double	dist;			// Euclidean dist bw. block posn and point posn.
	double	width;			// block width
	CPoint	blk_posn;		// block posn.
	CPoint	point;			// local point var.
	CString sMsg;			// main msg string
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.
	list<CBlock*>::iterator it_blk;			// iterator
	
	// Get the point at which the context menu was invoked
	point = m_ptContextMenu;	// init a local copy.

	// Print msg.
	//sMsg.Format("\n CDiagramEngDoc::OnReverseBlockDirection(), point (x,y) = (%d,%d)\n", point.x, point.y);
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// Get a copy of the blk_list in the system model
	list<CBlock*> &blk_list = GetSystemModel().GetBlockList();	// MUST BE A REFERENCE!

	// -- ITERATE THROUGH THE BLOCK LIST TO FIND SELECTED BLOCK
	for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
	{
		blk_posn = (*it_blk)->GetBlockPosition();
		width = (*it_blk)->GetBlockShape().GetBlockWidth();
		dist = sqrt(pow((blk_posn.x - point.x),2) + pow((blk_posn.y - point.y),2));

		if(dist <= 0.5*width*0.5)
		{
			(*it_blk)->ReverseBlockDirection();	// reverse block and all it's properties
			SetModifiedFlag(TRUE);				// set the doc. as modified to prompt the user to save
			UpdateAllViews(NULL);				// redraw the doc if block direction changed
			break;								// exit the for loop 
		}

	}// end it_blk

	// DiagramEng (end)
}

// -- CONVERT STRING TO DOUBLE FUNCTIONS

double **ConvertStringToDouble(CString string, int &nrows, int &ncols)
{
	int i;							// row i
	int cnt = 0;					// counter used
	int dim_flag = 0;				// flag to control determining of data dims: (0) strpbrk(), (1) strtok()
	int max_row_length = 0;			// max row length
	int str_length = 0;				// length of input string
	int valid = 0;					// validity of consistency of number of cols.
	char *ptr_char = NULL;			// ptr-to-char holds base address of first char of a poss gp. of chars.
	char *input_str = NULL;			// input string declared as an array of chars
	char **str_matrix = NULL;		// str matrix of size nrows, no. of rows, by str_length no. of char. cols
	char *stripped_str = NULL;		// input_str after preceding and trailing unwanted chars removed
	char *stripped_str_copy = NULL;	// copy of stripped string
	double **d_matrix = NULL;		// double matrix				
	

	// GET THE INITIAL LENGTH OF THE STRING using the GetLength() member function
	// WARNING! THIS DOES NOT INCLUDE THE NULL TERMINATOR: HENCE ADD ONE TO THE LENGTH (see below).
	str_length = string.GetLength();

	// -- MEMORY ALLOC
	input_str = new char[str_length+1];						// add one to the str_length to cater for the null terminator.

	// CONVERT the original CString into a CHAR ARRAY typecasting CString to a long pointer-to-constant character string.
	strcpy(input_str, LPCTSTR(string));	

	// STRIP UNNEC CHARS FROM STRING
	stripped_str = StripInputString(input_str);				// memory for stripped str allocated within StripInputString()
	
	if(stripped_str == NULL)
	{
		delete [] input_str;								// delete the input_str that had memory just allocated to it 
		return NULL;
	}

	// -- MEMORY ALLOC (Delete memory for stripped_str along with stripped_str_copy.)
	stripped_str_copy = new char[strlen(stripped_str)+1];	// add one to the str_length to cater for the null terminator.

	// DETERMINE INPUT DATA DIMENSIONS
	// Need to copy the stripped string since strtok() is called inside DetermineInputDataDimensions() and this modifies
	// the input string: hence pass in stripped_str_copy instead of stripped_str.
	strcpy(stripped_str_copy, stripped_str);	
	if(dim_flag == 0)
	{
		valid = DetermineDataDimsByStrpbrk(stripped_str_copy, nrows, ncols, max_row_length);
	}
	else
	{
		valid = DetermineDataDimsByStrtok(stripped_str_copy, nrows, ncols, max_row_length);
	}

	// -- BUILD THE STRING MATRIX 
	// -- MEMORY ALLOC
	str_matrix = new char *[nrows];							// input string matrix
	for(i=0; i<nrows; i++)
	{
		str_matrix[i] = new char[max_row_length + 1];		// allocate an array of chars, return & to str_Matrix[i]
	}

	// Get the first TOKEN/SET-OF-CHARS/ROW-OF-DATA of stripped_str, up until the first delimiter/SEPARATOR ";"
	// The address of the first character of the token is returned.
	// The character immediately following the token is overwritten with the NULL character.	
	ptr_char = strtok(stripped_str, ";");


	// Record the rows
	while(ptr_char != NULL)		// while not at the end of the original string (denoted by NULL)
	{	
		strcpy(str_matrix[cnt], ptr_char); 

		// -- GET THE NEXT TOKEN/SET-OF-CHARS/ROW-OF-DATA 
		// NOTE! - "Subsequent calls with the first string = NULL, return the base address of a string supplied 
		// by the system that contains the NEXT token." [Kelley & Pohl]
		// Hence, find the next set of chars from the prev end pt (NULL) as a result of using strtok(),
		// to the next delimiter ";", i.e. get the next row of characters, where the base address of the first char,
		// is returned to ptr_char.
		ptr_char = strtok(NULL, ";");
	
		cnt++;	// increment the row cnt.
	}


	// EXTRACT THE STRING DATA OUT OF THE STRING MATRIX AND CONVERT IT TO A DOUBLE MATRIX
	if(valid == 0)			// inconsistent no. of cols per row
	{
		d_matrix = NULL;
	}
	else if(valid == 1)		// consistent no. of cols per row
	{
		d_matrix = ConvertStringMatrixToDoubleMatrix(str_matrix, nrows, ncols);
	}

	// MEMORY DELETE
	// Note: memory for stripped_str is only allocated when return value of StripInputString() is not NULL.
	delete [] input_str;			// original input str
	delete [] stripped_str;			// stripped_str memory allocated in StripInputString()
	delete [] stripped_str_copy;	// copy of stripped string
	
	for(i=0; i<nrows; i++)
	{
		delete [] str_matrix[i]; 
	}
	delete [] str_matrix;
	
	if(valid == 0)
	{
		nrows = 0;
		ncols = 0;
	}

	return d_matrix;
}

char *StripInputString(char *input_str)
{
	int length = 0;				// length of stripped string
	int number_present = 0;		// flag indicating whether a number is present in the string
	char *ptr_begin = NULL;		// the base address of the new char array beginning at a number of decimal point
	char *ptr_char = NULL;		// a ptr to the next number in the array
	char *ptr_end = NULL;		// a ptr to the position just after the last number in the array
	char *ptr_str = input_str;	// a ptr to the str being searched incrementally one character at a time
	char *stripped_str = NULL;	// the new string with unnec. chars removed.
	CString sMsg;				// main msg string
	CString sMsgTemp;			// temp msg string
	UINT nType = MB_OK;			// style of msg. box
	UINT nIDhelp = 0;			// help context ID for the msg.


	// GET THE NEW CHAR ARRAY BEGINNING AT THE FIRST MINUS SIGN, NUMBER, OR DECIMAL POINT
	ptr_begin = strpbrk(input_str, "-0123456789.");

	// Check that the string contains at least a number or a decimal point: if not return NULL.
	if(ptr_begin == NULL)
	{
		sMsgTemp.Format("\n StripInputString()\n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n Incomplete data!\n Please enter numerical data.\n");
		sMsg += sMsgTemp;
		//AfxMessageBox(sMsg, nType, nIDhelp);
		return NULL;
	}

	// GET THE NEW CHAR ARRAY BEGINNING JUST AFTER THE LAST NUMBER
	ptr_char = ptr_str;								// initialize the ptr_char to ptr_str.
	while(ptr_char != NULL)
	{
		ptr_char = strpbrk(ptr_str, "0123456789");	// get the next number

		// Check for presence of a number
		if(ptr_char != NULL)
		{
			ptr_end = ptr_char;
			number_present = 1;
		}
		else if(ptr_char == NULL)
		{
			ptr_end = ptr_end + 1;
		}

		// Increment the ptr to the next char. of the string
		ptr_str++;
	}

	// It the string does not contain a number display msg. and return NULL.
	if(number_present == 0)
	{
		sMsgTemp.Format("\n StripInputString()\n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n Incomplete data!\n Please enter numerical data.\n");
		sMsg += sMsgTemp;
		//AfxMessageBox(sMsg, nType, nIDhelp);
		return NULL;
	}
	
	// BUILD THE STRIPPED INPUT_STR 
	length = strlen(ptr_begin) - strlen(ptr_end);				// get distance bw. ptr_begin and ptr_end

	// -- MEMORY ALLOC
	stripped_str = new char[length + 1];
	
	// Copy ptr_begin into the new stripped string.
	strncpy(stripped_str, ptr_begin, length);	// ptr_begin is larger than stripped_str, hence no NULL is placed at the end
	stripped_str[length] = '\0';				// manually add the NULL in the last entry at offset length.

	// -- MEMORY DELETE
	// Note: deletion of stripped_str should take place in the calling environ.

	return stripped_str;
}

int DetermineDataDimsByStrpbrk(char *in_str, int &nrows, int &ncols, int &max_row_length)
{
	int ncols_ref = 0;		// reference no. of cols.
	int valid = 0;			// validity of consistency of no. of cols per row.
	char *pch1 = NULL;		// ptr to char
	char *pch2 = NULL;		// ptr to char
	char *ptr_row = NULL;	// ptr to the start of each row
	char *ptr_str = NULL;	// string being search for no. of cols, i.e. the row being searched.

	// Get the first row
	ptr_row = strtok(in_str, ";");

	// Init. vars.
	max_row_length = 0;
	nrows = 0;

	// -- ITERATE OVER ALL ROWS
	while(ptr_row != NULL)
	{
		ncols = 1;

		// -- ITERATE OVER ALL COLS
		ptr_str = ptr_row;

		// While the address of the next no. or decimal pt. is not NULL
		while((pch1 = strpbrk(ptr_str, "0123456789.")) != NULL)
		{
			// Advance the ptr_str by one.
			ptr_str = pch1 + 1;

			// Get the address of the next number or decimal pt.
			pch2 = strpbrk(ptr_str, "0123456789.");

			// If the address of the next no. is greater than one space away, then white space intervened.
			if((pch2 != NULL) && (pch2 > (pch1 + 1)))
			{
				ncols++;
			}
		}

		// Increment nrows
		nrows++;

		// Check the consistency of ncols.	
		if(nrows == 1)
		{
			ncols_ref = ncols;
			valid = 1;
		}
		else if(ncols != ncols_ref)
		{
			AfxMessageBox("\n DetermineDataDimsByStrpbrk(): Inconsistent number of columns\n", MB_OK, 0);
			valid = 0;
		}

		// Record the max row length for memory alloc purposes
		if(strlen(ptr_row) >= max_row_length)
		{
			max_row_length = strlen(ptr_row);
		}

		// Get the new row
		ptr_row = strtok(NULL, ";");
	}

	return valid;
}

int DetermineDataDimsByStrtok(char *in_str, int &nrows, int &ncols, int &max_row_length)
{
	int ncols_ref = 0;				// reference no. of cols.
	int row_length;					// length of row
	int total_length = 0;			// accumulated sum of row lengths
	int valid = 0;					// validity of consistency of no. of cols per row.
	char *ptr_row = NULL;			// ptr holding the base address of the row
	char *ptr_element = NULL;		// ptr holding the base address of the element within a row
	char *ptr_str_temp_copy = NULL;	// ptr to a temp string
	char *str_temp = NULL;			// temp char array
	char *str_temp_copy = NULL;		// copy of str_temp used since str_temp is altered by strtok()

	// -- MEMORY ALLOC
	str_temp = new char[strlen(in_str) + 1];		// allocate the length of the string plus one to hold the '\0' char.
	str_temp_copy = new char[strlen(in_str) + 1];	// allocate the length of the string plus one to hold the '\0' char.

	// Copy in_str into str_temp and str_temp_copy
	strcpy(str_temp, in_str);
	strcpy(str_temp_copy, in_str);

	// Assign the base address of str_temp_copy to ptr_str_temp_copy.
	ptr_str_temp_copy = str_temp_copy;			

	ptr_row = strtok(str_temp, ";");			// get the first row separated by ";"

	// ITERATE OVER ALL THE ROWS	
	nrows = 1;
	while(ptr_row != NULL)
	{
		row_length = strlen(ptr_row);
		total_length += row_length + 1;			// one is added here to cater for the ";"

		ncols = 1;

		ptr_element = strtok(ptr_row, " ");		// get element separated by " " on the current row

		while(ptr_element != NULL)
		{
			ptr_element = strtok(NULL, " ");	// get the next element separated by " " on the current row

			if(ptr_element != NULL)
			{
				ncols++;
			}
		}

		// Check the consistency of ncols.	
		if(nrows == 1)
		{
			ncols_ref = ncols;
			valid = 1;
		}
		else if(ncols != ncols_ref)
		{
			//AfxMessageBox("\n DetermineDataDimsByStrtok(): Inconsistent number of columns\n", MB_OK, 0);
			valid = 0;
		}

		// Record the max row length for memory alloc purposes
		if(row_length >= max_row_length)
		{
			max_row_length = row_length;
		}

		// Advance the position of the ptr to the beginning of the next row
		ptr_str_temp_copy = ptr_str_temp_copy + row_length + 1;		// one is added to cater for the ";"

		// Exit if looping has traversed to the end of the original string
		if(total_length >= strlen(in_str))
		{
			break;
		}
		
		// Copy what is pointed to by ptr_str_temp_copy, i.e. the next row onwards, into str_temp.
		strcpy(str_temp, ptr_str_temp_copy);

		// Obtain the next row separated by ";"
		ptr_row = strtok(str_temp, ";");

		if(ptr_row != NULL)
		{
			nrows++;
		}
	}

	// MEMORY DELETE
	delete [] str_temp;
	delete [] str_temp_copy;

	return valid;
}

double **ConvertStringMatrixToDoubleMatrix(char **str_matrix, int nrows, int ncols)
{
	int i;						// row i
	int j;						// col j
	int valid = 1;				// confirm the no. of cols of data per row. (assume valid to begin with)
	char *ptr_char = NULL;		// ptr-to-char holds base address of first char of a poss gp. of chars.
	double **d_matrix = NULL;	// input matrix of chars converted to doubles
	
	// MEMORY ALLOCATION
	d_matrix = new double *[nrows];
	for(i=0; i<nrows; i++)
	{
		d_matrix[i] = new double[ncols];			// allocate an array of doubles, return & to matrix[i]	}
	}

	// Iterate over the number of rows.
	for(i=0; i<nrows; i++)
	{
		ptr_char = strtok(str_matrix[i], " ");		// extract the character of the current row separated by " " (space).

		// Iterate across the cols for this partic. row.
		j = 0;
		while(ptr_char != NULL)						// while not at the end of the current row of chars.
		{
			d_matrix[i][j]  = atof(ptr_char);		// convert the char to a double and place in the double array. 

			ptr_char = strtok(NULL, " ");			// extract the subsequent char of the current row separated by " " (space).	

			j++;									// increment the col cnt
		}

		if(j != ncols)	// if the no. of times the loop was iterated is not equal to the number of cols, print error msg.
		{
			AfxMessageBox("\n ConvertStringMatrixToDoubleMatrix(): Inconsistent number of columns\n", MB_OK, 0);
			valid = 0;
		}
	}

	return d_matrix;
}

int ConvertDoubleMatrixToString(CString &str, double **matrix, int nrows, int ncols)
{ 
	int i;
	int j;
	CString strTemp;

	//AfxMessageBox("\n ConvertDoubleMatrixToString()\n", MB_OK, 0);

	// Convert double matrix to CString form

	// Init the incoming string
	str.Format("");			

	// Matrix content
	for(i=0; i<nrows; i++)
	{
		for(j=0; j<ncols; j++)
		{
			strTemp.Format(" %.10lf", matrix[i][j]);	// convert to 10 decimal places
			str += strTemp;
		}

		// NOTE: NO '/n' CHARACTER IS PERMITTED HERE, ONLY THE ";" END OF LINE, DEMARCATION.
		if(i < (nrows - 1))
		{
			strTemp.Format(";");
			str += strTemp;
		}
	}
	
	return 0;
}

int ConvertDoubleVectorToString(CString &str, double *vec, int length)
{ 
	int i;
	CString strTemp;

	//AfxMessageBox("\n ConvertDoubleVectorToString()\n", MB_OK, 0);

	// Convert double vector to CString form

	// Init the incoming string
	str.Format("");			

	// Vector content
	for(i=0; i<length; i++)
	{
		strTemp.Format(" %.10lf", vec[i]);			// convert to 10 decimal places
		str += strTemp;
	}
	
	return 0;
}

int PrintMatrix(double **M, int nrows, int ncols)
{
	int	i;
	int	j;
	CString sMsg;		// main msg string
	CString sMsgTemp;	// temp msg string
	UINT nType = MB_OK;	// style of msg. box
	UINT nIDhelp = 0;	// help context ID for the msg.

	// If the matrix is NULL then return.
	if(M == NULL)
	{
		return 1;
	}

	sMsgTemp.Format("\n PrintMatrix()\n");
	sMsg += sMsgTemp;
	
	// Print Matrix
	sMsgTemp.Format("\n");
	sMsg += sMsgTemp;
	
	for(i=0; i<nrows; i++)
	{
		for(j=0; j<ncols; j++)
		{
			//sMsgTemp.Format(" %-10.3e", M[i][j]); // left adjusted, total width 10, 3 decimal places, exponential notation
			sMsgTemp.Format(" %lf", M[i][j]);		// standard double formatting
			sMsg += sMsgTemp;
		}
		sMsgTemp.Format("\n");
		sMsg += sMsgTemp;
	}
	sMsgTemp.Format("\n");
	sMsg += sMsgTemp;

	// Display msg. box
	AfxMessageBox(sMsg, nType, nIDhelp);

	return 0;
}

int PrintMatrix(int **M, int nrows, int ncols)
{
	int	i;
	int	j;
	CString sMsg;		// main msg string
	CString sMsgTemp;	// temp msg string
	UINT nType = MB_OK;	// style of msg. box
	UINT nIDhelp = 0;	// help context ID for the msg.

	// If the matrix is NULL then return.
	if(M == NULL)
	{
		return 1;
	}

	sMsgTemp.Format("\n PrintMatrix()\n");
	sMsg += sMsgTemp;
	
	// Print Matrix
	sMsgTemp.Format("\n");
	sMsg += sMsgTemp;
	
	for(i=0; i<nrows; i++)
	{
		for(j=0; j<ncols; j++)
		{
			sMsgTemp.Format(" %d", M[i][j]);		// standard double formatting
			sMsg += sMsgTemp;
		}
		sMsgTemp.Format("\n");
		sMsg += sMsgTemp;
	}
	sMsgTemp.Format("\n");
	sMsg += sMsgTemp;

	// Display msg. box
	AfxMessageBox(sMsg, nType, nIDhelp);

	return 0;
}

int PrintVector(double *v, int ncols)
{
	int	i;
	CString sMsg;		// main msg string
	CString sMsgTemp;	// temp msg string
	UINT nType = MB_OK;	// style of msg. box
	UINT nIDhelp = 0;	// help context ID for the msg.

	// If the vector is NULL then return.
	if(v == NULL)
	{
		return 1;
	}

	sMsgTemp.Format("\n PrintVector()\n");
	sMsg += sMsgTemp;
	
	// Print Vector
	sMsgTemp.Format("\n");
	sMsg += sMsgTemp;

	for(i=0; i<ncols; i++)
	{
		//sMsgTemp.Format(" %-10.3e", v[i]);	// left adjusted, total width 10, 3 decimal places, exponential notation
		sMsgTemp.Format(" %lf", v[i]);			// standard double formatting
		sMsg += sMsgTemp;
	}
	sMsgTemp.Format("\n");
	sMsg += sMsgTemp;

	// Display msg. box
	AfxMessageBox(sMsg, nType, nIDhelp);

	return 0;
}

int PrintVector(float *v, int ncols)
{
	int	i;
	CString sMsg;		// main msg string
	CString sMsgTemp;	// temp msg string
	UINT nType = MB_OK;	// style of msg. box
	UINT nIDhelp = 0;	// help context ID for the msg.

	// If the vector is NULL then return.
	if(v == NULL)
	{
		return 1;
	}

	sMsgTemp.Format("\n PrintVector()\n");
	sMsg += sMsgTemp;
	
	// Print Vector
	sMsgTemp.Format("\n");
	sMsg += sMsgTemp;

	for(i=0; i<ncols; i++)
	{
		//sMsgTemp.Format(" %-10.3e", v[i]);	// left adjusted, total width 10, 3 decimal places, exponential notation
		sMsgTemp.Format(" %f", v[i]);			// standard double formatting
		sMsg += sMsgTemp;
	}
	sMsgTemp.Format("\n");
	sMsg += sMsgTemp;

	// Display msg. box
	AfxMessageBox(sMsg, nType, nIDhelp);

	return 0;
}

int PrintVector(int *v, int ncols)
{
	int	i;
	CString sMsg;		// main msg string
	CString sMsgTemp;	// temp msg string
	UINT nType = MB_OK;	// style of msg. box
	UINT nIDhelp = 0;	// help context ID for the msg.

	// If the vector is NULL then return.
	if(v == NULL)
	{
		return 1;
	}

	sMsgTemp.Format("\n PrintVector()\n");
	sMsg += sMsgTemp;
	
	// Print Vector
	sMsgTemp.Format("\n");
	sMsg += sMsgTemp;

	for(i=0; i<ncols; i++)
	{
		sMsgTemp.Format(" %d", v[i]);			// standard double formatting
		sMsg += sMsgTemp;
	}
	sMsgTemp.Format("\n");
	sMsg += sMsgTemp;

	// Display msg. box
	AfxMessageBox(sMsg, nType, nIDhelp);

	return 0;
}

void MatrixMultiply(int nrowsA, int ncolsA, int ncolsB, double **A, double **B, double **C)
{
	int i;
	int j;
	int k;
	
	// Matrix multiplication
	// A: nrowsA x ncolsA
	// B: nrowsB x ncolsB
	// C: nrowsA x ncolsB
	// C = A*B

	// Nullify C
	for(i=0; i<nrowsA; i++)
	{
		for(j=0; j<ncolsB; j++)
		{
			C[i][j] = 0.0;
		}
	}

	// Matrix multiplication
	for(k=0; k<ncolsB; k++)
	{
		for(i=0; i<nrowsA; i++)
		{
			for(j=0; j<ncolsA; j++)
			{
				C[i][k] = C[i][k] + A[i][j]*B[j][k];
				//printf(" C[%d][%d] = %lf\n", i, k, C[i][k]);
			}
		}
	}

	return;
}

int GaussJordanEliminationFullPivoting(double **A, int n, double **B, int m)
{
	//  WARNING! THIS MATERIAL FOLLOWS CLOSELY THE WORK OF:
	//  Numerical Recipes in C: The Art of Scientific Computing (2nd Ed.), sections 2.0 - 2.1.
	//	by, W.H. Press, S.A. Teukolsky, W.T. Vetterling and B.P. Flannery.
	//  Only minor syntactic changes are made here to the original, e.g. array indexing 
	//  (0 to n-1 is used here, rather than the original 1 to n).

	/*  GaussJordanEliminationFullPivoting() solves the systems of equations represented by, e.g.,

		[A].[x_1, x_2, x_3, Y] = [b_1, b_2, b_3, I]

		where

		A.x_1 = b_1,
		A.x_2 = b_2,
		A.x_3 = b_3,

		and

		A.Y = I
	 => Y = A^-1

		A:nxn = input matrix. 
		This is overwritten with A^-1.

		B:nxm = input matrix containing the m r.h.s. vectors (of length n).
		This is overwritten with the corres. set of soln. vectors. That is,
		x_1 = A^-1.b_1,
		x_2 = A^-1.b_2,
		x_3 = A^-1.b_3.
	*/

	// Declaration
	int i;
	int icol;				// col index
	int irow;				// row index
	int j;
	int k;
	int u;
	int v;
	int valid = 1;			// validity of result: (-1) if singular, (1) if no error
	int *index_c = NULL;	// col index used for pivoting
	int *index_r = NULL;	// row index used for pivoting
	int *ipiv = NULL;		// pivot index array
	double big;
	double dum;
	double pivinv;
	double eps = 1.0e-6;	// ALTERATION (BF): epsilon value to check for a "zero" on the diagonal

	// MEMORY NEW
	// Integer arrays used to manage the pivoting
	index_c = new int[n];	
	index_r = new int[n];
	ipiv = new int[n];

	// Nullify the ipiv array
	for(j=0; j<n; j++)
	{
		ipiv[j] = 0;
	}

	// Main loop over the n columns to be reduced.
	for(i=0; i<n; i++)
	{
		big = 0.0;											// init big == 0.0
		
		// Outer loop of the search for a pivot element
		for(j=0; j<n; j++)
		{
			if(ipiv[j] != 1)								// if the pivot array element != unity
			{
				for(k=0; k<n; k++)							
				{
					if(ipiv[k] == 0)						// if the pivot array element == nullity
					{
						if(fabs(A[j][k]) >= big)			// floating point absolute value
						{
							big = fabs(A[j][k]);			// update big with the double-type abs value of A[j][k]
							irow = j;
							icol = k;
						}
					}
				}// end for k					
			}// end if ipiv[j]
		}// end for j

		++(ipiv[icol]);

		// Now the pivot element is found, rows need to be interchanged, if rqd. to put the pivot element on the diag.
		// The cols are not physically interchanged, only relabelled:
		// index_c[i] the col of the ith pivot element is the ith col that is reduced
		// index_r[i] the row in which the pivot element was originally located.
		// If index_r[i] != index_c[i] there is an implied column interchange.
		// With this form of pivot management, the solutions x = A^-1.b which overwrite B, will end up in the correct order,
		// and the inverse matrix will be out of column alignment.

		if(irow != icol)
		{
			for(u=0; u<n; u++)
			{
				Swap(A[irow][u], A[icol][u]);
			}

			for(u=0; u<m; u++)
			{
				Swap(B[irow][u], B[icol][u]);
			}
		}

		// Pivot element indexing
		index_r[i] = irow;
		index_c[i] = icol;

		// Warn if there is a zero on the diag. implying a singular (non-invertible) matrix: was "if(A[icol][icol] == 0.0)"
		if((A[icol][icol] >= -eps) && (A[icol][icol] <= eps))
		{	
			AfxMessageBox("\n GaussJordanEliminationFullPivoting(): Singular Matrix   \n", MB_OK, 0);
			valid = -1;	// operational error due to singular matrix
		}

		// Now the pivot row can be divided by the pivot element, located at irow and icol.
		pivinv = 1.0/A[icol][icol];
		A[icol][icol] = 1.0;

		for(u=0; u<n; u++)
		{
			A[icol][u] *= pivinv;
		}

		for(u=0; u<m; u++)
		{
			B[icol][u] *= pivinv;
		}

		// Now the rows are reduced, except for the pivot one.
		for(v=0; v<n; v++)
		{
			if(v != icol)
			{
				dum = A[v][icol];
				A[v][icol] = 0.0;

				for(u=0; u<n; u++)
				{
					A[v][u] -= A[icol][u]*dum;
				}

				for(u=0; u<m; u++)
				{
					B[v][u] -= B[icol][u]*dum;
				}
			}
		}
	}// end for i
	// The end of the main loop over columns of the reduction.

	// Realign the soln given the column interchanges. This is done by interchanging pairs of columns in the 
	// reverse order to that in which the permutation was built.
	for(u=(n-1); u>=0; u--)
	{
		if(index_r[u] != index_c[u])
		{
			for(k=0; k<n; k++)
			{
				Swap(A[k][index_r[u]], A[k][index_c[u]]);
			}
		}
	}
	
	// MEMORY DELETE
	delete [] index_c;
	delete [] index_r;
	delete [] ipiv;

	return valid;
}

void Swap(double &a, double &b)
{
	// NOTE: pass by reference uses syntactically clean pass-by-reference mechanism, i.e. without requiring
	// client of the fn to pass an address, which would then be reflected by a ptr-to-double in the arg. list.
	double temp;
	
	// Swap a and b
	temp = a;
	a = b;
	b = temp;
}

void CDiagramEngDoc::SnapConnectionEndPointsAfterInitialConstruction(void)
{
	int modified = 0;			// flag indicating whether drawing is modified
	CPoint head_pt;				// connection head point
	CPoint tail_pt;				// connection tail point
	CPoint *from_point = NULL;	// connection from point
	CPort *from_port = NULL;	// connection from port
	CPort *to_port = NULL;		// connection to port
	list<CConnection*>::iterator it_con;
	list<CConnection*> con_list = GetSystemModel().GetConnectionList();


	// -- ITERATE THROUGH SYSTEM MODEL'S CONNECTION LIST
	for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
	{
		// Get tail and head points of connection
		tail_pt = (*it_con)->GetConnectionPointTail();
		head_pt = (*it_con)->GetConnectionPointHead();

		// CHECK TO SNAP TAIL POINT TO PORT
		if((*it_con)->GetRefFromPort() == NULL)		// If not already a ref_from port
		{
			from_port = SnapConnectionTailPointToPort(tail_pt); // tail_pt passed by ref. and may be updated.
			if(from_port != NULL)
			{
				(*it_con)->SetRefFromPort(from_port);
				modified = 1;
			}
		}
		(*it_con)->SetConnectionPointTail(tail_pt);


		// CHECK TO SNAP TAIL POINT TO BEND POINT
		if((*it_con)->GetRefFromPoint() == NULL)		// If not already a ref_from point
		{
			from_point = SnapConnectionTailPointToBendPoint(tail_pt); // tail_pt passed by ref. and may be updated.
			if(from_point != NULL)
			{
				(*it_con)->SetRefFromPoint(from_point);
				modified = 1;
			}
		}
		(*it_con)->SetConnectionPointTail(tail_pt);
			
		// CHECK TO SNAP HEAD POINT TO PORT
		if((*it_con)->GetRefToPort() == NULL)		// If not already a ref_to port
		{
			to_port = SnapConnectionHeadPointToPort(head_pt); 	// head_pt passed by ref. and may be updated.
			if(to_port != NULL)
			{
				(*it_con)->SetRefToPort(to_port);
				modified = 1;
			}
		}
		(*it_con)->SetConnectionPointHead(head_pt);

	}// end for

	// Set flags	
	if(modified)
	{
		//SetModifiedFlag(TRUE); 	// DO NOT SET MODIFIED TO TRUE SINCE IT IS UNNEC AND INTERFERES WITH OTHER FNS
		UpdateAllViews(NULL); 		// indicate that sys. should redraw.
	}

	return;
}

void CDiagramEngDoc::DeleteOutputBlockView(CBlock *ptr_blk)
{
	// Set the COutputBlockView ref-to-block to NULL

	POSITION posView = GetFirstViewPosition();
	while(posView)
	{
		CView *pView = GetNextView(posView);
		
		if((pView != NULL) && (pView->IsKindOf(RUNTIME_CLASS(COutputBlockView))))
		{
			if(((COutputBlockView*)pView)->GetRefOutputBlock() == ptr_blk)
			{
				// Set the m_pOutputBlk of the COutputBlockView class to NULL
				((COutputBlockView*)pView)->SetRefOutputBlock(NULL);
				
				// Close the view wnd.
				pView->GetParent()->DestroyWindow();
			}
		}
	}
}

void CDiagramEngDoc::OnSetOutputSignal() 
{
	// TODO: Add your command handler code here
	// DiagramEng (start)
	double	dist;			// Euclidean dist bw. block posn and point posn.
	double	width;			// block width
	CPoint	blk_posn;		// block posn.
	CPoint	point;			// local point var.
	CString sMsg;			// main msg string
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.
	list<CBlock*>::iterator it_blk;	// iterator
	
	// Get the point at which the context menu was invoked
	point = m_ptContextMenu;	// init a local copy.

	// Print msg.
	//sMsg.Format("\n CDiagramEngDoc::OnSetOutputSignal(), point (x,y) = (%d,%d)\n", point.x, point.y);
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// Get a copy of the blk_list in the system model
	list<CBlock*> &blk_list = GetSystemModel().GetBlockList();	// MUST BE A REFERENCE!

	// -- ITERATE THROUGH THE BLOCK LIST TO FIND SELECTED BLOCK
	for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
	{
		blk_posn = (*it_blk)->GetBlockPosition();
		width = (*it_blk)->GetBlockShape().GetBlockWidth();
		dist = sqrt(pow((blk_posn.x - point.x),2) + pow((blk_posn.y - point.y),2));

		if(dist <= 0.5*width*0.5)
		{
			// Only allow setting of an output signal for the Divide block
			if((*it_blk)->GetBlockName() == "divide_block")
			{
				(*it_blk)->AssignBlockOutputSignal();	// assign output signal for current block
				break;									// exit the for loop 
			}
		}

	}// end it_blk

	// DiagramEng (end)
}

void CDiagramEngDoc::UpdateCommonOpsToolbar(CString string)
{
	// Get the position of the first view
	POSITION pos = GetFirstViewPosition();
	if(pos != NULL)
	{
		// Get a ptr to the view in that position
		CView *pView = GetNextView(pos);
		if(pView)
		{
			// Get a ptr to the frame through the view
			CMainFrame *pFrame = (CMainFrame*)pView->GetTopLevelFrame();
			if(pFrame)
			{
				// Update the Edit box control on the CommonOps Toolbar through the frame
				pFrame->UpdateEditBoxTimeOnCommonOpsToolbar(string);
			}
		}
	}
}

int AssertManualFn(bool expr, char *file_name, int line)
{
	// Check truth of expr.
	if(!(expr))
	{
		// Print a msg to user
		PrintAssertMsg(file_name, line);

		// Native code generation for the fn.
		__asm{int 3};
	}
	
	return 0;
}

void PrintAssertMsg(char *file_name, int line)
{
	CString sMsg;			// main msg string
	FILE *ofp = NULL;		// outfile pointer
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.
	
	// Print msg. to user
	sMsg.Format("\n PrintAssertMsg()\n Assertion failed:\n File = %s\n Line = %d.    \n", file_name, line);
	AfxMessageBox(sMsg, nType, nIDhelp);

	// Open file for writing
	ofp = fopen("assert_log.txt", "w");	
	
	// Warn if file could not be opened
	if(ofp == NULL)
	{
		sMsg.Format("");
		sMsg.Format("\n PrintAssertMsg(): WARNING! 'assert_log.txt' file could not be opened.    \n");
		AfxMessageBox(sMsg, nType, nIDhelp);
	}
	else
	{
		fprintf(ofp, " Assertion failed, in file = %s, on line = %d.\n", file_name, line);	// write msg. to file
		fclose(ofp);	// close file
	}
}

void CDiagramEngDoc::WriteDiagramEngDataToFile(CString file_path, int display)
{
	int btnSel = IDYES;	// button selection: default file output stream opening of file
	int set_prec = 1;	// precision setting flag: (0) unset, (1) set.
	ifstream fin;		// infile stream
	ofstream fout;		// outfile stream
	CString sCaption;	// message box caption
	CString sMsg;		// main msg string
	CString sMsgTemp;	// temp msg string
	CWnd l_wnd;			// local CWnd
	UINT nType = MB_OK;	// style of msg. box
	UINT nIDhelp = 0;	// help context ID for the msg.

	//sMsg.Format("\n CDiagramEngDoc::WriteDiagramEngDataToFile()\n");
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// Check to see if file exists
	fin.open(file_path, ios::in);		// open file in input mode
	if(!fin)
	{
		fout.open(file_path, ios::out);				// open file in output mode						
	}
	else
	{
		// Question user about overwriting existing file
		sMsgTemp.Format(" %s already exists.  \n", file_path);
		sMsg += sMsgTemp;
		sMsgTemp.Format(" Do you want to replace it?\n");
		sMsg += sMsgTemp;
		sCaption = "DiagramEng - Overwrite";
		nType = MB_YESNO | MB_ICONEXCLAMATION;

		if(display)
		{
			btnSel = l_wnd.MessageBox(sMsg, sCaption, nType);
		}
		
		// Check button selection
		if(btnSel == IDYES)
		{
			fout.open(file_path, ios::out);	// open file in output mode
		}
		else if(btnSel == IDNO)
		{
			sMsg.Format("");
			sMsg.Format("\n Aborting writing model data to file.    \n");
			nType = MB_OK | MB_ICONSTOP;
			//AfxMessageBox(sMsg, nType, nIDhelp);

			fin.close();
			fin.clear();
			return;
		}
	}
	fin.close();
	fin.clear();
	
	// Check if file open
	if(!fout)			// if(!false) then execute
	{
		sMsg.Format("");
		sMsgTemp.Format("\n CDiagramEngDoc::WriteDiagramEngDataToFile()\n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n Unable to open file: %s\n", file_path);
		sMsg += sMsgTemp;
		sMsgTemp.Format(" Aborting file i/o.\n");
		sMsg += sMsgTemp;
		nType = MB_OK | MB_ICONSTOP;
		AfxMessageBox(sMsg, nType, nIDhelp);

		// Close the outfile stream and clear fail flag
		fout.close();									
		fout.clear();	// clears fail flag setting from previous file io.
		return;
	}

	// SET PRECISION FOR OUTPUT FILE STREAM
	// Note: a double has an approx precision of 15 signif. digits and a range of 10^-308 to 10^+308.
	if(set_prec)
	{
		fout.setf(ios::scientific, ios::floatfield); // floatfield format: scientific (ios::scientific), fixed (ios::fixed) 
		fout.precision(FILE_IO_PRECISION);			 // file i/o precision
	}

	// Call the CSystemModel fn to start the writing of data to a file.
	GetSystemModel().WriteSystemModelDataToFile(fout);

	// Document has been saved
	SetModifiedFlag(FALSE);

	// Close the outfile stream and clear fail flag
	fout.close();									
	fout.clear();	// clears fail flag setting from previous file i/o.
}

int CDiagramEngDoc::ReadDiagramEngDataFromFile(CString file_path)
{
	int file_flag = 0;		// (0) no error, (1) error
	ifstream fin;			// infile stream
	CString sMsg;			// main msg string
	CString sMsgTemp;		// temp msg string
	UINT nType = MB_OK;		// style of msg. box
	UINT nIDhelp = 0;		// help context ID for the msg.

	//sMsg.Format("\n CDiagramEngDoc::ReadDiagramEngDataFromFile()\n");
	//AfxMessageBox(sMsg, nType, nIDhelp);

	// Open file for reading
	fin.open(file_path, ios::in);			// open the infile stream, in mode

	if(!fin)		// if(!false) then execute
	{
		sMsg.Format("");
		sMsgTemp.Format("\n CDiagramEngDoc::ReadDiagramEngDataFromFile()\n");
		sMsg += sMsgTemp;
		sMsgTemp.Format("\n Unable to open file: %s\n", file_path);
		sMsg += sMsgTemp;
		sMsgTemp.Format(" Aborting file i/o.\n");
		sMsg += sMsgTemp;
		AfxMessageBox(sMsg, nType, nIDhelp);

		fin.close();
		fin.clear();

		file_flag = 1;		// error
		return file_flag;
	}

	// Call the CSystemModel fn to start the reading of data from a file.
	GetSystemModel().ReadSystemModelDataFromFile(fin);

	// Snap connection end points to port or bend point
	SnapConnectionEndPointsAfterInitialConstruction();

	// Redraw original diagram and mark as saved
	UpdateAllViews(NULL);	// this fn calls OnDraw to redraw the system model.
	SetModifiedFlag(TRUE);	

	// Close the infile stream and clear fail flag
	fin.close();									
	fin.clear();	// clears fail flag setting from previous file i/o.

	return file_flag;
}

void CDiagramEngDoc::OnFileSave() 
{
	// TODO: Add your command handler code here

	// DiagramEng (start)
	
	// Check whether document has been modified: non-zero => modified, zero => not modified
	if(IsModified())
	{
		// Reset default geometery as a result of zooming in/out
		ResetDefaultGeometry();

		// Check whether a file path already exists
		if(m_strFilePath == "")		
		{
			OnFileSaveAs();
		}
		else
		{
			WriteDiagramEngDataToFile(m_strFilePath, 0);	// 0 implies overwrite warning will not appear
		}
	}
	// DiagramEng (end)
}

void CDiagramEngDoc::OnFileSaveAs() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	BOOL dlgType = FALSE;					// TRUE => File Open dlg, FALSE => File Save dlg
	CString strDefExt = ".txt";				// default file extension
	CString strFileName = "model_data.txt";	// file name		
	CString strFilter = "All Files (*.*)|*.*|Plain Text (*.txt)|*.txt||";	// string pairs specifying filters for file list box
	DWORD dwFlags = OFN_ENABLESIZING | OFN_HIDEREADONLY;					// customization flags

	// Create a CFileDialog wnd
	CFileDialog dlgFile(dlgType, strDefExt, strFileName, dwFlags, strFilter, NULL);

	if(dlgFile.DoModal() == IDOK)
	{
		// Reset default geometry as a result of zooming in/out
		ResetDefaultGeometry();

		m_strFilePath = dlgFile.GetPathName();			// get the file path and name
		SetTitle(m_strFilePath);						// set the title of the current doc
		WriteDiagramEngDataToFile(m_strFilePath, 1);	// 1 implies overwrite warning will appear
	}
	// DiagramEng (end)
}

void CDiagramEngDoc::OnFileOpen() 
{
	// TODO: Add your command handler code here
	
	// DiagramEng (start)
	int btnSel;								// button selection
	BOOL dlgType = TRUE;					// TRUE => File Open dlg, FALSE => File Save dlg
	CString strDefExt = ".txt";				// default file extension
	CString strFileName = "model_data.txt";	// file name		
	CString strFilter = "All Files (*.*)|*.*|Plain Text (*.txt)|*.txt||";	// string pairs specifying filters for file list box
	CString sMsg;
	DWORD dwFlags = OFN_ENABLESIZING | OFN_HIDEREADONLY;					// customization flags

	// Create a new document
	if((m_SystemModel.GetBlockList().size() != 0) || (m_SystemModel.GetConnectionList().size() != 0) || (m_SystemModel.GetAnnotationList().size() != 0))
	{
		sMsg.Format(" A system model already exists.    \n Do you want to erase it? \n");
		btnSel = AfxMessageBox(sMsg, MB_YESNOCANCEL, 0);

		if(btnSel == IDYES)
		{
			// Delete System Model Lists
			m_SystemModel.DeleteBlockList();
			m_SystemModel.DeleteConnectionList();
			m_SystemModel.DeleteSourceOutputLoopLists();
			m_SystemModel.DeleteAnnotationList();

			// Set flags and redraw the doc
			SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
			UpdateAllViews(NULL); 		// indicate that sys. should redraw.
		}
		else if(btnSel == IDNO)
		{
			// The application will create a new document and re-enter the current CDiagramEngDoc::OnFileOpen() fn, i.e.:
			// CDiagramEngDoc::OnFileOpen(), (the current fn) calls
			// theApp.OnFileOpen(), (as shown in the code below), i.e. CDiagramEngApp::OnFileOpen(), which calls
			// CDiagramEngDoc::OnFileOpenAccessor(), which chains to the protected fn
			// CDiagramEngDoc::OnFileOpen(), i.e. the current function but now with a NEW DOCUMENT in place.
			theApp.OnFileOpen();
			return;
		}
		else if(btnSel == IDCANCEL)
		{
			return;
		}
	}

	// Create a CFileDialog wnd
	CFileDialog dlgFile(dlgType, strDefExt, strFileName, dwFlags, strFilter, NULL);

	if(dlgFile.DoModal() == IDOK)
	{
		m_strFilePath = dlgFile.GetPathName();		// get the path name including file name and location
		SetTitle(m_strFilePath);					// set the title of the document window
		
		// Read the model data from file
		if(ReadDiagramEngDataFromFile(m_strFilePath) == 0)	// read the model data from the file
		{
			theApp.AddToRecentFileList(m_strFilePath);		// if successful, add the file name to the recent file list.
		}
	}
	// DiagramEng (end)
}

int CDiagramEngDoc::OnFileClose() 
{
	// TODO: Add your command handler code here

	// DiagramEng (start)
	int btnSel;			// button selection
	int cancel = 0;		// flag denoting cancellation of file-save action: (0) no cancel, (1) cancel

	// Check whether document has been modified: non-zero => modified, zero => not modified
	if(IsModified())	
	{
		btnSel = AfxMessageBox("\n Do you want to save changes to the document?    \n", MB_YESNOCANCEL, 0);

		if(btnSel == IDYES)			// save changes
		{
			OnFileSave();		
		}
		else if(btnSel == IDNO)		// do not save changes
		{
			// no action to save
		}
		else if(btnSel == IDCANCEL)
		{
			cancel = 1;
			return cancel;
		}
	}	

	// Close the document (regardless)
	OnCloseDocument();	
	
	return cancel;
	// DiagramEng (end)
}

int CDiagramEngDoc::OnFileCloseAccessor()
{
	int cancel = 0;
	// Public interfacing fn for the protected OnFileClose() fn.

	// Call the OnFileClose() event handler fn.
	cancel = OnFileClose();

	return cancel;
}

void CDiagramEngDoc::OnFileOpenAccessor()
{
	OnFileOpen();
}

void CDiagramEngDoc::SetFilePath(CString path)
{
	// Set file path including the file name
	m_strFilePath = path;
}

void UpdateAllViewsForAllDocs(void)
{
	// Get the first doc template in the app
	POSITION pos = theApp.GetFirstDocTemplatePosition();

	// Iterate through all docs
	while(pos)
	{
		// Get the first CDocTemplate obj.
		CDocTemplate *pTemplate = theApp.GetNextDocTemplate(pos);

		// Get the string describing the doc
		CString sDocName;
		pTemplate->GetDocString(sDocName, CDocTemplate::docName);

		// If the doc is a DiagramEng "DiaEng" doc
		if(sDocName == _T("DiaEng"))
		{
			// Get the posn of the first doc in the list of docs associated with this template
			POSITION posDoc = pTemplate->GetFirstDocPosition();

			// While there is a first doc or a subsequent doc
			// If there is a first doc, then posDoc is not NULL: o/wise if there is no next doc, then posDoc = NULL.
			while(posDoc)
			{
				// Get a pointer to the doc
				CDiagramEngDoc* pDoc = (CDiagramEngDoc*)pTemplate->GetNextDoc(posDoc);
			
				if(pDoc != NULL)	
				{
					// Indicate that all views associated with this doc should be redrawn.
					pDoc->UpdateAllViews(NULL);
				}
			}// end while(posDoc)

		}// end if(sDocName)

	}// end while(pos)
}

void CDiagramEngDoc::OnFileSaveAccessor()
{
	OnFileSave();
}

const CString& CDiagramEngDoc::GetFilePath()
{
	return m_strFilePath;
}

double CDiagramEngDoc::GetDeltaLength() const
{
	return m_dDeltaLength;
}

void CDiagramEngDoc::SetDeltaLength(double length)
{
	m_dDeltaLength = length;
}

void CDiagramEngDoc::ResetDefaultGeometry()
{
	double scale_factor;

	// Divide the original Constructor initialized value by the current value of m_dDeltaLength
	if(m_dDeltaLength != 0)
	{
		scale_factor = 50.0/m_dDeltaLength;		// 50.0 is the default value of m_dDeltaLength
	}
	else
	{
		scale_factor = 1.0;
		AfxMessageBox("\n CDiagramEngDoc::ResetDefaultGeometry(): scaling error.\n", MB_OK, 0);
	}

	// Reset m_dDeltaLength to the constructor initialized value
	m_dDeltaLength = 50.0;
	
	// Scale the current diagram
	GetSystemModel().ScaleSystemModel(scale_factor);
	
	// Invalidate the client area to force a redraw on the next WM_PAINT message.
	UpdateAllViews(NULL);		// indicate that sys. should redraw.
	SetModifiedFlag(TRUE);		// mark the document as having been modified 
}

int CDiagramEngDoc::ResetSelectAll()
{
	int buffer = 20;		// 20 point buffer
	CPoint ptExtrema;		// extreme coordinates of diagram entities of system model
	CPoint ptMinima;		// minimum coordinates of diagram entities of system model
	
	//AfxMessageBox("\n CDiagramEngDoc::ResetSelectAll()\n", MB_OK, 0);

	// Reset flags
	if(m_iSelectAll == 1)
	{
		m_iRubberBandCreated = 0;
		m_iSelectAll = 0;
	}

	// Get the position of the first view in the list of views for the current doc.
	POSITION posn = GetFirstViewPosition();

	// Get a pointer to the view identified by posn.
	CView *pView = GetNextView(posn);		// CView is derived from CWnd, so pView can be used with the rect tracker

	// Create a device context using CView *pView where CView is derived from CWnd
	CClientDC dc(pView);

	// CScrollView-related impl.
	((CScrollView*)pView)->OnPrepareDC(&dc);	// prepare device context casting pView to ptr-to-CScrollView

	// Determine Diagram Coordinate Minima and Extrema and creating a bounding box
	ptMinima = m_SystemModel.DetermineDiagramCoordinateMinima();		// pseudo minima
	ptExtrema = m_SystemModel.DetermineDiagramCoordinateExtrema();		// pseudo maxima
	ptMinima.x -= buffer;
	ptMinima.y -= buffer;
	ptExtrema.x += buffer;
	ptExtrema.y += buffer;

	// Manually create a white-line rectangle to overdraw a possibly existing rectangle	
	CPen lpen(PS_DOT, 1, RGB(255,255,255));							// create a pen: RGB(0,0,0) black
	CPen *pOldPen = dc.SelectObject(&lpen);							// create a copy
	dc.SetROP2(R2_NOTXORPEN);										// make the rectangle transparent
	dc.Rectangle(ptMinima.x, ptMinima.y, ptExtrema.x, ptExtrema.y); // create the rectangle
	dc.SelectObject(pOldPen);										// reset the old pen
		
	// Invalidate the client area to force a redraw on the next WM_PAINT message.
	UpdateAllViews(NULL);		// indicate that sys. should redraw.
	SetModifiedFlag(TRUE);		// mark the document as having been modified 

	return 0;
}

int CDiagramEngDoc::GetSelectAllFlag() const
{
	return m_iSelectAll;
}

void CDiagramEngDoc::OnEditCut() 
{
	// TODO: Add your command handler code here
	// DiagramEng (start)
	int intersected = 0;
	int intersected_head = 0;
	int intersected_tail = 0;
	double blk_width;
	double delta = 0.25*m_dDeltaLength;
	CAnnotation *pAn = NULL;
	CBlock *pBlock = NULL;
	CConnection *pCon = NULL;
	CPoint an_posn;
	CPoint blk_posn;
	CPoint head_pt;
	CPoint tail_pt;
	CRectTracker temp_tracker;
	list<CAnnotation*> &an_list = m_SystemModel.GetAnnotationList();
	list<CAnnotation*> &an_list_cb = m_Clipboard.GetAnnotationList();
	list<CAnnotation*>::iterator it_an;
	list<CBlock*> &blk_list = m_SystemModel.GetBlockList();
	list<CBlock*> &blk_list_cb = m_Clipboard.GetBlockList();
	list<CBlock*>::iterator it_blk;
	list<CConnection*> &con_list = m_SystemModel.GetConnectionList();
	list<CConnection*> &con_list_cb = m_Clipboard.GetConnectionList();
	list<CConnection*>::iterator it_con;
	
	// -- IF A RUBBER BAND HAS BEEN CREATED
	if(m_iRubberBandCreated == 1)
	{
		// Create a temp tracker since m_RectTracker will have its coords updated when the rubber band is moved.
		temp_tracker = m_RectTracker;
		
		// -- ITERATE THROUGH BLOCKS
		it_blk = blk_list.begin();
		while(it_blk != blk_list.end())
		{
			// Get block properties
			blk_posn = (*it_blk)->GetBlockPosition();
			blk_width = (*it_blk)->GetBlockShape().GetBlockWidth();

			// Determine if item lies within rubber band
			intersected = DetermineCurrentAndIntersectRects(temp_tracker, blk_posn, (blk_width*0.5));
			if(intersected) 
			{
				// COPY BLOCK TO CLIPBOARD BLOCK LIST
				pBlock = (*it_blk)->CopyBlockData();
				blk_list_cb.push_back(pBlock);

				// Delete Output Block View
				DeleteOutputBlockView(*it_blk);

				// Delete block
				delete *it_blk;				// delete block pointed to by it_blk
				it_blk = blk_list.erase(it_blk); 	// delete element at offset it_blk in list (that held the block)
			}
			else // only increment the iterator if there were no intersection
			{
				it_blk++;
			}
			
		}// end for it_blk

		
		// -- ITERATE THROUGH ALL CONNECTIONS (of this model)
		it_con = con_list.begin();
		while(it_con != con_list.end())
		{
			// -- CHECK HEAD OR TAIL POINT SELECTED (denoting connection selection)

			// -- Get Tail Point
			tail_pt = (*it_con)->GetConnectionPointTail();

			// Determine if item lies within rubber band
			intersected_tail = DetermineCurrentAndIntersectRects(temp_tracker, tail_pt, delta);

			// -- Get Head Point
			head_pt = (*it_con)->GetConnectionPointHead();

			// Determine if item lies within rubber band
			intersected_head = DetermineCurrentAndIntersectRects(temp_tracker, head_pt, delta);

			// -- Check if head or tail points circumscribed
			if(intersected_tail || intersected_head) 
			{
				// COPY CONNECTION TO CLIPBOARD CONNECTION LIST
				pCon = new CConnection(**it_con);
				con_list_cb.push_back(pCon);

				// Disconnect all tail points from all the bend points on this connection.
				DisconnectTailPointsFromBendPoints(*it_con);

				// Delete connection
				delete *it_con;						// delete actual connection pointed to by it_con
				it_con = con_list.erase(it_con); 	// delete element at offset it_con in list (that held the connection)
			}
			else
			{
				it_con++;
			}
		}// end for it_con		

		// -- ITERATE THROUGH ANNOTATIONS
		it_an = an_list.begin();
		while(it_an != an_list.end())
		{
			// Get annotation properties
			an_posn = (*it_an)->GetPosition();

			// Determine if item lies within rubber band
			intersected = DetermineCurrentAndIntersectRects(temp_tracker, an_posn, delta);
			if(intersected) 
			{
				// COPY ANNOTATION TO CLIPBOARD BLOCK LIST
				pAn = new CAnnotation(**it_an);
				an_list_cb.push_back(pAn);

				// Delete annotation
				delete *it_an;						// delete annotation pointed to by it_an
				it_an = an_list.erase(it_an); 	// delete element at offset it_an in list (that held the annotation)
			}
			else // only increment the iterator if there were no intersection
			{
				it_an++;
			}
			
		}// end for it_an


		// Reset member vars.
		m_iRubberBandCreated = 0;	// end the rubber band state
		SetKeyFlagTrack(0);		// reset the key-based track flag since tracking aborted	

		// Set flags	
		SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
		UpdateAllViews (NULL); 		// indicate that sys. should redraw.

	}// end if m_iRubberBandCreated

	// DiagramEng (end)
}

void CDiagramEngDoc::OnEditCopy() 
{
	// TODO: Add your command handler code here
	// DiagramEng (start)
	int intersected = 0;
	int intersected_head = 0;
	int intersected_tail = 0;
	double blk_width;
	double delta = 0.25*m_dDeltaLength;
	CAnnotation *pAn = NULL;
	CBlock *pBlock = NULL;
	CConnection *pCon = NULL;
	CPoint an_posn;
	CPoint blk_posn;
	CPoint head_pt;
	CPoint tail_pt;
	CRectTracker temp_tracker;
	list<CAnnotation*> &an_list = m_SystemModel.GetAnnotationList();
	list<CAnnotation*> &an_list_cb = m_Clipboard.GetAnnotationList();
	list<CAnnotation*>::iterator it_an;
	list<CBlock*> &blk_list = m_SystemModel.GetBlockList();
	list<CBlock*> &blk_list_cb = m_Clipboard.GetBlockList();
	list<CBlock*>::iterator it_blk;
	list<CConnection*> &con_list = m_SystemModel.GetConnectionList();
	list<CConnection*> &con_list_cb = m_Clipboard.GetConnectionList();
	list<CConnection*>::iterator it_con;
	
	// -- IF A RUBBER BAND HAS BEEN CREATED
	if(m_iRubberBandCreated == 1)
	{
		// Create a temp tracker since m_RectTracker will have its coords updated when the rubber band is moved.
		temp_tracker = m_RectTracker;
		
		// -- ITERATE THROUGH BLOCKS
		for(it_blk = blk_list.begin(); it_blk != blk_list.end(); it_blk++)
		{
			// Get block properties
			blk_posn = (*it_blk)->GetBlockPosition();
			blk_width = (*it_blk)->GetBlockShape().GetBlockWidth();

			// Determine if item lies within rubber band
			intersected = DetermineCurrentAndIntersectRects(temp_tracker, blk_posn, (blk_width*0.5));
			if(intersected) 
			{
				// COPY BLOCK TO CLIPBOARD BLOCK LIST
				pBlock = (*it_blk)->CopyBlockData();
				blk_list_cb.push_back(pBlock);

			}
		}// end for it_blk

		
		// -- ITERATE THROUGH ALL CONNECTIONS (of this model)
		for(it_con = con_list.begin(); it_con != con_list.end(); it_con++)
		{
			// -- CHECK HEAD OR TAIL POINT SELECTED (denoting connection selection)

			// -- Get Tail Point
			tail_pt = (*it_con)->GetConnectionPointTail();

			// Determine if item lies within rubber band
			intersected_tail = DetermineCurrentAndIntersectRects(temp_tracker, tail_pt, delta);

			// -- Get Head Point
			head_pt = (*it_con)->GetConnectionPointHead();

			// Determine if item lies within rubber band
			intersected_head = DetermineCurrentAndIntersectRects(temp_tracker, head_pt, delta);

			// -- Check if head or tail points circumscribed
			if(intersected_tail || intersected_head) 
			{
				// COPY CONNECTION TO CLIPBOARD CONNECTION LIST
				pCon = new CConnection(**it_con);
				con_list_cb.push_back(pCon);	
			}
		}// end for it_con		


		// -- ITERATE THROUGH ANNOTATIONS
		for(it_an = an_list.begin(); it_an != an_list.end(); it_an++)
		{
			// Get annotation properties
			an_posn = (*it_an)->GetPosition();

			// Determine if item lies within rubber band
			intersected = DetermineCurrentAndIntersectRects(temp_tracker, an_posn, delta);
			if(intersected) 
			{
				// COPY ANNOTATION TO CLIPBOARD BLOCK LIST
				pAn = new CAnnotation(**it_an);
				an_list_cb.push_back(pAn);
			}		
		}// end for it_an


		// Reset member vars.
		m_iRubberBandCreated = 0;	// end the rubber band state
		SetKeyFlagTrack(0);		// reset the key-based track flag since tracking aborted	

		// Set flags	
		SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
		UpdateAllViews (NULL); 		// indicate that sys. should redraw.

	}// end if m_iRubberBandCreated

	// DiagramEng (end)
}

void CDiagramEngDoc::OnEditPaste() 
{
	// TODO: Add your command handler code here
	// DiagramEng (start)
	list<CAnnotation*> &an_list = m_SystemModel.GetAnnotationList();
	list<CAnnotation*> &an_list_cb = m_Clipboard.GetAnnotationList();
	list<CAnnotation*>::iterator it_an;
	list<CBlock*> &blk_list = m_SystemModel.GetBlockList();
	list<CBlock*> &blk_list_cb = m_Clipboard.GetBlockList();
	list<CBlock*>::iterator it_blk;
	list<CConnection*> &con_list = m_SystemModel.GetConnectionList();
	list<CConnection*> &con_list_cb = m_Clipboard.GetConnectionList();
	list<CConnection*>::iterator it_con;

	// Add clipboard block list contents to end of system model block list
	// merge() merges the arg list into the list upon which merge() is called leaving, the arg list empty.
	blk_list.merge(blk_list_cb);

	// Add clipboard connection list contents to end of system model connection list
	// merge() merges the arg list into the list upon which merge() is called, leaving the arg list empty.
	con_list.merge(con_list_cb);

	// Snap connection end points to port or bend point
	SnapConnectionEndPointsAfterInitialConstruction();

	// Add clipboard annotation list contents to end of system model annotation list
	// merge() merges the arg list into the list upon which merge() is called leaving, the arg list empty.
	an_list.merge(an_list_cb);

	// Set flags	
	SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
	UpdateAllViews (NULL); 		// indicate that sys. should redraw.

	// DiagramEng (end)
}

void CDiagramEngDoc::OnEditUndo() 
{
	// TODO: Add your command handler code here
	// DiagramEng (start)
	int i;
	CSystemModel *pSystemModel = NULL;
	list<CSystemModel*>::iterator it_sm;

	// Check index denoting position in list
	if(m_iIndexSystemModel == 0)
	{
		m_iEditUndoFlag = 0;		// menu disable flag set to disabled
		return;
	}
	else
	{	
		m_iIndexSystemModel--;		// decrement index to get previous item in list

		// Adjust undo flag
		if(m_iIndexSystemModel == 0)
		{	
			m_iEditUndoFlag = 0;	// menu disable flag set to disabled
		}
		else
		{
			m_iEditUndoFlag = 1;	// menu disable flag set to enabled
		}

		// Adjust redo flag
		m_iEditRedoFlag = 1;
	}

	// GET A SAVED SYSTEM MODEL FROM THE LIST OF PTRS-TO-CSystemModel
	it_sm = m_lstSystemModel.begin();
	for(i=0; i<m_iNSystemModels; i++)
	{	
		if(i == m_iIndexSystemModel)
		{	
			pSystemModel = *it_sm;
			break;
		}
		else
		{
			it_sm++;
		}
	}

	// ASSIGN SYSTEM MODEL IN LIST TO STANDALONE SYSTEM MODEL USING ASSIGNMENT OPERATOR
	// m_SystemModel = *pSystemModel; is equivalent to, m_SystemModel.operator=(*pSystemModel);
	m_SystemModel.operator=(*pSystemModel);	
		
	// Snap connection end points
	SnapConnectionEndPointsAfterInitialConstruction();

	// Update All Views
	UpdateAllViews(NULL);	

	// DiagramEng (end)
}

void CDiagramEngDoc::OnEditRedo() 
{
	// TODO: Add your command handler code here
	// DiagramEng (start)
	int i;
	int size;
	CSystemModel *pSystemModel = NULL;
	list<CSystemModel*>::iterator it_sm;

	// Check index denoting position in list
	size = m_lstSystemModel.size();
	if((size == 0) || (m_iIndexSystemModel == (size - 1)))
	{
		m_iEditRedoFlag = 0;		// menu disable flag set to disabled
		return;
	}
	else
	{
		m_iIndexSystemModel++;		// increment index to get next item in list

		// Adjust redo flag
		if(m_iIndexSystemModel == (m_lstSystemModel.size() - 1))
		{	
			m_iEditRedoFlag = 0;	// menu disable flag set to disabled
		}
		else
		{
			m_iEditRedoFlag = 1;	// menu disable flag set to enabled
		}

		// Adjust undo flag
		m_iEditUndoFlag = 1;
	}

	// GET A SAVED SYSTEM MODEL FROM THE LIST OF PTRS-TO-CSystemModel
	it_sm = m_lstSystemModel.begin();
	for(i=0; i<m_iNSystemModels; i++)
	{	
		if(i == m_iIndexSystemModel)
		{	
			pSystemModel = *it_sm;
			break;
		}
		else
		{
			it_sm++;
		}
	}

	// ASSIGN SYSTEM MODEL IN LIST TO STANDALONE SYSTEM MODEL USING ASSIGNMENT OPERATOR
	// m_SystemModel = *pSystemModel; is equivalent to, m_SystemModel.operator=(*pSystemModel);
	m_SystemModel.operator=(*pSystemModel);	
	
	// Snap connection end points
	SnapConnectionEndPointsAfterInitialConstruction();

	// Update All Views
	UpdateAllViews(NULL);
	
	// DiagramEng (end)
}

int CDiagramEngDoc::GetEditUndoFlag() const
{
	return m_iEditUndoFlag;
}

int CDiagramEngDoc::GetEditRedoFlag() const
{
	return m_iEditRedoFlag;
}

void CDiagramEngDoc::DeleteSystemModelList()
{
	list<CSystemModel*>::iterator it_sm;

	// Delete system model list
	for(it_sm = m_lstSystemModel.begin(); it_sm != m_lstSystemModel.end(); it_sm++)
	{
		delete (*it_sm);	// delete what it_sm is pointing to: i.e. deref the it_sm ptr and delete the ptr-to-CSystemModel.
	}
	m_lstSystemModel.clear();
}

void CDiagramEngDoc::SetModifiedFlag(BOOL bModified)
{
	// Chain to base class version
	CDocument::SetModifiedFlag(bModified);

	// Check whether saving to a list is rqd.
	if(bModified == TRUE)
	{
		SaveSystemModelToList();	// explicitly save the current system model to a list
	}
	else
	{
		// No need to save system model to list
	}
}

void CDiagramEngDoc::SaveSystemModelToList()
{
	int i;
	int lst_size;		
	list<CSystemModel*>::iterator it_sm;
	list<CSystemModel*>::iterator it_sm_erase_start;
	CSystemModel *pSystemModel = NULL;
	
	// Place an empty model in the list to begin with, representing an empty palette
	if(m_iIndexSystemModel == 0)
	{
		// Copy constructor called to construct a copy of a system model
		pSystemModel = new CSystemModel(m_SystemModel);

		pSystemModel->DeleteBlockList();
		pSystemModel->DeleteConnectionList();
		pSystemModel->DeleteSourceOutputLoopLists();
		pSystemModel->DeleteAnnotationList();

		m_lstSystemModel.push_back(pSystemModel);
	}

	// Determine start point for erase
	it_sm_erase_start = m_lstSystemModel.end();		// initialize iterator to the end of the array
	it_sm = m_lstSystemModel.begin();				// initialize iterator to the beginning of the array
	for(i=0; i<m_iNSystemModels; i++)
	{
		if(i == (m_iIndexSystemModel + 1))
		{
			it_sm_erase_start = it_sm;
			break;
		}
		else
		{
			it_sm++;	
		}
	}

	// Delete unwanted elements and then erase the portion of the list
	for(it_sm = it_sm_erase_start; it_sm != m_lstSystemModel.end(); it_sm++)
	{
		delete (*it_sm);					// delete the ptr-to-CSystemModel pointed to by it_sm
	}
	m_lstSystemModel.erase(it_sm_erase_start, m_lstSystemModel.end());

	// Create a new system model using the class member m_SystemModel and the copy constructor
	pSystemModel = new CSystemModel(m_SystemModel);

	// Put address of the newly created system model at the back of m_lstSystemModel
	m_lstSystemModel.push_back(pSystemModel);

	// If list too big delete the first element and take it off the list
	lst_size = m_lstSystemModel.size();
	if(lst_size > m_iNSystemModels)
	{
		it_sm = m_lstSystemModel.begin();
		delete (*it_sm);					// delete the ptr-to-CSystemModel pointed to by it_sm
		m_lstSystemModel.pop_front();
	}

	// Adjust index
	lst_size = m_lstSystemModel.size();
	m_iIndexSystemModel = lst_size - 1;

	// Adjust Undo/Redo flags
	if(m_iIndexSystemModel == 0)	// if at the beginning of the list
	{
		m_iEditUndoFlag = 0;		// can't undo
	}
	else
	{
		m_iEditUndoFlag = 1;		// can undo
	}
	m_iEditRedoFlag = 0;			// at the end of the list so can't redo
}

void CDiagramEngDoc::OnFormatAnnotation() 
{
	// TODO: Add your command handler code here
	// DiagramEng (start)
	int annotation_found = 0;				// flag indicating whether annotation found
	bool flag = FALSE;						// flag indicating whether "OK" or "Cancel" was selected in FormatAnnotation() 
	double dist;							// Euclidean dist bw. annotation posn and point posn.
	double length = 0.25*m_dDeltaLength;	// reference length
	CPoint annotation_posn;					// annotation posn.
	CPoint point;							// local point var.
	CString sMsg;							// main msg string
	list<CAnnotation*>::iterator it_an;		// iterator
	
	// Get the point at which the context menu was invoked
	point = m_ptContextMenu;	// init a local copy.

	// Print msg.
	//sMsg.Format("\n CDiagramEngDoc::OnFormatAnnotation(), point (x,y) = (%d,%d)\n", point.x, point.y);
	//AfxMessageBox(sMsg, MB_OK, 0);

	// Get a copy of the annotation list in the CSystemModel class
	list<CAnnotation*> &annotation_list = GetSystemModel().GetAnnotationList();	

	// ITERATE THROUGH THE ANNOTATION LIST TO FIND SELECTED ANNOTATION
	for(it_an = annotation_list.begin(); it_an != annotation_list.end(); it_an++)
	{
		annotation_posn = (*it_an)->GetPosition();
		dist = sqrt(pow((annotation_posn.x - point.x),2) + pow((annotation_posn.y - point.y),2));

		if(dist <= length)
		{
			flag = (*it_an)->FormatAnnotation(point);	// format the annotation
			if(flag == TRUE)							// if the annotation was changed
			{
				SetModifiedFlag(TRUE);					// set the doc. as modified to prompt the user to save
				UpdateAllViews(NULL);					// redraw the doc since annotation has changed
			}
			annotation_found = 1;						// an annotation was found for formatting
			break;										// exit the for loop 
		}
	}

	// CREATE A NEW ANNOTATION AT THE POINT AT WHICH THE CONTEXT MENU WAS INVOKED 
	if(annotation_found == 0)
	{
		// Create an annotation object
		CAnnotation *pAn = new CAnnotation();	// default constructor for CAnnotation object
		flag = pAn->FormatAnnotation(point);	// format the annotation

		// If the "OK" rather than "Cancel" button was clicked, then add the annotation.
		if(flag == TRUE)
		{
			annotation_list.push_back(pAn);		// add the new annotation to the actual list (passed by ref.)
			SetModifiedFlag(TRUE);				// set the doc. as modified to prompt the user to save
			UpdateAllViews(NULL);				// redraw the doc since annotation has been added
		}
		else
		{
			delete pAn;							// delete the recently created annotation
		}
	}

	// DiagramEng (end)	
}

int CDiagramEngDoc::DeleteAnnotation()
{
	int		delete_an = 0;						// annotation deletion flag
	double	dist;								// Euclidean dist bw. annotation posn and point posn.
	double	length = 0.25*m_dDeltaLength;		// reference length
	CPoint	an_posn;							// annotation posn.
	CPoint	point;								// local point var.
	list<CAnnotation*>::iterator it_an;			// iterator
	list<CAnnotation*>::iterator it_er = NULL;	// element to erase

	// Get the point at which the context menu was invoked
	point = m_ptContextMenu;	// init a local copy.

	// Get a copy of the annotation list in the system model
	list<CAnnotation*> &annotation_list = GetSystemModel().GetAnnotationList();	// MUST BE A REFERENCE!

	// Iterate through the list to find which item to delete.
	for(it_an = annotation_list.begin(); it_an != annotation_list.end(); it_an++)
	{
		an_posn = (*it_an)->GetPosition();
	
		dist = sqrt(pow((an_posn.x - point.x),2) + pow((an_posn.y - point.y),2));
		if(dist <= length)
		{
			// Record which annotation to erase
			delete_an = 1;
			it_er = it_an;
			break;
		}
	}

	// Delete the item in the list
	if(delete_an == 1)
	{
		// Delete annotation
		delete *it_er;						// delete actual annotation pointed to by it_er
		annotation_list.erase(it_er);		// delete element at offset it_er in list (that held the annotation)

		// Set as modified and redraw the doc.
		SetModifiedFlag(TRUE);	// set the doc. as having been modified to prompt user to save
		UpdateAllViews(NULL);	// indicate that sys. should redraw.
	}

	// Return a flag indicating whether an item was deleted
	return delete_an;
}

int CDiagramEngDoc::FineMoveAnnotation(int key_flag)
{
	int delta_x = 1;						// incr. in x direc. 
	int delta_y = 1;						// incr. in y direc. 
	int move_flag = 0;						// flag indicating entity movement			
	double dist;							// Euclidean dist bw. annotation posn and point posn.
	double length = 0.25*m_dDeltaLength;	// reference length
	CPoint an_posn;							// annotation posn.
	CPoint point;							// local point var.
	list<CAnnotation*>::iterator it_an;		// annotation iterator
	static CAnnotation *pAn = NULL;			// ptr-to-annotation to hold the chosen annotation's address.		
	
	// Get the point at which the context menu was invoked
	point = m_ptContextMenu;				// init a local copy.


	// -- IF THIS IS THE FIRST ENTRY INTO THIS FN THEN RECORD THE ADDRESS OF THE SELECTED ANNOTATION 
	if(GetFineMoveFlag() == 1)
	{
		// Get a copy of the annotation_list in the system model
		list<CAnnotation*> &annotation_list = GetSystemModel().GetAnnotationList();

		// -- ITERATE THROUGH THE ANNOTATION LIST
		for(it_an = annotation_list.begin(); it_an != annotation_list.end(); it_an++)
		{
			move_flag = 0;
			an_posn = (*it_an)->GetPosition();
			dist = sqrt(pow((an_posn.x - point.x),2) + pow((an_posn.y - point.y),2));
			
			// -- IF USER CLICKED ON AN ANNOTATION
			if(dist <= length)
			{	
				// -- FILTER THE KEY/DIRECTION (screen (0,0) top left cnr., +ve x to right, +ve y downward
				if(key_flag == 37) 							// LEFT ARROW
				{	
					// Update annotation posn.
					an_posn.x = an_posn.x - delta_x;		
					(*it_an)->SetPosition(an_posn);
					pAn = *it_an;							// record address of annotation for subsequent movt.
					move_flag = 1;
				}
				else if(key_flag == 38)						// UP ARROW
				{
					// Update annotation posn.
					an_posn.y = an_posn.y - delta_y;
					(*it_an)->SetPosition(an_posn);
					pAn = *it_an;							// record address of annotation for subsequent movt.
					move_flag = 1;
				}
				else if(key_flag == 39)						// RIGHT ARROW
				{
					// Update annotation posn.
					an_posn.x = an_posn.x + delta_x;
					(*it_an)->SetPosition(an_posn);
					pAn = *it_an;							// record address of annotation for subsequent movt.
					move_flag = 1;
				}
				else if(key_flag == 40)						// DOWN ARROW
				{
					// Update annotation posn.
					an_posn.y = an_posn.y + delta_y;
					(*it_an)->SetPosition(an_posn);
					pAn = *it_an;							// record address of annotation for subsequent movt.
					move_flag = 1;
				}
		
				if(move_flag == 1)
				{
					// Set flag
					m_iFineMoveFlag = 4;

					// Mark the document as changed
					UpdateAllViews(NULL);		// this fn calls OnDraw to redraw the system model.
					SetModifiedFlag(TRUE);		// prompt the user to save 
					break;						// break out of for loop
				}
			}// end if dist
		}// end it_blk
	}
	else if(m_iFineMoveFlag == 4) // -- IF THIS IS NOT THE FIRST TIME INTO THE FN, THEN MOVE THE ANNOTATION USING IT'S ADDRESS
	{
		// -- FILTER THE KEY/DIRECTION (screen (0,0) top left cnr., +ve x to right, +ve y downward
		if(key_flag == 37) 							// LEFT ARROW
		{	
			// Update annotation posn.
			an_posn = pAn->GetPosition();
			an_posn.x = an_posn.x - delta_x;		
			pAn->SetPosition(an_posn);
			move_flag = 1;
		}
		else if(key_flag == 38)						// UP ARROW
		{
			// Update annotation posn.
			an_posn = pAn->GetPosition();
			an_posn.y = an_posn.y - delta_y;
			pAn->SetPosition(an_posn);
			move_flag = 1;
		}
		else if(key_flag == 39)						// RIGHT ARROW
		{
			// Update annotation posn.
			an_posn = pAn->GetPosition();
			an_posn.x = an_posn.x + delta_x;
			pAn->SetPosition(an_posn);
			move_flag = 1;
		}
		else if(key_flag == 40)						// DOWN ARROW
		{
			// Update annotation posn.
			an_posn = pAn->GetPosition();
			an_posn.y = an_posn.y + delta_y;
			pAn->SetPosition(an_posn);
			move_flag = 1;
		}

		if(move_flag == 1)
		{
			// Mark the document as changed
			UpdateAllViews(NULL);		// this fn calls OnDraw to redraw the system model.
			SetModifiedFlag(TRUE);		// prompt the user to save 
		}
	}

	return move_flag;
}

int CDiagramEngDoc::TrackAnnotation(CPoint point, CWnd *pWnd)
{
	int tracker_flag = 0;
	double dist;							// Euclidean distance
	double length = 0.25*m_dDeltaLength;	// reference length
	CPoint an_posn;							// annotation position
	CSize an_size;							// annotation output text size
	CPoint bottom_right;					
	CPoint top_left;
	list<CAnnotation*>::iterator it_an;		
	list<CAnnotation*> &annotation_list = GetSystemModel().GetAnnotationList();

	// CScrollView-related change
	CPoint ptLogical = point;				// local point to record the point in logical units
	CClientDC dc(pWnd);						// create a device context using the pointer-to-CWnd pWnd.
	((CScrollView*)pWnd)->OnPrepareDC(&dc);	// prepare the device context casting pWnd to ptr-to-CScrollView
	dc.DPtoLP(&ptLogical);					// use the dc to convert ptLogical from device to logical units

	// Iterate through annotation list
	for(it_an = annotation_list.begin(); it_an != annotation_list.end(); it_an++)
	{
		an_posn = (*it_an)->GetPosition();
		//dist = sqrt(pow(an_posn.x - point.x,2) + pow(an_posn.y - point.y,2));
		dist = sqrt(pow(an_posn.x - ptLogical.x,2) + pow(an_posn.y - ptLogical.y,2));	// CScrollView-related change using ptLogical
		
		// If annotation is clicked
		if(dist <= length)
		{		
			an_size = (*it_an)->GetSize();				// annotation font size
			top_left.x = an_posn.x;						// an_posn is not the center of the tracking rectangle but top left
			top_left.y = an_posn.y;
			an_size = (*it_an)->GetOutputTextSize();	// size of annotation output text
			bottom_right.x = an_posn.x + an_size.cx;
			bottom_right.y = an_posn.y + an_size.cy;

			// CScrollView-related change
			dc.LPtoDP(&top_left);		// convert logical pt based on logical annotation posn to device pt
			dc.LPtoDP(&bottom_right);	// convert logical pt based on logical annotation posn to device pt

			// Set the rect tracker's position
			m_RectTracker.m_rect.SetRect(top_left.x, top_left.y, bottom_right.x, bottom_right.y);

			// Track the mouse’s position till left mouse button up
			tracker_flag = m_RectTracker.Track(pWnd, point, TRUE); // 0 => item not tracked, 1 => item was tracked
		
			// Get new tracker position and update new annotation position with tracker position
			// Since tracking rectangle was sized from annotation position downward, use top left cnr. not center point. 
			//an_posn = m_RectTracker.m_rect.CenterPoint();		// center point of rectangle
			an_posn = m_RectTracker.m_rect.TopLeft();			// top left cnr of rectangle

			dc.DPtoLP(&an_posn);		// CScrollView-related change: convert device units to logical units

			(*it_an)->SetPosition(an_posn);

			// Set flags	
			SetModifiedFlag(TRUE); 		// set the doc. as having been modified to prompt user to save
			UpdateAllViews(NULL); 		// indicate that sys. should redraw.
			break;
		}
	}
	
	// Return flag indicating if an item was tracked: 0 => not tracked, 1 => tracked.
	return tracker_flag;
}


