Click here to Skip to main content
15,892,746 members
Articles / Mobile Apps

The StateWizard VC++ Add-in and Engine with Source Code

Rate me:
Please Sign up or sign in to vote.
4.73/5 (24 votes)
26 Mar 2009CPOL12 min read 190.7K   2.8K   132  
A cross-platform state-oriented application framework and a ClassWizard-like round-trip UML dynamic modeling/development tool that runs in popular IDEs. Aims at providing concurrent, distributed, and real-time application development tools for Win32/Linux
/**********************************************************************
UML StateWizard provides its software under the LGPL License and 
zlib/libpng License.

Email us at info@intelliwizard.com for any information, suggestions and 
feature requestions.

Home Page: http://www.intelliwizard.com
*************************************************************************/

/* =============================================================================
 * Filename:    StateChartView.cpp
 * 
 * Copyright  Inc
 * All rights reserved.
 * -----------------------------------------------------------------------------
 * General description of this file:
 *
 * The state chart implementation.
 * -----------------------------------------------------------------------------
 *                               Revision History
 * -----------------------------------------------------------------------------
 * Version   Date      Author          Revision Detail
 * 1.0.0    2004/12/21                 State charts and arrows drawing.
 *			2005/03/16				   Draw arc for each corner	of the rectangle.
									   Draw an arrow for default state.	
									   Calcute column width and row height of each state.
 * ===========================================================================*/

#include "stdafx.h"
#include "StateChartView.h"
#include "dib.h"
#include "treefile.h"
#include "..\Common\ParamStore.h"

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

/////////////////////////////////////////////////////////////////////////////
// CStateChartView

IMPLEMENT_DYNCREATE(CStateChartView, CScrollView)

BEGIN_MESSAGE_MAP(CStateChartView, CScrollView)
	//{{AFX_MSG_MAP(CStateChartView)
	ON_WM_RBUTTONDOWN()
	ON_COMMAND(IDD_ADD_STATE, OnAddState)
	ON_COMMAND(IDD_DEL_APP, OnDelApp)
	ON_COMMAND(IDD_ADD_HANDLE, OnAddHandle)
	ON_COMMAND(IDD_DEFLT_CHANGE, OnDefltChange)
	ON_COMMAND(IDD_DEL_STATE, OnDelState)
	ON_COMMAND(IDD_GOTO_ENTRY, OnGotoEntry)
	ON_COMMAND(IDD_GOTO_EXIT, OnGotoExit)
	ON_COMMAND(IDD_MENU_SAVEASBMP,OnSaveAsBmp)
	ON_COMMAND(IDD_MENU_SAVEASTXT,OnSaveAsTxt)
	ON_WM_LBUTTONDOWN()
	ON_WM_DESTROY()
	ON_WM_LBUTTONUP()
	ON_WM_SETCURSOR()
	ON_WM_MOUSEMOVE()
	//}}AFX_MSG_MAP
	// Standard printing commands
	ON_COMMAND(ID_FILE_PRINT, CScrollView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, CScrollView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, CScrollView::OnFilePrintPreview)
	ON_COMMAND_RANGE(WM_STATE_TRAN_ITEM_0, WM_STATE_TRAN_ITEM_LAST, OnGotoHdler)
//	ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipNotify)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CStateChartView construction/destruction

int g_nMaxLevelNum = CParamStore::GetInstance().GetIntValue("StateChart","MaxLevel") == -1  
	? DEF_STATE_CHART_MAX_LEVEL : CParamStore::GetInstance().GetIntValue("StateChart","MaxLevel");


//int g_nIsCriticalSection = 0;
CStateChartView::CStateChartView()
{
	// TODO: add construction code here

	m_hItemDrag = NULL;
	m_bMouseMove = FALSE;
	m_hTopStateItem =NULL;

}

CStateChartView::~CStateChartView()
{
}

BOOL CStateChartView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO  : Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	return CScrollView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CStateChartView drawing

/********************************************************************************************
DESCRIPTION: 

INPUT:  m_hTopStateItem
	m_hTopStateItem is the tree item of the top state item to be draw; We have to record down it, because while state chart is open,
	the selected state tree item may be changed.
OUTPUT: 
NOTE: 

**********************************************************************************************/
void CStateChartView::OnDraw(CDC* pDC)
{
	// TODO: add draw code for native data here
	TRACE("Starting....\n");

	///////////////////
	if (m_hTopStateItem==NULL) return;


	SME_STATE_T nSelState = 0;
	m_EventLineNo = 0;

	CalculateStateChartSize(pDC, m_hTopStateItem, m_hTopStateItem);
	CalculateColWidthRowHeight(m_hTopStateItem, m_hTopStateItem);

   	// ensure scroll bars work
	TRACE("Ensure scroll bars %d %d\n", ((STATE_CHART_INFO_T*)m_StateChartInfoList[nSelState])->nWidth,
		((STATE_CHART_INFO_T*)m_StateChartInfoList[nSelState])->nHeight);

	ASSERT(((STATE_CHART_INFO_T*)m_StateChartInfoList[nSelState])->nWidth>0);
	ASSERT(((STATE_CHART_INFO_T*)m_StateChartInfoList[nSelState])->nHeight>0);
	int mm;
	CSize size, sizePage, sizeLine;
	GetDeviceScrollSizes(mm, size, sizePage, sizeLine);
	SetScrollSizes(MM_TEXT, 
		CSize(max(size.cx, ((STATE_CHART_INFO_T*)m_StateChartInfoList[nSelState])->nWidth+ 100), 
		max(size.cy, ((STATE_CHART_INFO_T*)m_StateChartInfoList[nSelState])->nHeight+ 100 )));

	DrawStateChart(pDC, m_hTopStateItem, m_hTopStateItem, 50,50);
	DrawArrow(pDC, m_hTopStateItem, m_hTopStateItem);
}

/********************************************************************************************
DESCRIPTION: 

INPUT:  
OUTPUT: 
NOTE: 

**********************************************************************************************/
void CStateChartView::OnInitialUpdate()
{
	CScrollView::OnInitialUpdate();

	// TODO: calculate the total size of this view
	// Initialize state chart list to record information
	m_hTopStateItem = m_pStateTree->tree.GetSelectedItem();

	InitChartInfoList(m_hTopStateItem);
	InitEventInfoList(m_hTopStateItem, m_hTopStateItem);
	InitStateNameList(m_hTopStateItem);
	
	m_nSelStateSeqNo = -1;
	m_EventLineNo = 0;
	m_bSizeChange = -1;
	g_nMaxLevelNum = CParamStore::GetInstance().GetIntValue("StateChart","MaxLevel") == -1  
		? DEF_STATE_CHART_MAX_LEVEL : CParamStore::GetInstance().GetIntValue("StateChart","MaxLevel");
	m_nSavedMaxLevel = g_nMaxLevelNum;
/*	m_ListBox.Create
		(LBS_OWNERDRAWVARIABLE | LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | LBS_NOTIFY | WS_BORDER  | WS_VSCROLL , CRect(0, 0, 50, 50), this, IDD_LBXITEMS);

	BOOL	bRetValue = FALSE;
	HICON	hIcon = NULL;
	bRetValue = m_ImageList.Create(16, 16, ILC_COLOR8 | ILC_MASK, 1, 1);
	ASSERT(bRetValue == TRUE);
	
	// Add some icons
	hIcon = AfxGetApp()->LoadIcon(IDI_EVENT_ID);
	m_ImageList.Add(hIcon);
	
	m_ListBox.SetImageList(&m_ImageList);
	m_Font.CreateFont(
		14,                         // nHeight
		0,                         // nWidth
		0,                         // nEscapement
		0,                         // nOrientation
		FW_BOLD,              	   // nWeight
		FALSE,                     // bItalic
		FALSE,                     // bUnderline
		0,                         // cStrikeOut
		ANSI_CHARSET,              // nCharSet
		OUT_DEFAULT_PRECIS,        // nOutPrecision
		CLIP_DEFAULT_PRECIS,       // nClipPrecision
		DEFAULT_QUALITY,           // nQuality
		DEFAULT_PITCH | FF_SWISS,  // nPitchAndFamily
		_T("Microsoft Sans Serif") // lpszFacename
		);
	m_ListBox.SetFont(&m_Font);
	m_ListBox.ShowWindow(SW_HIDE); //Hide m_ListBox	
	m_ListBox.SetOwner(GetParent());
*/
	CSize sizeTotal;
	sizeTotal.cx = sizeTotal.cy = 100;
	SetScrollSizes(MM_TEXT, sizeTotal);

	m_hItemDrag = NULL;
	m_bMouseMove = FALSE;

}

///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:  Initialize state chart information.
// INPUT:  1) hSelector: 
// OUTPUT: CPtrArray m_StateChartInfoList; The size of list is the number of descendants.  
// RETURN: 
// NOTE: 
//   
///////////////////////////////////////////////////////////////////////////////////////////
BOOL CStateChartView::InitChartInfoList(HTREEITEM hSelector)
{
	m_StateChartInfoList.RemoveAll();
	
	int nSum = 0;
	m_pStateTree->tree.GetChildSum(hSelector, &nSum);
	
	int i = 0;
	for (; i < nSum; i++)
	{
		STATE_CHART_INFO_T *pNewItem = (STATE_CHART_INFO_T*)new(STATE_CHART_INFO_T);
		ASSERT(NULL != pNewItem);
		
		memset(pNewItem, 0, sizeof(STATE_CHART_INFO_T));
		m_StateChartInfoList.Add(pNewItem);
	}
	return TRUE;
}

/********************************************************************************************
DESCRIPTION: 

INPUT:  
OUTPUT: 
NOTE: 

**********************************************************************************************/
void CStateChartView::ZeroCharInfoList()
{
	int nSize = (int)m_StateChartInfoList.GetSize();

	int i = 0;
	for (; i < nSize; i++)
	{
		memset(m_StateChartInfoList[i], 0, sizeof(STATE_CHART_INFO_T));
	}
}

/********************************************************************************************
DESCRIPTION: 

INPUT:  
OUTPUT: 
NOTE: 

**********************************************************************************************/
BOOL CStateChartView::DestroyChartInfoList()
{
	int nSize = (int)m_StateChartInfoList.GetSize();
	
	int i = 0;
	for (; i < nSize; i++)
	{
		delete((STATE_CHART_INFO_T*)m_StateChartInfoList[i]);
	}
	m_StateChartInfoList.RemoveAll();
	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:  Get all descendant state names of given state. It is a recursive function.
// INPUT:  A state tree item.
// OUTPUT: CStringArray m_StateNameList;
// RETURN: 
// NOTE: 
//   
///////////////////////////////////////////////////////////////////////////////////////////
void CStateChartView::InitStateNameList(HTREEITEM hItem)
{
	if (m_pStateTree->tree.GetItemData(hItem) &(STATE_TREE_APPLICATION_MASK|STATE_TREE_STATE_MASK))
	{
		CString sName = m_pStateTree->tree.GetItemText(hItem);
		sName.Replace("(default)", NULL);
		sName.TrimLeft();
		sName.TrimRight();

		m_StateNameList.Add(sName);
	}
	
	HTREEITEM hChild = m_pStateTree->tree.GetChildItem(hItem);
	while (true)
	{
		if (NULL == hChild)
			break;
		
		if (m_pStateTree->tree.GetItemData(hChild) & (STATE_TREE_APPLICATION_MASK|STATE_TREE_STATE_MASK))
		{
			InitStateNameList(hChild);
		}
		hChild = m_pStateTree->tree.GetNextSiblingItem(hChild);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Get all events which trigger state transitions below the given state: hSrcItem
// INPUT:  1)hSrcItem: The state   2)hSelItem: The current selected item.
// OUTPUT: CStringArray m_EventNameList;
// RETURN: 
// NOTE: Do NOT filter out too deep states.
//   
///////////////////////////////////////////////////////////////////////////////////////////
void CStateChartView::InitEventNameList(HTREEITEM hSrcItem, HTREEITEM hSelItem)
{
	//if (GetStateLevel(hSrcItem, hSelItem) > g_nMaxLevelNum)
	//	return; // exceed the maximum level of state to be drawn
	
	HTREEITEM hEvent = m_pStateTree->tree.GetChildItem(hSrcItem);
	CString sEvent;
	
	while (true)
	{
		if (NULL == hEvent)
			break;
		
		if (m_pStateTree->tree.GetItemData(hEvent) & STATE_TREE_EVENT_ID_MASK)
		{
			HTREEITEM hDestState = m_pStateTree->tree.GetChildItem(hEvent);
			CString sDestStateName = m_pStateTree->tree.GetItemText(hDestState);
			
			if (sDestStateName != STR_INTERNAL_TRAN)
			{
				sDestStateName.Replace("Transit to:", NULL);
				sDestStateName.TrimLeft();
				sDestStateName.TrimRight();
				
				HTREEITEM hDestItem = NULL;
				if (GetDestStateItem(hSelItem, sDestStateName, hDestItem) == FALSE)
				{
					/*
					if (GetStateLevel(hDestItem, hSelItem) > g_nMaxLevelNum)
					{
						hEvent = m_pStateTree->tree.GetNextSiblingItem(hEvent);
						continue; // exceed the maximum level of state to be drawn
					}
					*/
					m_EventNameList.Add(m_pStateTree->tree.GetItemText(hEvent));	
				}
			}
		}
		hEvent = m_pStateTree->tree.GetNextSiblingItem(hEvent);
	}
	
	HTREEITEM hChild = m_pStateTree->tree.GetChildItem(hSrcItem);
	while (true)
	{
		if (NULL == hChild)
			break;
		
		if (m_pStateTree->tree.GetItemData(hChild) & (STATE_TREE_APPLICATION_MASK|STATE_TREE_STATE_MASK))
		{
			InitEventNameList(hChild, hSelItem);
		}
		hChild = m_pStateTree->tree.GetNextSiblingItem(hChild);
	}
	return;
}
///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION: Allocate the memory for state transition info list. The size depends on m_EventNameList. 
// INPUT:  m_EventNameList
// OUTPUT: CPtrArray m_EventLineInfoList;
// RETURN: 
// NOTE: 
//   
///////////////////////////////////////////////////////////////////////////////////////////
void CStateChartView::InitEventInfoList(HTREEITEM hSrcItem, HTREEITEM hSelItem)
{
	m_EventNameList.RemoveAll();
	InitEventNameList(hSrcItem, hSelItem);
	int sum = (int)m_EventNameList.GetSize();
	
	int i = 0;
	for (; i < sum; i++)
	{
		EVENT_LINE_INFO_T *pNewItem = (EVENT_LINE_INFO_T*)new(EVENT_LINE_INFO_T);
		memset(pNewItem, 0, sizeof(EVENT_LINE_INFO_T));
		
	    m_EventLineInfoList.Add(pNewItem);
	}
}

/********************************************************************************************
DESCRIPTION: 

INPUT:  
OUTPUT: 
NOTE: 

**********************************************************************************************/
void CStateChartView::ZeroEventInfoList()
{
	int nSize = (int)m_EventLineInfoList.GetSize();
	
	int i = 0;
	for (; i < nSize; i++)
	{
		memset(m_EventLineInfoList[i], 0, sizeof(EVENT_LINE_INFO_T));
	}
}

/********************************************************************************************
DESCRIPTION: 

INPUT:  
OUTPUT: 
NOTE: 

**********************************************************************************************/
BOOL CStateChartView::DestroyEventInfoList()
{
	int nSize = (int)m_EventLineInfoList.GetSize();
	
	int i = 0;
	for (; i < nSize; i++)
	{
		delete((EVENT_LINE_INFO_T*)m_EventLineInfoList[i]);
	}
	m_EventLineInfoList.RemoveAll();
	return TRUE;
}

void CStateChartView::OnDestroy() 
{
//	m_Font.DeleteObject();
//	m_ListBox.DestroyWindow();
	DestroyChartInfoList();
	DestroyEventInfoList();
	m_EventNameList.RemoveAll();
	m_StateNameList.RemoveAll();
	g_nMaxLevelNum = m_nSavedMaxLevel;
	CScrollView::OnDestroy();
	
	// TODO: Add your message handler code here
	
}
/////////////////////////////////////////////////////////////////////////////
// CStateChartView printing

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

void CStateChartView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: add extra initialization before printing
}

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

/////////////////////////////////////////////////////////////////////////////
// CStateChartView diagnostics

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

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

/////////////////////////////////////////////////////////////////////////////
// CStateChartView message handlers
// StateRect origin position is input data;
// StateRect width and height are output data;

///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:  Calculate the state width and height.
// INPUT:  
// OUTPUT: m_StateChartInfo[nState].nWidth and m_StateChartInfo[nState].nHeight
// RETURN: 
// NOTE: Filter out too deep states.
//   
///////////////////////////////////////////////////////////////////////////////////////////
#define STATE_CHART_MARGIN	32
#define STATE_CHART_HALF_MARGIN (STATE_CHART_MARGIN>>1)

void CStateChartView::CalculateStateChartSize(CDC* pDC, 
									 HTREEITEM hSelItem,
									 HTREEITEM hStart) 
{
	//////////////////////////////////////////////////////////////////////////
//	if (GetStateLevel(hSelItem, hStart) > g_nMaxLevelNum)
//		return; // exceed the maximum level of state to be drawn

	int nChildrenNum=0,nColNum,nRowNum;
	SME_STATE_T nState = GetChartInfoSeqNum(hSelItem);

/*
	CString stemp;
	stemp = m_pStateTree->tree.GetItemText(hSelItem);
*/
	CRect TextRect;
	TextRect.SetRect(0,0,0,0);
	// Calculate the size of rectangle in which draw the text.
	if (m_pStateTree->tree.GetItemData(hSelItem) & STATE_TREE_APPLICATION_MASK)
		pDC->DrawText(m_pStateTree->tree.GetItemText(hSelItem), -1, &TextRect, DT_CALCRECT  | DT_SINGLELINE);
	else
	{
		CString sSelStateName = m_pStateTree->tree.GetItemText(hSelItem);

		sSelStateName.Replace("(default)", NULL);
		sSelStateName.TrimLeft();
		sSelStateName.TrimRight();
		pDC->DrawText(sSelStateName, -1, &TextRect, DT_CALCRECT  | DT_SINGLELINE);
	}

	if (((m_pStateTree->tree.GetItemData(hSelItem)) & STATE_TREE_HAS_DEFAULT_CHILD_STATE) == 0 ||
		(GetStateLevel(hSelItem, hStart) > g_nMaxLevelNum - 1))
	{
		// This state is a leaf. 
		((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nWidth = TextRect.Width() + STATE_CHART_MARGIN;
		((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nHeight = TextRect.Height() + STATE_CHART_MARGIN;

		TRACE("leaf state =%d width=%d height=%d \n",  
			nState, 
			((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nWidth, 
			((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nHeight); 
		return; 
	}
	
	// If state is not a leaf, draw text on the top of state rectangle.
	// The least width and height.
	HTREEITEM hChild = m_pStateTree->tree.GetChildItem(hSelItem);

	while (true)
	{
		if (NULL == hChild)
			break;
		if (m_pStateTree->tree.GetItemData(hChild) & (STATE_TREE_APPLICATION_MASK|STATE_TREE_STATE_MASK))
			nChildrenNum++;
		hChild = m_pStateTree->tree.GetNextSiblingItem(hChild);
	}

	if (nChildrenNum==0)
		return; // To prevent nChildrenNum equal to 0.

	nColNum=(int)(sqrt((double)(nChildrenNum-1)))+1;
    nRowNum=(int)((nChildrenNum-1)/nColNum)+1;

	// Get the max width in every column and max height in every row among child states.
	int *pMaxWidthInColumn = new int[nColNum];
	int *pMaxHeightInRow = new int[nRowNum];
	int i;
	for (i=0; i<nColNum; i++) pMaxWidthInColumn[i] = 0;
	for (i=0; i<nRowNum; i++) pMaxHeightInRow[i] = 0;

	int nChildIdx = 0;
	hChild = m_pStateTree->tree.GetChildItem(hSelItem);
	while (true)
	{
		if (NULL == hChild)
			break;
		if (m_pStateTree->tree.GetItemData(hChild) & (STATE_TREE_APPLICATION_MASK|STATE_TREE_STATE_MASK))
		{
			int nChildXPos = nChildIdx % nColNum;
			int nChildYPos = nChildIdx / nColNum;
			CRect ChildRect(0,0,0,0);
			SME_STATE_T nChildState = GetChartInfoSeqNum(hChild);
			
			CString sChildStateName;
			sChildStateName = m_pStateTree->tree.GetItemText(hChild);
			sChildStateName.Replace("(default)", NULL);
			sChildStateName.TrimLeft();
			sChildStateName.TrimRight();
			
			// Return child state width and height.
			CalculateStateChartSize(pDC, hChild, hStart);
			
			// Keep track of max width in every column.
			if (((STATE_CHART_INFO_T*)m_StateChartInfoList[nChildState])->nWidth > pMaxWidthInColumn[nChildXPos])
				pMaxWidthInColumn[nChildXPos] = ((STATE_CHART_INFO_T*)m_StateChartInfoList[nChildState])->nWidth;
		
			// Keep track of max height in every row.
			if (((STATE_CHART_INFO_T*)m_StateChartInfoList[nChildState])->nHeight > pMaxHeightInRow[nChildYPos])
				pMaxHeightInRow[nChildYPos] = ((STATE_CHART_INFO_T*)m_StateChartInfoList[nChildState])->nHeight;

			nChildIdx++;
		}
		hChild = m_pStateTree->tree.GetNextSiblingItem(hChild);
	}

	// The width of a state is the summary of every column width or the state name drawing width.
	// Text string width
	int nStateWidth1 = TextRect.Width() + STATE_CHART_MARGIN; 
	// Total child state width.
	int nStateWidth2 = STATE_CHART_MARGIN * (nColNum + 1); 
	for (i=0; i<nColNum; i++)
		nStateWidth2 += pMaxWidthInColumn[i];
	((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nWidth = max(nStateWidth1,nStateWidth2);

	// The height of a state is the summary of every row height and the state name drawing height.
	((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nHeight = TextRect.Height() + STATE_CHART_MARGIN * (nRowNum+1);
	for (i=0; i<nRowNum; i++)
		((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nHeight += pMaxHeightInRow[i];

	delete pMaxWidthInColumn;
	delete pMaxHeightInRow;

	TRACE("state =%d width=%d height=%d \n",  
		nState, 
		((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nWidth, 
		((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nHeight); 

	return;
}

///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:  Calculate the column width and row height of the state.
// INPUT:  1) hItem: Handler of current state to be drawn
//         2) hStart: Handler of selected item
// OUTPUT: m_StateChartInfo[nState].nColWidth and m_StateChartInfo[nState].nRowHeight
// RETURN: 
// NOTE: 
//   
///////////////////////////////////////////////////////////////////////////////////////////
void CStateChartView::CalculateColWidthRowHeight(HTREEITEM hItem, HTREEITEM hStart)
{
	if (GetStateLevel(hItem, hStart) > g_nMaxLevelNum)
		return; // exceed the maximum level of state to be drawn
	
	int nChildrenNum=0,nColNum,nRowNum;
	HTREEITEM hChild = m_pStateTree->tree.GetChildItem(hItem);

	while (true)
	{
		if (NULL == hChild)
			break;
		if (m_pStateTree->tree.GetItemData(hChild) & (STATE_TREE_APPLICATION_MASK|STATE_TREE_STATE_MASK))
			nChildrenNum++;
		hChild = m_pStateTree->tree.GetNextSiblingItem(hChild);
	}

	if (nChildrenNum==0)
		return; // To prevent nChildrenNum equal to 0.
	
	nColNum=(int)(sqrt((double)(nChildrenNum-1)))+1;
    nRowNum=(int)((nChildrenNum-1)/nColNum)+1;

	// Get the max width in every column and max height in every row among child states.
	int *pMaxWidthInColumn = new int[nColNum];
	int *pMaxHeightInRow = new int[nRowNum];
	int i;
	for (i=0; i<nColNum; i++) pMaxWidthInColumn[i] = 0;
	for (i=0; i<nRowNum; i++) pMaxHeightInRow[i] = 0;

	int nChildIdx = 0;

	// Get max width in every column and max height in every row.
	hChild = m_pStateTree->tree.GetChildItem(hItem);
	while (true)
	{
		if (NULL == hChild)
			break;
		
		if (m_pStateTree->tree.GetItemData(hChild) & (STATE_TREE_APPLICATION_MASK|STATE_TREE_STATE_MASK))
		{
			int nChildXPos = nChildIdx % nColNum;
			int nChildYPos = nChildIdx / nColNum;

			SME_STATE_T nChildState = GetChartInfoSeqNum(hChild); 
			
			// Keep track of max width in every column.
			if (((STATE_CHART_INFO_T*)m_StateChartInfoList[nChildState])->nWidth > pMaxWidthInColumn[nChildXPos])
				pMaxWidthInColumn[nChildXPos] = ((STATE_CHART_INFO_T*)m_StateChartInfoList[nChildState])->nWidth;
		
			// Keep track of max height in every row.
			if (((STATE_CHART_INFO_T*)m_StateChartInfoList[nChildState])->nHeight > pMaxHeightInRow[nChildYPos])
				pMaxHeightInRow[nChildYPos] = ((STATE_CHART_INFO_T*)m_StateChartInfoList[nChildState])->nHeight;

			nChildIdx ++;
		}
		hChild = m_pStateTree->tree.GetNextSiblingItem(hChild);
	}

	// Assign column width and row height for every state.
	hChild = m_pStateTree->tree.GetChildItem(hItem);
	nChildIdx = 0;
	while (true)
	{
		if (NULL == hChild)
			break;
		
		if (m_pStateTree->tree.GetItemData(hChild) & (STATE_TREE_APPLICATION_MASK|STATE_TREE_STATE_MASK))
		{	
			int nChildXPos = nChildIdx % nColNum;
			int nChildYPos = nChildIdx / nColNum;

			SME_STATE_T nChildState = GetChartInfoSeqNum(hChild); 
			
			if (((STATE_CHART_INFO_T*)m_StateChartInfoList[nChildState])->nColWidth < pMaxWidthInColumn[nChildXPos])
				((STATE_CHART_INFO_T*)m_StateChartInfoList[nChildState])->nColWidth = pMaxWidthInColumn[nChildXPos];
			
			if (((STATE_CHART_INFO_T*)m_StateChartInfoList[nChildState])->nRowHeight < pMaxHeightInRow[nChildYPos])
				((STATE_CHART_INFO_T*)m_StateChartInfoList[nChildState])->nRowHeight = pMaxHeightInRow[nChildYPos];			

			nChildIdx ++;
		}
		hChild = m_pStateTree->tree.GetNextSiblingItem(hChild);
	}

	// Scan child state width and height.
	hChild = m_pStateTree->tree.GetChildItem(hItem);
	while (true)
	{
		if (NULL == hChild)
			break;
		
		if (m_pStateTree->tree.GetItemData(hChild) & (STATE_TREE_APPLICATION_MASK|STATE_TREE_STATE_MASK))
		{	
			CalculateColWidthRowHeight(hChild, hStart);
		}
		hChild = m_pStateTree->tree.GetNextSiblingItem(hChild);
	}

	if (pMaxWidthInColumn) delete pMaxWidthInColumn;
	if (pMaxHeightInRow) delete pMaxHeightInRow;
	pMaxWidthInColumn = 0;
	pMaxHeightInRow = 0;

}

///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:  Draw the state chart.
// INPUT:  1)pDC:
//         2)hItem: Handler of current state to be drawn
//         3)hStart: Handler of selected tree item
//         4)nOriginPosX:
//         5)nOriginPosY:
//
// OUTPUT: 
// RETURN: 
// NOTE: Filter out too deep states.
//   
///////////////////////////////////////////////////////////////////////////////////////////
void CStateChartView::DrawStateChart(CDC* pDC, 
									 HTREEITEM hItem, 
									 HTREEITEM hStart,
									 int nOriginPosX,
									 int nOriginPosY) 
{
//	if (GetStateLevel(hItem, hStart) > g_nMaxLevelNum)
//		return; // exceed the maximum level of state to be drawn
	// Draw this state.
	// The origin position is (nOriginPosX, nOriginPosY) 
	SME_STATE_T nState = GetChartInfoSeqNum(hItem);
	CString sItemName;

	// Get pure name of state or application
	sItemName = m_pStateTree->tree.GetItemText(hItem);
	if (m_pStateTree->tree.GetItemData(hItem) & STATE_TREE_STATE_MASK)
	{
		sItemName.Replace("(default)", NULL);
		sItemName.TrimLeft();
		sItemName.TrimRight();
	}

	CRect StateRect(nOriginPosX, nOriginPosY, 
		nOriginPosX + ((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nWidth,
		nOriginPosY + ((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nHeight); 

	// Draw an arrow for default state.
	if ((m_pStateTree->tree.GetItemData(hItem) & STATE_TREE_DEFAULT_STATE) != 0)
	{
		POINT ArrowPoints[3] = {{nOriginPosX-8,nOriginPosY-6},
			{nOriginPosX,nOriginPosY},
			{nOriginPosX-8,nOriginPosY+6}};
		CRgn ArrowRgn;
		ArrowRgn.CreatePolygonRgn(ArrowPoints,3,WINDING);
		CBrush Brush(RGB(200,0,0)); //grey red
		pDC->FillRgn(&ArrowRgn,&Brush);  
		ArrowRgn.DeleteObject();
	}

	int nArcHeight = ((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nHeight >> 2;

	if (nState == m_nSelStateSeqNo && m_nSelStateSeqNo != -1)
	{
		CBrush newBrush;
		CBrush* oldBrush;
		newBrush.CreateSolidBrush(RGB(255, 255, 100)); //light yellow
		oldBrush=pDC->SelectObject(&newBrush );
		pDC->RoundRect(&StateRect, CPoint(nArcHeight, nArcHeight));	
		pDC->SelectObject(oldBrush);
		newBrush.DeleteObject();
	}
	else
	{
		pDC->RoundRect(&StateRect, CPoint(nArcHeight, nArcHeight));	
	}
/*
	pDC->Pie(CRect(nOriginPosX, nOriginPosY, nOriginPosX+nArcHeight, nOriginPosY+nArcHeight),
		CPoint(nOriginPosX+nArcHeight/2, nOriginPosY),
		CPoint(nOriginPosX, nOriginPosY+nArcHeight/2));
/*
	int nMiddleX = nOriginPosX + m_StateChartInfo[nState].nWidth/2;
	int nRadius = m_StateChartInfo[nState].nHeight >> 2;
	int nLimit = 35; // limit for the boundary of radius	
	nRadius = (nRadius > nLimit)?nLimit:nRadius;
	pDC->Pie(CRect(nMiddleX-nRadius, nOriginPosY-nRadius, nMiddleX+nRadius, nOriginPosY+nRadius),
		CPoint(nMiddleX-nRadius, nOriginPosY),
		CPoint(nMiddleX+nRadius, nOriginPosY));
	
	pDC->Pie(CRect(nMiddleX-nRadius, nOriginPosY+m_StateChartInfo[nState].nHeight-nRadius, nMiddleX+nRadius, nOriginPosY+m_StateChartInfo[nState].nHeight+nRadius),
		CPoint(nMiddleX+nRadius, nOriginPosY+m_StateChartInfo[nState].nHeight),
		CPoint(nMiddleX-nRadius, nOriginPosY+m_StateChartInfo[nState].nHeight));
/*
	// Draw 4 arcs for rectangle angles in a counterclockwise direction.
	int nArcHeight = m_StateChartInfo[nState].nHeight >> 3;
	// Left top
	pDC->Arc(CRect(nOriginPosX, nOriginPosY, nOriginPosX + (nArcHeight<<1), nOriginPosY + (nArcHeight<<1)),
		CPoint(nOriginPosX+nArcHeight, nOriginPosY),
		CPoint(nOriginPosX, nOriginPosY+nArcHeight));
	pDC->MoveTo(CPoint(nOriginPosX, nOriginPosY+nArcHeight));
	pDC->LineTo(CPoint(nOriginPosX, nOriginPosY+m_StateChartInfo[nState].nHeight-nArcHeight));

	// Left bottom
	pDC->Arc(CRect(nOriginPosX, nOriginPosY+m_StateChartInfo[nState].nHeight-(nArcHeight<<1), 
		nOriginPosX + (nArcHeight<<1), nOriginPosY + m_StateChartInfo[nState].nHeight),
		CPoint(nOriginPosX, nOriginPosY+m_StateChartInfo[nState].nHeight-nArcHeight),
		CPoint(nOriginPosX+nArcHeight, nOriginPosY+ m_StateChartInfo[nState].nHeight));
	pDC->MoveTo(CPoint(nOriginPosX+nArcHeight, nOriginPosY+ m_StateChartInfo[nState].nHeight));
	pDC->LineTo(CPoint(nOriginPosX+m_StateChartInfo[nState].nWidth-nArcHeight, nOriginPosY+m_StateChartInfo[nState].nHeight));

	// Right bottom
	pDC->Arc(CRect(nOriginPosX + m_StateChartInfo[nState].nWidth - (nArcHeight<<1), nOriginPosY+ m_StateChartInfo[nState].nHeight-(nArcHeight<<1), 
		nOriginPosX + m_StateChartInfo[nState].nWidth, nOriginPosY + m_StateChartInfo[nState].nHeight),
		CPoint(nOriginPosX+m_StateChartInfo[nState].nWidth-nArcHeight, nOriginPosY+m_StateChartInfo[nState].nHeight),
		CPoint(nOriginPosX+m_StateChartInfo[nState].nWidth, nOriginPosY+m_StateChartInfo[nState].nHeight-nArcHeight));
	pDC->MoveTo(CPoint(nOriginPosX+m_StateChartInfo[nState].nWidth, nOriginPosY+m_StateChartInfo[nState].nHeight-nArcHeight));
	pDC->LineTo(CPoint(nOriginPosX+m_StateChartInfo[nState].nWidth, nOriginPosY+nArcHeight));

	// Right top
	pDC->Arc(CRect(nOriginPosX + m_StateChartInfo[nState].nWidth - (nArcHeight<<1), 
		nOriginPosY, nOriginPosX + m_StateChartInfo[nState].nWidth, nOriginPosY + (nArcHeight<<1)),
		CPoint(nOriginPosX+m_StateChartInfo[nState].nWidth, nOriginPosY+nArcHeight),
		CPoint(nOriginPosX+m_StateChartInfo[nState].nWidth-nArcHeight, nOriginPosY));
	pDC->MoveTo(CPoint(nOriginPosX+m_StateChartInfo[nState].nWidth-nArcHeight, nOriginPosY));
	pDC->LineTo(CPoint(nOriginPosX+nArcHeight, nOriginPosY));
*/

	pDC->TextOut(nOriginPosX + STATE_CHART_HALF_MARGIN,
		nOriginPosY + STATE_CHART_HALF_MARGIN, 
		sItemName);
	// Keep track of state chart left-top position for drawing state trasnsition arrows.
	((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nLeft = nOriginPosX;
	((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nTop = nOriginPosY;

	// exceed the maximum level of state to be drawn or state is a leaf
	if ((m_pStateTree->tree.GetItemData(hItem) & STATE_TREE_HAS_DEFAULT_CHILD_STATE) == 0 ||
		(GetStateLevel(hItem, hStart) > g_nMaxLevelNum - 1))
	{
		// This state is a leaf. 
		return; 
	}

	// If state is not a leaf, draw child states.
	CRect TextRect;
	TextRect.SetRect(0,0,0,0);
	// Calculate the size of rectangle in which draw the text.
	pDC->DrawText(sItemName, -1, &TextRect, DT_CALCRECT  | DT_SINGLELINE);

	int nChildrenNum=0,nColNum,nRowNum;

	HTREEITEM hChild = m_pStateTree->tree.GetChildItem(hItem);
	while (true)
	{
		if (NULL == hChild)
			break;
		if (m_pStateTree->tree.GetItemData(hChild) & (STATE_TREE_APPLICATION_MASK|STATE_TREE_STATE_MASK))
			nChildrenNum++;
		hChild = m_pStateTree->tree.GetNextSiblingItem(hChild);
	}

	nColNum=(int)(sqrt((double)(nChildrenNum-1)))+1;
    nRowNum=(int)((nChildrenNum-1)/nColNum)+1;

	// Determine the original positions among child states.
	int nXCount=0;
	int nYCount=0;
	int nChildOriginX = nOriginPosX + STATE_CHART_MARGIN;
	int nChildOriginY = nOriginPosY + STATE_CHART_MARGIN + TextRect.Height(); 

	hChild = m_pStateTree->tree.GetChildItem(hItem);
	while (true)
	{
		if (NULL == hChild)
			break;

		if (m_pStateTree->tree.GetItemData(hChild) & (STATE_TREE_APPLICATION_MASK|STATE_TREE_STATE_MASK))
		{
			int i = GetChartInfoSeqNum(hChild);

			// Draw the child state
			DrawStateChart(pDC,hChild, hStart, nChildOriginX, nChildOriginY);
			
			nXCount++;
			if (nXCount != nColNum)
			{
				nChildOriginX += ((STATE_CHART_INFO_T*)m_StateChartInfoList[i])->nColWidth + STATE_CHART_MARGIN; 
		}
			else
			{
				// Move to next line.
				// Go back to original X-position.
				nChildOriginX = nOriginPosX + STATE_CHART_MARGIN; 
				nChildOriginY += ((STATE_CHART_INFO_T*)m_StateChartInfoList[i])->nRowHeight + STATE_CHART_MARGIN;
				nXCount =0;
				nYCount ++;
				
			}
		}
		hChild = m_pStateTree->tree.GetNextSiblingItem(hChild);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:  Draw the arrow from source state to destination state.
// INPUT:  1) pDC:
//		   2) hSrcItem: Tree handler item of source state to be drawn
//		   3) hSelItem: Current selected item
// OUTPUT: 
// RETURN: 
// NOTE: If the level of source state is over g_nMaxLevelNum, then all about its status will be
//       hidden.
//   
///////////////////////////////////////////////////////////////////////////////////////////
void CStateChartView::DrawArrow(CDC* pDC, 
								HTREEITEM hSrcItem,
								HTREEITEM hSelItem) 
{
	if (GetStateLevel(hSrcItem, hSelItem) > g_nMaxLevelNum)
		return; // exceed the maximum level of state to be drawn
	SME_STATE_T nSrcState;
	nSrcState = GetChartInfoSeqNum(hSrcItem);
	
	HTREEITEM hEvent = m_pStateTree->tree.GetChildItem(hSrcItem);

	while (true)
	{
		if (NULL == hEvent)
			break;

		if (m_pStateTree->tree.GetItemData(hEvent) & STATE_TREE_EVENT_ID_MASK)
		{
			HTREEITEM hDestState = m_pStateTree->tree.GetChildItem(hEvent);
			CString sDestStateName = m_pStateTree->tree.GetItemText(hDestState);

			if (sDestStateName != STR_INTERNAL_TRAN)
			{
				sDestStateName.Replace("Transit to:", NULL);
				sDestStateName.TrimLeft();
				sDestStateName.TrimRight();

				HTREEITEM hDestItem = NULL;
				if (GetDestStateItem(hSelItem, sDestStateName, hDestItem) == FALSE)
				{
					if (GetStateLevel(hDestItem, hSelItem) > g_nMaxLevelNum)
					{
						hEvent = m_pStateTree->tree.GetNextSiblingItem(hEvent);
						continue; // exceed the maximum level of state to be drawn
					}
					
					SME_STATE_T nDestState;

					nDestState = GetChartInfoSeqNum(hDestItem);

					
					int nSrcX = ((STATE_CHART_INFO_T*)m_StateChartInfoList[nSrcState])->nLeft;
					int nSrcY = ((STATE_CHART_INFO_T*)m_StateChartInfoList[nSrcState])->nTop;
					int nDestX = ((STATE_CHART_INFO_T*)m_StateChartInfoList[nDestState])->nLeft;
					int nDestY = ((STATE_CHART_INFO_T*)m_StateChartInfoList[nDestState])->nTop;
					BOOL bAdjustXYBoth= FALSE;
				
					
					if (nSrcState == nDestState) 
					{
						int nRadius = ((STATE_CHART_INFO_T*)m_StateChartInfoList[nSrcState])->nHeight >> 2;
						int nLimit = 35; // limit for the boundary of radius
			
						nRadius = (nRadius > nLimit)?nLimit:nRadius;
						pDC->Arc(
							CRect(nSrcX-nRadius, nSrcY+nRadius, nSrcX+nRadius, nSrcY-nRadius),
							CPoint(nSrcX+nRadius, nSrcY),
							CPoint(nSrcX, nSrcY+nRadius));
				
						((EVENT_LINE_INFO_T*)m_EventLineInfoList[m_EventLineNo])->nStartX = nSrcX;
						((EVENT_LINE_INFO_T*)m_EventLineInfoList[m_EventLineNo])->nStartY = nSrcY+nRadius;
						((EVENT_LINE_INFO_T*)m_EventLineInfoList[m_EventLineNo])->nEndX = nSrcX+nRadius;
						((EVENT_LINE_INFO_T*)m_EventLineInfoList[m_EventLineNo])->nEndY = nSrcY;
						((EVENT_LINE_INFO_T*)m_EventLineInfoList[m_EventLineNo])->nRadius = nRadius;
						((EVENT_LINE_INFO_T*)m_EventLineInfoList[m_EventLineNo])->nSrcState = nSrcState;
						((EVENT_LINE_INFO_T*)m_EventLineInfoList[m_EventLineNo])->nDestState = nDestState;
						m_EventLineNo++;
	
						nDestX += nRadius;
						nSrcX = nDestX;
						nSrcY = nDestY+40;

						// Draw arrow header the angle is 30.
						const double fDiff = 3.1415926535 * 20 / 180;
						double fXLen = tan(fDiff)*10;
						
						POINT ArrowPoints[3] = {
							{nDestX, nDestY},
							{(int)(nDestX + fXLen), (int)(nDestY - 10)},
							{(int)(nDestX - fXLen),  (int)(nDestY - 10)}};
							CRgn ArrowRgn;
							ArrowRgn.CreatePolygonRgn(ArrowPoints,3,WINDING);
							CBrush Brush(RGB(0,0,0));
							pDC->FillRgn(&ArrowRgn,&Brush);  
							ArrowRgn.DeleteObject();
							
						hEvent = m_pStateTree->tree.GetNextSiblingItem(hEvent);
						continue;
					}
					// case 1: Dest state is on the left of the src state without any intersection.
					if (nSrcX > nDestX + ((STATE_CHART_INFO_T*)m_StateChartInfoList[nDestState])->nWidth)
						// Draw it from right to left. 
						// Draw it from the left side of src state chart to the right side of dest state chart.
						nDestX += ((STATE_CHART_INFO_T*)m_StateChartInfoList[nDestState])->nWidth;
					// case 4: Dest state is below the src state without some intersection.
					else if (nSrcX + ((STATE_CHART_INFO_T*)m_StateChartInfoList[nSrcState])->nWidth  < nDestX)
						// Draw it from left to right. 
						// Draw it from the right side of src state chart to the left side of dest state chart.
						nSrcX += ((STATE_CHART_INFO_T*)m_StateChartInfoList[nSrcState])->nWidth;
					else 
					{
						// case 2: Dest state is on the left the src state with some intersection.
						// case 3: Dest state is on the right the src state with some intersection.
						// Draw it from the center of top/down side of src state chart to the center of down/top side of dest state chart.
						//if (nSrcY > nDestY + (STATE_CHART_INFO_T*)m_StateChartInfoList[nDestState]->nHeight 
						//	|| nSrcY + (STATE_CHART_INFO_T*)m_StateChartInfoList[nSrcState]->nHeight  < nDestY)
						{
							nSrcX += ((STATE_CHART_INFO_T*)m_StateChartInfoList[nSrcState])->nWidth >> 1;
							nDestX += ((STATE_CHART_INFO_T*)m_StateChartInfoList[nDestState])->nWidth >> 1;
					    	bAdjustXYBoth = TRUE;
						}
					};
					
					// case 1: Dest state is above the src state without any intersection.
					if (nSrcY > nDestY + ((STATE_CHART_INFO_T*)m_StateChartInfoList[nDestState])->nHeight)
						// Draw it from bottom to top. 
						// Draw it from the top side of src state chart to the bottom side of dest state chart.
						nDestY += ((STATE_CHART_INFO_T*)m_StateChartInfoList[nDestState])->nHeight;
					// case 4: Dest state is below the src state without some intersection.
					else if (nSrcY + ((STATE_CHART_INFO_T*)m_StateChartInfoList[nSrcState])->nHeight  < nDestY)
						// Draw it from top to down. 
						// Draw it from the bottom side of src state chart to the top side of dest state chart.
						nSrcY += ((STATE_CHART_INFO_T*)m_StateChartInfoList[nSrcState])->nHeight;
					else 
					{
						// case 2: Dest state is above the src state with some intersection.
						// case 3: Dest state is below the src state with some intersection.
						// And there is no inersection in terms of horizontal direction.
						// Draw it from the center of left/right side of src state chart to the center of right/left side of dest state chart.
						//if (nSrcX > nDestX + ((STATE_CHART_INFO_T*)m_StateChartInfoList[nDestState])->nWidth
						//	|| nSrcX + ((STATE_CHART_INFO_T*)m_StateChartInfoList[nSrcState])->nWidth  < nDestX)
						if (!bAdjustXYBoth)
						{
							nSrcY += ((STATE_CHART_INFO_T*)m_StateChartInfoList[nSrcState])->nHeight >> 1;
							nDestY += ((STATE_CHART_INFO_T*)m_StateChartInfoList[nDestState])->nHeight >> 1;
						};
					};
					
					pDC->MoveTo(nSrcX, nSrcY);
					pDC->LineTo(nDestX, nDestY);

					// Test ?????????????
					int nDiff = nSrcX - nDestX;
					if (abs(nDiff) < 4)
					{
						nSrcX = (nSrcX + nDestX)/2;
						nDestX = nSrcX;
					}

					nDiff = nSrcY - nDestY;
					if (abs(nDiff) < 4)
					{
						nSrcY = (nDestY + nSrcY)/2;
						nDestY = nSrcY;
					}
					((EVENT_LINE_INFO_T*)m_EventLineInfoList[m_EventLineNo])->nStartX = nSrcX;
					((EVENT_LINE_INFO_T*)m_EventLineInfoList[m_EventLineNo])->nStartY = nSrcY;
					((EVENT_LINE_INFO_T*)m_EventLineInfoList[m_EventLineNo])->nEndX= nDestX;
					((EVENT_LINE_INFO_T*)m_EventLineInfoList[m_EventLineNo])->nEndY = nDestY;
					((EVENT_LINE_INFO_T*)m_EventLineInfoList[m_EventLineNo])->nRadius = 0;
					((EVENT_LINE_INFO_T*)m_EventLineInfoList[m_EventLineNo])->nSrcState = nSrcState;
					((EVENT_LINE_INFO_T*)m_EventLineInfoList[m_EventLineNo])->nDestState = nDestState;
					m_EventLineNo++;

					// Draw arrow header the angle is 30.
					double fAngle = atan2((double)(nSrcY-nDestY), (double)(nDestX - nSrcX));
					const double fDiff = 3.1415926535 * 20 / 180;

					POINT ArrowPoints[3] = {
						{nDestX, nDestY},
						{(int)(nDestX - cos(fDiff-fAngle)*10), (int)(nDestY -  sin(fDiff-fAngle)*10)},
						{(int)(nDestX - cos(fDiff+fAngle)*10),  (int)(nDestY +  sin(fDiff+fAngle)*10)}};
					CRgn ArrowRgn;
					ArrowRgn.CreatePolygonRgn(ArrowPoints,3,WINDING);
					CBrush Brush(RGB(0,0,0));
					pDC->FillRgn(&ArrowRgn,&Brush);  
					ArrowRgn.DeleteObject();
				}
			}
		}
		hEvent = m_pStateTree->tree.GetNextSiblingItem(hEvent);
	}

	HTREEITEM hChild = m_pStateTree->tree.GetChildItem(hSrcItem);
	while (true)
	{
		if (NULL == hChild)
			break;

		if (m_pStateTree->tree.GetItemData(hChild) & (STATE_TREE_APPLICATION_MASK|STATE_TREE_STATE_MASK))
		{
			DrawArrow(pDC, hChild, hSelItem);
		}
		hChild = m_pStateTree->tree.GetNextSiblingItem(hChild);
	}
	return;
}

///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:  Get sequence number in StateChartInfo array
// INPUT:  1) hItem: Current handler of selected item in state tree
// OUTPUT: 
// RETURN: 
// NOTE: Count the state or application items which is in front of the current state in state 
//       tree.
//       Current state is the start node. Thus, sequence we get is the number from application
///////////////////////////////////////////////////////////////////////////////////////////
int CStateChartView::GetChartInfoSeqNum(HTREEITEM hItem)
{
	int nSum = 0;
	// m_pStateTree->tree.GetSelectedItem() may change, use m_hTopStateItem instead.
	m_pStateTree->tree.CountBeforehandAppState(m_hTopStateItem, hItem, &nSum);
	return nSum;
}


///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:  Draw the arrow from source state to destination state.
// INPUT:  1) hSelItem:			Item handler in state tree to be started from
//         2) sDestStateName:	State name of transited state
//         3) hDestStateItem:   Handler of transited state
// OUTPUT: 1) TRUE 2) FALSE
// RETURN: 
// NOTE: Get destinate state handler if it is a child of selected item in state tree. If it 
//       is not, then the variable hDestStateItem will be assigned null.
//   
///////////////////////////////////////////////////////////////////////////////////////////
BOOL CStateChartView::GetDestStateItem(HTREEITEM hSelItem, 
									   LPCTSTR sDestStateName, 
									   HTREEITEM &hDestStateItem)
{
	if ((m_pStateTree->tree.GetItemData(hSelItem) & (STATE_TREE_APPLICATION_MASK|STATE_TREE_STATE_MASK)) != 0)
	{
		// Get text of current selected tree item 
		CString sCurrentName = "";
		
		// Selected item is a state
		if ((m_pStateTree->tree.GetItemData(hSelItem) & STATE_TREE_STATE_MASK) != 0)
		{
			CString sStateName = m_pStateTree->tree.GetItemText(hSelItem);
			
			if ((m_pStateTree->tree.GetItemData(hSelItem) & STATE_TREE_DEFAULT_STATE) != 0)
				sStateName.Replace("(default)", NULL);
			sStateName.TrimLeft();
			sStateName.TrimRight();
			sCurrentName = sStateName;
		}
		// Selected item is an application 
		if ((m_pStateTree->tree.GetItemData(hSelItem) & STATE_TREE_APPLICATION_MASK) != 0)
		{
			sCurrentName = m_pStateTree->tree.GetItemText(hSelItem);
		}
		
		if(sCurrentName.CompareNoCase(sDestStateName) == 0)
		{
			hDestStateItem = hSelItem;
			return FALSE;
		}
	}
	
	// Recursive check tree items
	HTREEITEM hChild;
	hChild = m_pStateTree->tree.GetChildItem(hSelItem);
	
	while (true)
	{
		if (NULL == hChild)
			break;
		
		if(GetDestStateItem(hChild, sDestStateName, hDestStateItem) == FALSE)
			return FALSE;
		hChild = m_pStateTree->tree.GetNextSiblingItem(hChild);
	}
	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:  Get current state's level based on selected item in state tree
// INPUT:  1) hItem:	Current item to be counted its level
//         2) hStart:	Selected item in state tree.
//        
// OUTPUT: 
// RETURN: 
// NOTE: 
//   
///////////////////////////////////////////////////////////////////////////////////////////
int CStateChartView::GetStateLevel(HTREEITEM hItem, HTREEITEM hStart)
{
	if (hItem == hStart) // the same node in state tree
		return 0;
	
	HTREEITEM hParent;
	int nLevel = 1; // initialize to be one

	hParent = m_pStateTree->tree.GetParentItem(hItem);
	while (true)
	{
		if (NULL == hParent)
			break;
		if (hParent == hStart)
			break;
		nLevel++;
		hParent = m_pStateTree->tree.GetParentItem(hParent);
	}
	return nLevel;
}

void CStateChartView::PostNcDestroy() 
{
	// TODO: Add your specialized code here and/or call the base class
	// do not delete off the heap...	
	// CScrollView::PostNcDestroy();
}

/********************************************************************************************
DESCRIPTION: Pop action list by a menu on the selected object,app, state, state chart.

INPUT:  
OUTPUT: 
NOTE: 

**********************************************************************************************/
void CStateChartView::OnRButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	SME_STATE_T nSelState = m_nSelStateSeqNo;
	HTREEITEM hSelecter = m_pStateTree->tree.GetSelectedItem();

	// When users has left-clicked on item , right click will do nothing
	if (nSelState == -1)
	{
		CPoint local = point;
		CClientDC dc(this);
		OnPrepareDC(&dc);
		dc.DPtoLP(&local);// get local position
		
		SME_STATE_T nState = 0;		
		while(true)
		{
			if (nState >= m_StateChartInfoList.GetSize())
				break;
			
			if (((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nTop == 0)
			{
				// State which exceeds state hierarchy level
				nState++;
				continue;
			}

			if (local.x > ((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nLeft && local.x < (((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nLeft+((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nWidth)
				&& local.y > ((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nTop && local.y < (((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nTop+((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nHeight))

				nSelState = nState;
			nState++;
		} //while true.
	}// if nSelState==-1

	// Update color here to turn yellow when right click on item
	if (m_nSelStateSeqNo != nSelState)
	{
		m_nSelStateSeqNo = nSelState;
		Invalidate();
		UpdateWindow();
	}
	
	CMenu Menu;
	if (nSelState != -1)
	{
		DWORD lMask;

		Menu.CreatePopupMenu();
		lMask = m_pStateTree->tree.GetItemData(hSelecter);
		
		if (lMask & STATE_TREE_APPLICATION_MASK && nSelState == 0)
		{
			Menu.AppendMenu(MF_POPUP, IDD_ADD_STATE, "New State");
			Menu.AppendMenu(MF_POPUP, IDD_DEL_APP, "Remove Application");
			Menu.AppendMenu(MF_POPUP, IDD_ADD_HANDLE, "Add Event Handler");
			Menu.AppendMenu(MF_SEPARATOR);
			Menu.AppendMenu(MF_POPUP, IDD_GOTO_ENTRY, "Go To Entry");
			Menu.AppendMenu(MF_POPUP, IDD_GOTO_EXIT, "Go To Exit");
		}
		else
		{
			Menu.AppendMenu(MF_POPUP, IDD_ADD_STATE, "New State");
			Menu.AppendMenu(MF_POPUP, IDD_DEFLT_CHANGE, "Set As Default");
			Menu.AppendMenu(MF_POPUP, IDD_DEL_STATE, "Delete State");
			Menu.AppendMenu(MF_POPUP, IDD_ADD_HANDLE, "Add Event Handler");
			Menu.AppendMenu(MF_SEPARATOR);
			Menu.AppendMenu(MF_POPUP, IDD_GOTO_ENTRY, "Go To Entry");
			Menu.AppendMenu(MF_POPUP, IDD_GOTO_EXIT, "Go To Exit");

			long nSeq = m_nSelStateSeqNo;
			HTREEITEM hNewSelState;			
			hNewSelState = m_pStateTree->tree.GetSpecifiedState(
				m_pStateTree->tree.GetSelectedApp(hSelecter), 
			    &nSeq
				);
			if (m_pStateTree->tree.GetItemData(hNewSelState) & STATE_TREE_DEFAULT_STATE)
				Menu.EnableMenuItem(IDD_DEFLT_CHANGE, MF_GRAYED);

			// Gray goto exit or entry menu item if they are empty function
			HTREEITEM hEntry;
			HTREEITEM hExit;
			CString sEntry;
			CString sExit;
			
			hEntry = m_pStateTree->tree.GetChildItem(hNewSelState);
			hExit = m_pStateTree->tree.GetNextSiblingItem(hEntry);

			sEntry = m_pStateTree->tree.GetItemText(hEntry);
			sExit = m_pStateTree->tree.GetItemText(hExit);
			sEntry.TrimLeft();
			sEntry.TrimRight();
			sExit.TrimLeft();
			sExit.TrimRight();
			sEntry.Replace("Entry:", NULL);
			sExit.Replace("Exit:", NULL);

			if (sEntry.IsEmpty())
				Menu.EnableMenuItem(IDD_GOTO_ENTRY, MF_GRAYED);
			if (sExit.IsEmpty())
				Menu.EnableMenuItem(IDD_GOTO_EXIT, MF_GRAYED);
		}
}
	
	else // on any state selected.
	{
				
		Menu.CreatePopupMenu();
		Menu.AppendMenu(MF_POPUP, IDD_MENU_SAVEASBMP, "Save as BMP file");
		Menu.AppendMenu(MF_POPUP, IDD_MENU_SAVEASTXT, "Save as TXT file");
	}
		
		Menu.EnableMenuItem(IDD_DRAW_CHART, MF_GRAYED);
		CPoint curPos;
		GetCursorPos(&curPos);		
		Menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_VERTICAL,curPos.x,curPos.y,this);

	CScrollView::OnRButtonDown(nFlags, point);
}


/********************************************************************************************
DESCRIPTION: If the mouse is over a line, display the state transitions. If the mouse is over a
state chart, drag the selected state to be a child of state.

INPUT:  
OUTPUT: 
NOTE: 

m_hItemDrag: The state that is being draging. If it is NULL, there is no state draging.

A special case: When the mouse is out of state chart window, press down, move the mouse to the state chart, 
relase the mouse, m_hItemDrag should be NULL.

**********************************************************************************************/
void CStateChartView::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	m_bMouseMove = FALSE;
	m_dwDragStart = GetTickCount();
	m_hItemDrag = NULL;
	
	// TODO: Add your message handler code here and/or call default
	CPoint local = point;
	CClientDC dc(this);
	OnPrepareDC(&dc);
	dc.DPtoLP(&local);// get local position

	if (PointIsOnLine(local, 5))
		;
	else
	{
		HTREEITEM hSelecter = m_pStateTree->tree.GetSelectedItem();
		SME_STATE_T nState = 0;	
		SME_STATE_T nSelState = -1;	
		
		while(true)
		{
			if (nState >= m_StateChartInfoList.GetSize())
				break;
			
			if (((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nTop == 0)
			{
				// State which exceeds state hierarchy level
				nState++;
				continue;
			}
			
			if (local.x > ((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nLeft && local.x < (((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nLeft+((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nWidth)
				&& local.y > ((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nTop && local.y < (((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nTop+((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nHeight))
				
				nSelState = nState;
			nState++;
		}
		if (m_nSelStateSeqNo != nSelState)
		{
			m_nSelStateSeqNo = nSelState;
			Invalidate();
			UpdateWindow();
		}

		long nSelStatel = nSelState;
		m_hItemDrag = 
			m_pStateTree->tree.GetSpecifiedState(m_pStateTree->tree.GetSelectedItem(),&nSelStatel);
		
		if (m_hItemDrag == NULL)
			return;
	}
/*
	if (g_nIsCriticalSection == 0)
	{
		g_nIsCriticalSection = 1;
		if (PointIsOnLine(local, 5))
		{
			m_ActionNameList.RemoveAll();
			CRect rect;	
			m_ListBox.GetWindowRect(&rect);

			CString    sText; 
			CSize      sz; 
			int        nMaxLen=0; 
			CDC*       pDC  =  m_ListBox.GetDC(); 
			
			for  (int  i= 1; i  <  m_ListBox.GetCount();i++) 
			{ 
				m_ListBox.GetText(i, sText); 
				sz  =  pDC->GetTextExtent(sText); 

				CString sSrcState;
				CString sEventName;
				
				int idx = sText.Find("->");
				ASSERT( -1 != idx);
				sSrcState = sText.Left(idx);

				idx = sText.Find(":");
				sEventName = sText.Right(sText.GetLength()-idx-1);
				
				sSrcState.TrimLeft();
				sSrcState.TrimRight();
				sEventName.TrimLeft();
				sEventName.TrimRight();
				
				for (long j = 0 ; j < m_StateNameList.GetSize(); j++)
				{
					CString sName = m_StateNameList[j];
					
					if (sName == sSrcState)
						break;
				}
				HTREEITEM hNewSelState = 
					m_pStateTree->tree.GetSpecifiedState(m_pStateTree->tree.GetSelectedItem(),&j);

				HTREEITEM hAction;
				HTREEITEM hItem;
				// Get Entry Item and Entry is the first child of current state
				hItem = m_pStateTree->tree.GetChildItem(hNewSelState);
				while (true)
				{
					if (NULL == hItem)
						break;
					if (m_pStateTree->tree.GetItemData(hItem) & STATE_TREE_EVENT_ID_MASK)
					{
						CString sName = m_pStateTree->tree.GetItemText(hItem);
						
						if (sName == sEventName)
						{
							hAction = m_pStateTree->tree.GetChildItem(hItem);
							hAction = m_pStateTree->tree.GetNextSiblingItem(hAction);
							break;				
						}
					}
					hItem = m_pStateTree->tree.GetNextSiblingItem(hItem);
				}

				CString sFuncName;
				sFuncName = m_pStateTree->tree.GetItemText(hAction);
				sFuncName.TrimRight();
				sFuncName.TrimLeft();

				idx = sFuncName.ReverseFind(':');

				if (sFuncName.Right(sFuncName.GetLength()-idx-1).IsEmpty())
				{
					m_ListBox.EnableItem(i, FALSE);
					m_ActionNameList.Add(" ");
				}
				else
					m_ActionNameList.Add(sFuncName);
				if  (sz.cx  >  nMaxLen) 
					nMaxLen  =  sz.cx; 
			} 

			m_ListBox.ReleaseDC(pDC); 
			m_ListBox.MoveWindow(CRect(local.x, local.y, local.x+nMaxLen+ICON_SIZE, local.y+rect.Height()), TRUE);
			m_ListBox.ShowWindow(SW_SHOWNA);
		}	
		else
		{
			m_ListBox.ShowWindow(SW_HIDE);
		}
		g_nIsCriticalSection = 0;
	}
*/	
	CScrollView::OnLButtonDown(nFlags, point);
}

/********************************************************************************************
DESCRIPTION: Check whether a given point is on any line of state transitions.
If meets, pops the state transition list

INPUT:  
OUTPUT: m_ActionNameList;
NOTE: 

**********************************************************************************************/
BOOL CStateChartView::PointIsOnLine(CPoint point, int nError) 
{
	int i = 0;
	int nMatch = 0;

	int nDiff;
/*	if (bIsListBox == TRUE)
	{
		m_ListBox.ResetContent();
		m_ListBox.AddString(_T("State Transition:"), -1);
	}
*/
	BOOL bFound = FALSE;
	BOOL bNeedCheck = FALSE;
	CMenu Menu;
	
	m_ActionNameList.RemoveAll();
	while (true)
	{

		if (i >= m_EventLineNo)
			break;

		if (((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nRadius == 0)
		{
			int nMaxX;
			int nMinX;
			int nMaxY;
			int nMinY;
			
			if (((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nEndX >= ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nStartX)
			{
				nMaxX = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nEndX;
				nMinX = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nStartX;
			}
			else
			{
				nMinX = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nEndX;
				nMaxX = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nStartX;
			}
			
			if (((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nEndY >= ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nStartY)
			{
				nMaxY = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nEndY;
				nMinY = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nStartY;
			}
			else
			{
				nMinY = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nEndY;
				nMaxY = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nStartY;
			}
			
			if (nMaxX == nMinX)
			{
				if (point.y >= nMinY && point.y <= nMaxY)
				{
					nDiff = point.x-nMaxX;
					if (abs(nDiff) < nError)
						bFound = TRUE;
				}
				else
				{
					i++;
					continue;
				}
				
			}
			else if (nMaxY == nMinY)
			{
				if (point.x >= nMinX && point.x <= nMaxX )
				{
					nDiff = point.y-nMaxY;
					if (abs(nDiff) < nError)
						bFound = TRUE;
				}
				else
				{
					i++;
					continue;
				}
			}
			else if (point.x >= nMinX && point.x <= nMaxX && point.y >= nMinY && point.y <= nMaxY)
			{
				double k = (((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nEndY-((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nStartY)/(double)(((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nEndX-((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nStartX);

/*				double Y =  k*(point.x-((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nEndX)+ ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nEndY;
				
				nDiff = (int)Y-point.y;
				if (abs(nDiff) < nError)
					bFound = TRUE;
*/
				// ��P��x0��y0����ֱ��Ax��By��C=0�ľ���: d = |Ax0 +By0 +C| /sqrt(A*A+B*B)
				// In this case: k = -(A/B)
				double Y = k*point.x-point.y
					-k*((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nEndX+((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nEndY;
				double X = k*k+1;
				X = sqrt((double)X);
				nDiff = (abs((int)Y))/(int)X;
				if (abs(nDiff) < nError)
					bFound = TRUE;
			}
		}
		else
		{
			CPoint p;
			p.x = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nStartX;
			p.y = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nStartY -	
				((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nRadius;

			double nDiff;
			double nDist;

			int nMulRes = (point.x-p.x)*(point.x-p.x)+(point.y-p.y)*(point.y-p.y);
			nDist = sqrt((double)nMulRes);

			nDiff = nDist-((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nRadius;

			if (abs((int)nDiff) < nError)
			{
				if (point.x > p.x && point.y >p.y)
					;
				else bFound = TRUE;	
			}
		}
		if (bFound == TRUE)
		{
			bFound = FALSE;
		
			if (nMatch == 0)
			{
				Menu.CreatePopupMenu();
			}

			long nSrcState = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nSrcState;
			long nDestState = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nDestState;
			
			CString sText = m_StateNameList[nSrcState];
			sText += " -> ";
			sText += m_StateNameList[nDestState];
			sText += ":  ";
			sText += m_EventNameList[i];
			Menu.AppendMenu(MF_POPUP, WM_STATE_TRAN_ITEM_0+nMatch, sText);

			HTREEITEM hNewSelState = 
				m_pStateTree->tree.GetSpecifiedState(m_pStateTree->tree.GetSelectedItem(),&nSrcState);
		
			HTREEITEM hAction;
			HTREEITEM hItem;
			// Get Entry Item and Entry is the first child of current state
			hItem = m_pStateTree->tree.GetChildItem(hNewSelState);
			while (true)
			{
				if (NULL == hItem)
					break;
				if (m_pStateTree->tree.GetItemData(hItem) & STATE_TREE_EVENT_ID_MASK)
				{
					CString sName = m_pStateTree->tree.GetItemText(hItem);
					
					if (sName == m_EventNameList[i])
					{
						hAction = m_pStateTree->tree.GetChildItem(hItem);
						hAction = m_pStateTree->tree.GetNextSiblingItem(hAction);
						break;				
					}
				}
				hItem = m_pStateTree->tree.GetNextSiblingItem(hItem);
			}
			
			// Get function name depending on the class or non-class
			CString sFuncName = m_pStateTree->tree.GetItemFuncName(hAction);
			if (sFuncName.IsEmpty())
			{
				Menu.EnableMenuItem(nMatch, MF_GRAYED|MF_BYPOSITION);
			}
			m_ActionNameList.Add(sFuncName);

/*			if (bIsListBox == TRUE)
			{
				int nSrcState = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nSrcState;
				int nDestState = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nDestState;
		
				CString sText = m_StateNameList[nSrcState];
				sText += " -> ";
				sText += m_StateNameList[nDestState];
				sText += ":  ";
				sText += m_EventNameList[i];
				m_ListBox.AddString(_T(sText), 0);
			}
			else
			{
				m_sShowText += m_EventNameList[i];
				m_sShowText += "(";
				int nSrcState = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nSrcState;
				int nDestState = ((EVENT_LINE_INFO_T*)m_EventLineInfoList[i])->nDestState;
				m_sShowText += m_StateNameList[nSrcState];
				m_sShowText += "->";
				m_sShowText += m_StateNameList[nDestState];
				m_sShowText += ")\n";
			}
*/
			nMatch++;
		}
		i++;
	}
	if (nMatch == 0)
		return FALSE;
	
	CPoint curPos;
	GetCursorPos(&curPos);		
	Menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_VERTICAL,curPos.x,curPos.y,this);

	return TRUE;
}

/********************************************************************************************
DESCRIPTION: If the mouse is draging a state, move the selected state to be a child of state.

INPUT:  
OUTPUT: 
NOTE: 

m_hItemDrag: The state that is being draging. If it is NULL, there is no state draging.

A special case: When the mouse is out of state chart window, press down, move the mouse to the state chart, 
relase the mouse, m_hItemDrag should be NULL.

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

	if (m_hItemDrag==NULL)
		return;

	// Confirm not to respond to events by enlarging window
	::SetCursor(::LoadCursor(NULL, IDC_ARROW));	
	
	if (m_bSizeChange == 1)
	{
		m_bSizeChange = -1;
		return;
	}
	CPoint local = point;
	CClientDC dc(this);
	OnPrepareDC(&dc);
	dc.DPtoLP(&local);// get local position
	
	HTREEITEM hSelecter = m_pStateTree->tree.GetSelectedItem();
	SME_STATE_T nState = 0;	
	SME_STATE_T nSelState = -1;	
	
	while(true)
	{
		if (nState >= m_StateChartInfoList.GetSize())
			break;
		
		if (((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nTop == 0)
		{
			// State which exceeds state hierarchy level
			nState++;
			continue;
		}
		
		if (local.x > ((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nLeft && local.x < (((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nLeft+((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nWidth)
			&& local.y > ((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nTop && local.y < (((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nTop+((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nHeight))
			
			nSelState = nState;
		nState++;
	}

	if ((m_bMouseMove == TRUE) && (nSelState != -1))
	{
		long nSelStatel = nSelState;
		HTREEITEM hNewSelState = 
			m_pStateTree->tree.GetSpecifiedState(m_pStateTree->tree.GetSelectedItem(),&nSelStatel);

		if ((m_pStateTree->tree.GetParentItem(m_hItemDrag) != hNewSelState) && (m_hItemDrag != hNewSelState))
		{
			// Confirm whether hNewParent is child of hItem then deny 
			HTREEITEM hParent = m_pStateTree->tree.GetParentItem(hNewSelState);
			BOOL bIsParent = FALSE;
			while(true)
			{
				if (m_pStateTree->tree.GetItemData(hParent) & STATE_TREE_WORKSPACE_MASK)
					break;

				if (hParent == m_hItemDrag)
				{
					bIsParent = TRUE;
					break;
				}
				hParent = m_pStateTree->tree.GetParentItem(hParent);
			}

			if (bIsParent == FALSE)
			{
				m_pStateTree->MoveItem(m_hItemDrag, hNewSelState);
				m_bMouseMove = FALSE;
				//Restore the old selected tree item.
				m_pStateTree->tree.SelectItem(hSelecter);
				
				
				m_EventNameList.RemoveAll();
				m_StateNameList.RemoveAll();
				
				InitStateNameList(hSelecter);
				InitEventNameList(hSelecter, hSelecter);
				
				ZeroCharInfoList();
				ZeroEventInfoList();
				// Eliminate limits on level allowed to be showed
				// g_nMaxLevelNum = 255; ???????
			}
		}
		else //not move to an other state.
		{
			m_hItemDrag=NULL;
			CScrollView::OnLButtonUp(nFlags, point);
			return;
		}
	}
	else //if ((m_bMouseMove == TRUE) && (nSelState != -1))
	{
		m_hItemDrag=NULL;
		CScrollView::OnLButtonUp(nFlags, point);
		return;
	}
	
	m_nSelStateSeqNo = nSelState;
	m_hItemDrag=NULL;
	Invalidate();
	UpdateWindow();

	CScrollView::OnLButtonUp(nFlags, point);
}

void CStateChartView::OnMouseMove(UINT nFlags, CPoint point) 
{
	if(((GetTickCount() - m_dwDragStart) < DRAG_DELAY) || m_hItemDrag == NULL)
		return;


	if (nFlags == MK_LBUTTON)
	{
		::SetCursor(::LoadCursor(NULL, IDC_SIZEALL));
		HTREEITEM hSelecter = m_pStateTree->tree.GetSelectedItem();
		SME_STATE_T nState = 0;	
		SME_STATE_T nSelState = -1;	

		while(true)
		{
			if (nState >= m_StateChartInfoList.GetSize())
				break;
			
			if (((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nTop == 0)
			{
				// State which exceeds state hierarchy level
				nState++;
				continue;
			}
			
			if (point.x > ((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nLeft && point.x < (((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nLeft+((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nWidth)
				&& point.y > ((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nTop && point.y < (((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nTop+((STATE_CHART_INFO_T*)m_StateChartInfoList[nState])->nHeight))
				
				nSelState = nState;
			nState++;
		}
		
		if (nSelState != -1)
		{
			long nSelStatel = nSelState;
			HTREEITEM hNewSelState = 
				m_pStateTree->tree.GetSpecifiedState(m_pStateTree->tree.GetSelectedItem(),&nSelStatel);
			
			if ((m_pStateTree->tree.GetParentItem(m_hItemDrag) != hNewSelState) && (m_hItemDrag != hNewSelState))
			{
				// Confirm whether hNewParent is child of hItem then deny 
				HTREEITEM hParent = m_pStateTree->tree.GetParentItem(hNewSelState);
				BOOL bIsParent = FALSE;
				while(true)
				{
					if (m_pStateTree->tree.GetItemData(hParent) & STATE_TREE_WORKSPACE_MASK)
						break;
					
					if (hParent == m_hItemDrag)
					{
						bIsParent = TRUE;
						break;
					}
					hParent = m_pStateTree->tree.GetParentItem(hParent);
				}
				
				if (bIsParent == TRUE)
					::SetCursor(::LoadCursor(NULL, IDC_NO));
			}
		}
		else
			::SetCursor(::LoadCursor(NULL, IDC_NO));
	}
	// TODO: Add your message handler code here and/or call default
    m_bMouseMove = TRUE;
	CScrollView::OnMouseMove(nFlags, point);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Alex "Question is more important than the answer."

Comments and Discussions