/*++
*******************************************************************************
* Copyright (c) 1999-2000 Digi International Inc., All Rights Reserved        *
*                                                                             *
* This software contains proprietary and confidential information of Digi     *
* International Inc.  By accepting transfer of this copy, Recipient agrees    *
* to retain this software in confidence, to prevent disclosure to others,     *
* and to make no use of this software other than that for which it was        *
* delivered.  This is an unpublished copyrighted work of Digi International   *
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly   *
* prohibited.                                                                 *
*                                                                             *
* Restricted Rights Legend                                                    *
*                                                                             *
* Use, duplication, or disclosure by the Government is subject to             *
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in         *
* Technical Data and Computer Software clause at DFARS 252.227-7031 or        *
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -          *
* Restricted Rights at 48 CFR 52.227-19, as applicable.                       *
*                                                                             *
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343          *
*                                                                             *
*******************************************************************************

Module Name:

	DataSource.cpp

Abstract:

	DataSource.cpp : implementation file

--*/

/*
Class Heirarchy
	CObject - MFC
		CCmdTarget - MFC
			CWnd - MFC
				CView - MFC
					CControlView - MFC
						CListView - MFC
							CListViewEx - ListViewEx.cpp
*/
								class CDataSourceView;
//			CDialog - MFC
					class CBufferSettings;
//				CPropertyPage - MFC
						class CTracePage;
//			CPropertySheet - MFC
					class CTraceSheet; // abstract base class
//					class CFRTraceSheet - config.cpp
//					class CX25TraceSheet - config.cpp
//			CFrameWnd - MFC
//				CMiniFrameWnd - MFC
						class CTraceMiniFrame;

	class CDataSourceControl;
/*
CDataSourceView
	an item is created in CDataSourceView's list control corresponding to an item in
	CMainFrame::m_DataSourceBox. Each item's item data is the CDataSourceControl's
	CDataSource from a CMainFrame::m_DataSourceBox item. Items in the list control
	are owner draw. Drawing doesn't use CListViewEx's drawing because we update the
	data much too frequently and there would be flicker. The data comes from a
	CDataSourceControl or specifically the CDataSource held in the CDataSourceControl.
	One exception the comment column data comes from the registry.
	
	When the user changes the selection in CMainFrame::m_DataSourceBox it is changed
	in CDataSourceView and visa-versa
	
	The CDataSourceView responds to several OnUpdate messages:
		uavStateChange -	the datasource's state changed, an icon is displayed for each 
											state. The states are listed below
			dsiNone - no icon, CDataSource does not display any data
			dsiPlay - a trace is currently running on this CDataSource
			dsiPause - CDataSource's trace is suspended
			dsiStop - CDataSource is not running a trace but is ready to do so
			dsiDisplay -	same as dsiStop but there is trace data ready to display from a
										previous trace
		uavNewComment - the user changed the comment for a CDataSource stored in the
										registry
		uavUpdateIdle - Update data displayed in list control. data display is updated
										by CDiagApp::OnIdle()

CDataSourceControl
	each item in CMainFrame::m_DataSourceBox combobox has a pointer to a 
	CDataSourceControl class as item data. A CDataSourceControl class associates 
	and CDataSource class with a CTraceMiniFrame. The CTraceMiniFrame displays a 
	running trace's statistics.

CBufferSettings
		the buffer settings dialog is how the user changes the trace capture
		frame buffer size and the maximum number of bytes of a frame to copy into
		the buffer. Both of those values are stored in the CURRENT_USER area
		of the registry

CTracePage
	all CPropertyPages used to display trace statistics are derived from
	CTracePage. Member's in CTracePage help to access trace data. Handles
	right-click

CTraceSheet
	class used to hold CTracePage's. Member's in CTraceSheet help CTracePage 
	to access trace data. CTraceSheet is not used directly, you dervice a
	class from it so it can add protocol specific pages like CFRTraceSheet.

CTraceMiniFrame
	frame window class used to create and hold a CTraceSheet

*/

#include "stdafx.h"
#include "Diagnostic.h"

#include "StatDoc.h"
#include "DataSource.h"

#include "DiagDoc.h"
#include "MainFrm.h"

#include <afxpriv.h> // WM_COMMANDHELP

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

static LPCTSTR Status[3] = {	"Deactivated",
                              "N/A",
                              "Running" };


/////////////////////////////////////////////////////////////////////////////
// CBufferSettings dialog


CBufferSettings::CBufferSettings(CWnd* pParent /*=NULL*/)
	: CDialog(CBufferSettings::IDD, pParent)
{
	//{{AFX_DATA_INIT(CBufferSettings)
	m_BufferSize = _T("");
	m_FrameSize = _T("");
	//}}AFX_DATA_INIT
}


void CBufferSettings::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CBufferSettings)
	DDX_CBString(pDX, IDC_BufferSize, m_BufferSize);
	DDV_MaxChars(pDX, m_BufferSize, 8);
	DDX_CBString(pDX, IDC_FrameSize, m_FrameSize);
	DDV_MaxChars(pDX, m_FrameSize, 4);
	//}}AFX_DATA_MAP

	if (m_FrameSize != "Full")
	{
		pDX->PrepareCtrl(IDC_FrameSize);
		DDV_MinMaxInt(pDX, atoi(m_FrameSize), 64, 3000);
	}
}


BEGIN_MESSAGE_MAP(CBufferSettings, CDialog)
	//{{AFX_MSG_MAP(CBufferSettings)
		// NOTE: the ClassWizard will add message map macros here
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CBufferSettings message handlers
/////////////////////////////////////////////////////////////////////////////
// CDataSourceControl

BOOL CDataSourceControl::CreateMiniFrame()
{ 
	m_pMiniFrame = new CTraceMiniFrame(m_pDataSource);
	
	BOOL result = m_pMiniFrame->Create(
			NULL,
			CString("Tracing on ") + m_pDataSource->GetName(),
			MFS_MOVEFRAME | MFS_4THICKFRAME | MFS_SYNCACTIVE | 
			WS_CLIPSIBLINGS | WS_OVERLAPPED | WS_CAPTION,
			CRect(20, 20, 20, 20),
			AfxGetMainWnd()->GetWindow(GW_CHILD));

	if (!result)
		m_pMiniFrame = NULL;
	else
		m_pMiniFrame->BringWindowToTop();

	return result;
};

void CDataSourceControl::KillTraceUI()
{
	if (IsWindow(m_pMiniFrame->GetSafeHwnd()))
		m_pMiniFrame->DestroyWindow();

	m_pMiniFrame = NULL;

	AfxGetMainWnd()->BringWindowToTop();
}

void CDataSourceControl::OnStopTrace()
{
	if (m_pMiniFrame == NULL)
		return;

	CString Name = CString("Trace statisics from - ") + m_pDataSource->GetName();

	// change to indicate trace has stopped
	m_pMiniFrame->SetWindowText(Name);

	// let user close window
	SetWindowLong(m_pMiniFrame->GetSafeHwnd(),
								GWL_STYLE,
								m_pMiniFrame->GetStyle() | WS_SYSMENU);

// redraw window
	m_pMiniFrame->RedrawWindow(	NULL,
															NULL,
															RDW_INVALIDATE | RDW_NOCHILDREN | RDW_FRAME);
}

const CTraceSheet *CDataSourceControl::GetTraceSheet() const
{ 
	return m_pMiniFrame != NULL ? m_pMiniFrame->GetTraceSheet() : NULL;
}

CTraceSheet *CDataSourceControl::GetTraceSheet()
{ 
	return m_pMiniFrame != NULL ? m_pMiniFrame->GetTraceSheet() : NULL;
}

void CDataSourceControl::OnNewDocument()
{ 
	if (m_pMiniFrame != NULL)
		m_pMiniFrame->SendMessage(WM_CLOSE);
	
	m_pMiniFrame = NULL;
}

eDataSourceImage CDataSourceControl::GetState() const
{
	if (m_pDataSource->IsPaused())
		return dsiPause;
	else if (m_pDataSource->IsTracing())
		return dsiPlay;
	else if (m_pDataSource->IsTracePPPSNA())
		return dsiPlay;
	else if (m_pDataSource->TraceDataExists())
		return dsiDisplay;
	else if (m_pDataSource->GetStatus(m_pDataSource->GetDefaultFirmwareDevice()) == dssRunning)
		return dsiStop;
	else
		return dsiNone;
}

/////////////////////////////////////////////////////////////////////////////
// CDataSourceView

IMPLEMENT_DYNCREATE(CDataSourceView, CListViewEx)

CDataSourceView::CDataSourceView()
{
}

CDataSourceView::~CDataSourceView()
{
}


BEGIN_MESSAGE_MAP(CDataSourceView, CListViewEx)
	//{{AFX_MSG_MAP(CDataSourceView)
	ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, OnItemchanged)
	ON_WM_ERASEBKGND()
	ON_NOTIFY_REFLECT(NM_DBLCLK, OnDblclk)
	ON_WM_SIZE()
	ON_CBN_SELCHANGE(IDW_DataSource, OnSelchangeDataSource)
	ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetdispinfo)
	ON_NOTIFY_REFLECT(LVN_KEYDOWN, OnKeydown)
	ON_WM_CONTEXTMENU()
	ON_UPDATE_COMMAND_UI(IDW_DataSource, OnUpdateDataSourceBox)
	//}}AFX_MSG_MAP
	ON_MESSAGE(WM_COMMANDHELP, OnCommandHelp)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDataSourceView drawing

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

/////////////////////////////////////////////////////////////////////////////
// CDataSourceView diagnostics

#ifdef _DEBUG
void CDataSourceView::AssertValid() const
{
	CListViewEx::AssertValid();
}

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

int CDataSourceView::GetSelectedItem() const
{
	return GetListCtrl().GetNextItem(-1, LVNI_SELECTED);
}

void CDataSourceView::SetSelectedItem(int NewSel)
{
	int CurSel = GetSelectedItem();

	// de-select current selection
	if (CurSel > -1 && CurSel != NewSel)
		GetListCtrl().SetItemState(CurSel, 0, LVIS_SELECTED);

	UINT state = LVIS_SELECTED;

	// make selection focused maybe
	if (m_IsActive)
		state |= LVIS_FOCUSED;

// select new item
	GetListCtrl().SetItemState(NewSel, state, state);
}

/////////////////////////////////////////////////////////////////////////////
// CDataSourceView message handlers

// topic ID defined in help file for CDataSourceView
const LPARAM c_DataSourceViewTopicID = 0x5006A;

LRESULT CDataSourceView::OnCommandHelp(WPARAM wParam, LPARAM lParam)
{
	if (lParam == 0)
	{
		lParam = c_DataSourceViewTopicID;
	}
	
	return GetParentFrame()->SendMessage(WM_COMMANDHELP, wParam, lParam);
}

// user change selection in CMainFrame::m_DataSourceBox combobox
// chnage selecion in list control to match
void CDataSourceView::OnSelchangeDataSource()
{
	CComboBox& Box = static_cast<CMainFrame *>(AfxGetMainWnd())->m_DataSourceBox;

	int nIndex = Box.GetCurSel();
	ASSERT(nIndex > CB_ERR);

	SetSelectedItem(nIndex);
}

// set style for CDataSourceView
BOOL CDataSourceView::PreCreateWindow(CREATESTRUCT& cs) 
{
	// TODO: Add your specialized code here and/or call the base class
	cs.style |= LVS_ALIGNLEFT | LVS_REPORT | LVS_SINGLESEL | 
							LVS_NOSORTHEADER | LVS_SHOWSELALWAYS;

	return CListViewEx::PreCreateWindow(cs);
}

void CDataSourceView::OnInitialUpdate() 
{
	CListViewEx::OnInitialUpdate();
	
	// TODO: Add your specialized code here and/or call the base class

	// load and set state item images
	m_iList.Create(IDB_DataSourceState, 15, 0, 0xFFFFFFFF);
	GetListCtrl().SetImageList(&m_iList, LVSIL_STATE);

	// column titles
	LPCTSTR columns[dsvcNumColumns] =	
			{	"Line", "Protocol", "Status", "Alarms", "Errors", 
				"Transmitted frames", "Received frames"
			};

	// default column sizes
	int size[dsvcNumColumns] = { 220, 76, 60, 60, 60, 110, 100 };
	
	// insert columns
	for (short i = 0; i < dsvcNumColumns; i++)
		GetListCtrl().InsertColumn(i, columns[i], LVCFMT_LEFT, size[i], i);

	GetListCtrl().DeleteAllItems(); // for gits and shiggles

	// for each item in CMainFrame::m_DataSourceBox add item to
	// list control
	CComboBox& Box = static_cast<CMainFrame *>(AfxGetMainWnd())->m_DataSourceBox;
	for (i = 0; i < Box.GetCount(); i++)
	{
		CDataSourceControl *pControl = (CDataSourceControl *)Box.GetItemDataPtr(i);

		// text is not stored in the list control
		GetListCtrl().InsertItem(i, LPSTR_TEXTCALLBACK);

		// data will be aquired from CDataSource
		GetListCtrl().SetItemData(i, (DWORD)pControl->GetDataSource());

		// set initial state image
		GetListCtrl().SetItemState(	i,
																INDEXTOSTATEIMAGEMASK((UINT)pControl->GetState()),
																LVIS_STATEIMAGEMASK);
	}

	m_IsActive = TRUE;
	SetSelectedItem(Box.GetCurSel());
	
	// the comment column resizes itself to fit the remaining space in the window
	// implemented in OnSize
	CRect rect;
	GetWindowRect(rect);
	OnSize(SIZE_RESTORED, rect.Width(), rect.Height());

	// CListViewEx will handle making the intire row in the list control selectable
	SetFullRowSel(FALSE);
}

// routine to a handle erasing the background of a CListCtrl
extern BOOL ListViewEraseBkgnd(CDC* pDC, CWnd *parent, CListCtrl& list);

BOOL CDataSourceView::OnEraseBkgnd(CDC* pDC) 
{
	// TODO: Add your message handler code here and/or call default
	
	return ListViewEraseBkgnd(pDC, this, GetListCtrl())
					? TRUE 
					: CListViewEx::OnEraseBkgnd(pDC);
}

// handle update message sent to this view
void CDataSourceView::OnUpdate(CView*, LPARAM lHint, CObject *pHint) 
{
	// TODO: Add your specialized code here and/or call the base class

	// lHint contains the message in it's loword and a
	// index to an item, if needed, in the hiword
	int index = HIWORD(lHint);
	lHint = LOWORD(lHint);

	CListCtrl& m_ListCtrl = GetListCtrl();

	if (lHint & uavStateChange)
		// change data source state image
		m_ListCtrl.SetItemState(index, 
														INDEXTOSTATEIMAGEMASK((UINT)pHint),
														LVIS_STATEIMAGEMASK);

	// user changed the data source comment, redraw list control item
	if (lHint & uavNewComment)
		m_ListCtrl.Update(index);

	// called possible many many times a second
	// drawing is handled this way for speed and to avoid flicker
	if (lHint & uavUpdateIdle)
	{
		CDC *pDC = m_ListCtrl.GetDC();
		CFont *font = pDC->SelectObject(GetFont()); // yes this is neccessary

		// only draw item's that fit in the control's display rect
		int limit = __min(m_ListCtrl.GetItemCount(),
											m_ListCtrl.GetTopIndex() + m_ListCtrl.GetCountPerPage());

		// draw the selected item differently
		int sel = GetSelectedItem();

		// draw items
		for (short i = m_ListCtrl.GetTopIndex(); i < limit; i++)
			DrawItem(pDC, i, sel == i && GetFullRowSel() == TRUE);

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

// user change selection in list control
// chnage selecion in CMainFrame::m_DataSourceBox combobox to match
void CDataSourceView::OnItemchanged(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	// TODO: Add your control notification handler code here
	
	CComboBox& Box = static_cast<CMainFrame *>(AfxGetMainWnd())->m_DataSourceBox;
				
	if ((pNMListView->uNewState & LVIS_SELECTED) > 0)
		Box.SetCurSel(pNMListView->iItem);
	
	*pResult = 0;
}

// handle right-click by putting up a pop-up menu
void CDataSourceView::OnContextMenu(CWnd* /*pWnd*/, CPoint point) 
{
	// TODO: Add your message handler code here
	CMenu menu;
	CMenu *submenu;
	// Load the menu.
	menu.LoadMenu(IDR_TRACETYPE);
	// Get the pop-up menu.
	submenu = menu.GetSubMenu(1);
	// Post the menu.
	if (submenu != NULL)
		submenu->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,
														point.x, point.y,
														AfxGetMainWnd(),NULL);
}

// routine used to quickly draw text without flicker
int DrawText(CDC *dc, LPCSTR txt, CRect rect);

void CDataSourceView::DrawItem(CDC *pDC, int iItem, BOOL Selected)
{
	CListCtrl& List = GetListCtrl();
	
	// bonding rectangle of item
	CRect rect, full;
	List.GetItemRect(iItem, &rect, LVIR_BOUNDS);

	COLORREF clrTextSave, clrBkSave;

	// handle drawing for selected item
	if (Selected)
	{
	// handle selected item when this view is not active
		if (m_IsActive)
		{
			clrTextSave = pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
			clrBkSave = pDC->SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));
		}
		else
		{
			clrTextSave = pDC->GetTextColor();
			clrBkSave = pDC->SetBkColor(PALETTERGB(192, 192, 192));
		}
		
		full = rect;

	// when selected do not draw over the item image
		CRect Icon;
		List.GetItemRect(iItem, &Icon, LVIR_LABEL);
		full.left += Icon.left;
	}

	int LastWidth = List.GetColumnWidth(dsvcName) +
									List.GetColumnWidth(dsvcProtocol);

	// each column is draw one at a time
	// columns 0, 1, and 6 change very seldom if at all
	for (short i = dsvcProtocol+1; i < dsvcNumColumns; i++)
	{
		// draw in column boundary
		rect.left += LastWidth;
		rect.right = rect.left + List.GetColumnWidth(i);
		LastWidth = rect.Width();

		// get item text
		CString Text = List.GetItemText(iItem, i);

		// clip text that doesn't fit in column with elipsis ...
		LPCTSTR pszText = MakeShortString(pDC, Text, LastWidth, 12);

		// draw text
		DrawText(pDC, pszText, rect);
	}

	if (Selected)
	{
		// restore DC 
		pDC->SetTextColor(clrTextSave);
		pDC->SetBkColor(clrBkSave);

		// put that focus rect in there
		pDC->DrawFocusRect(full);
	}
}

void CDataSourceView::OnUpdateDataSourceBox(CCmdUI* pCmdUI)
{
	pCmdUI->Enable();
}

// update display and CMainFrame::m_DataSourceBox when made active
void CDataSourceView::OnActivateView(	BOOL bActivate,
																			CView* pActivateView,
																			CView* pDeactiveView) 
{
	// TODO: Add your specialized code here and/or call the base class
	CListViewEx::OnActivateView(bActivate, pActivateView, pDeactiveView);

	m_IsActive = bActivate;

	if (bActivate)
	{
		CMainFrame *pMainFrame = static_cast<CMainFrame *>(AfxGetMainWnd());
		int sel = GetSelectedItem();
	
		if (sel > CB_ERR)
			pMainFrame->m_DataSourceBox.SetCurSel(sel);
		else
			SetSelectedItem(pMainFrame->m_DataSourceBox.GetCurSel());
	}

	RepaintSelectedItems();
}

const int c_ColumnMargin = 6;

int DrawText(CDC *dc, LPCSTR txt, CRect rect)
{
	CSize sz = dc->GetTextExtent(txt);

	// draw text aligned left with 6 pixel margin
	rect.left += c_ColumnMargin;
	dc->DrawText(txt, -1, rect, DT_SINGLELINE | DT_NOPREFIX | DT_LEFT);
	
	// erase area to right of text
	rect.left = rect.right - __max(0, rect.Width() - sz.cx);
	if (rect.left != rect.right)
	{
		rect.DeflateRect(0, 1);
		dc->FillSolidRect(rect, dc->GetBkColor()/*PALETTERGB(255, 255, 255)*/);
	}

	// return text width
	return sz.cy;
}

// convert DWORD to string but put in KB or MB units
LPSTR NumInBytes(char *buf, DWORD num)
{
	static LPCSTR units[4] = { "", "KB", "MB", "GB" };
	short unit = 0;

	if (num > 500000)
	{
		// can't have too much rounding error
		double n = num;
		while (n > 2048.0)
		{
			n /= 1024.0;
			unit++;
		}

		// convert double to string
		int dec, sign;
		char *buffer = _fcvt(n, (1 << (unit-1)) - 1, &dec, &sign);

		// build returned string
		strncpy(buf, buffer, dec);
		buf[dec] = 0;
		strcat(buf, ".");
		strcat(buf, buffer + dec);
		strcat(buf, units[unit]);
	}
	else
		// display as bytes
		_itoa(num, buf, 10);

	return buf;
}

// take two number and convert them to string with each num seperated
// by a backslash
LPSTR NumSlashNum(char *buf, DWORD num1, DWORD num2)
{
	char *temp = buf;
	
	itoa(num1, buf, 10);
	buf += strlen(buf);
	*buf++ = '/';
	itoa(num2, buf, 10);

	return temp;
}

// if the user pressed Return then do same action as double-click
void CDataSourceView::OnKeydown(NMHDR* pNMHDR, LRESULT* pResult) 
{
	LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR;
	// TODO: Add your control notification handler code here
	if (pLVKeyDow->wVKey == VK_RETURN)
		OnDblclk(pNMHDR, pResult);

	*pResult = 0;
}

// on double-click bring up a stats window on the selected data source
void CDataSourceView::OnDblclk(NMHDR*, LRESULT* pResult) 
{
	// TODO: Add your control notification handler code here
	AfxGetApp()->OnCmdMsg(ID_CAPTURE_DISPLAYSTATISTICS, 0, NULL, NULL);	
	*pResult = 0;
}

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

// data source will set Tx, Rx, Err to a special value
// that value needs to be displayed to user as a string, duh!
inline LPCTSTR DisplayString(LPTSTR Str, __int64& Num)
{
	if (Num == dseNotAvailable)
		Str = "N/A";
	else if (Num == dseLinkDown)
		Str = "Link down";
	else
		sprintf(Str, "%I64d", Num);

	return Str;
}

// return text for item and column asked for
void CDataSourceView::OnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult) 
{
	LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
	// TODO: Add your control notification handler code here

	// only respond to requests for text
	if ((pDispInfo->item.mask & LVIF_TEXT) != LVIF_TEXT)
		return;
	
	// data comes from item data
	CDataSource *ds = (CDataSource *)pDispInfo->item.lParam;

	CString buf;
	LPCTSTR temp = buf.GetBufferSetLength(256);

	// get text for what column
	switch (pDispInfo->item.iSubItem)
	{
		case dsvcName :
			temp = ds->GetName();
			break;
		case dsvcProtocol :
			temp = ds->GetProtocol();
			break;
		case dsvcStatus :
			temp = Status[ds->GetStatus(ds->GetDefaultFirmwareDevice())];
			break;
		case dsvcAlarms :
			temp = ds->m_Alarms;
			break;
		case dsvcErrors :
			temp = DisplayString(buf.GetBuffer(256), ds->m_Err);
			break;
		case dsvcTxFrames :
			temp = DisplayString(buf.GetBuffer(256), ds->m_Tx);
			break;
		case dsvcRxFrames :
			temp = DisplayString(buf.GetBuffer(256), ds->m_Rx);
			break;
	}

	// copy result into control buffer
	strncpy(pDispInfo->item.pszText, temp, pDispInfo->item.cchTextMax);

	*pResult = 0;
}

/////////////////////////////////////////////////////////////////////////////
// CTraceSheet

IMPLEMENT_DYNAMIC(CTraceSheet, CPropertySheet)

CTraceSheet::CTraceSheet(	CTraceData *td,
													UINT nIDCaption,
													CWnd* pParentWnd,
													UINT iSelectPage)
	:CPropertySheet(nIDCaption, pParentWnd, iSelectPage)
{
	m_TraceData = td;
}

CTraceSheet::CTraceSheet(	CTraceData *td,
													LPCTSTR pszCaption,
													CWnd* pParentWnd,
													UINT iSelectPage)
	:CPropertySheet(pszCaption, pParentWnd, iSelectPage)
{
	m_TraceData = td;
}

BEGIN_MESSAGE_MAP(CTraceSheet, CPropertySheet)
	//{{AFX_MSG_MAP(CTraceSheet)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

CTracePage *CTraceSheet::GetTracePage(int i)
{
	return dynamic_cast<CTracePage *>(GetPage(i));
}

CTracePage *CTraceSheet::GetActiveTracePage()
{
	return dynamic_cast<CTracePage *>(GetActivePage());
}

/////////////////////////////////////////////////////////////////////////////
// CTraceSheet message handlers

// send help messages to page
BOOL CTraceSheet::PreTranslateMessage(MSG* pMsg) 
{
	// TODO: Add your specialized code here and/or call the base class
	if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_F1)
	{
		GetActivePage()->SendMessage(WM_COMMANDHELP);
		return TRUE;
	}
	
	return CPropertySheet::PreTranslateMessage(pMsg);
}

/////////////////////////////////////////////////////////////////////////////
// CTracePage property page

IMPLEMENT_DYNCREATE(CTracePage, CPropertyPage)

BEGIN_MESSAGE_MAP(CTracePage, CPropertyPage)
	//{{AFX_MSG_MAP(CTracePage)
	ON_WM_CONTEXTMENU()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTracePage message handlers

BOOL CTracePage::OnSetActive() 
{
	// TODO: Add your specialized code here and/or call the base class
	BOOL result = CPropertyPage::OnSetActive();

	OnUpdate(uavTraceFasterPeriod);
	return result;
}

// right click on page, put up pop-menu
void CTracePage::OnContextMenu(CWnd *, CPoint point) 
{
	// TODO: Add your message handler code here
	CComboBox& Box = static_cast<CMainFrame *>(AfxGetMainWnd())->m_DataSourceBox;
	CTraceMiniFrame *pMiniFrame = (CTraceMiniFrame *)GetParent()->GetParent();
	CMenu menu;
	CMenu *submenu;

	// is the page in a sheet in a mini frame that is being used in a running
	// trace?
	for (short i = 0; i < Box.GetCount(); i++)
	{
		CDataSourceControl *pControl = (CDataSourceControl *)Box.GetItemDataPtr(i);

		if (*pControl == pMiniFrame)
		{
			// yes it is, so set the CMainFrame::m_DataSourceBox combobox selection to the
			// data source corresponding to this page
			Box.SetCurSel(i);

			// Load the menu.
			menu.LoadMenu(IDR_TRACETYPE);
			// Get the pop-up menu.
			submenu = menu.GetSubMenu(1)->GetSubMenu(3);
			break;
		}
	}

	// no, well then this is a page in a sheet in a mini frame owned by a
	// CDiagDoc
	if (i == Box.GetCount())
	{
		POSITION pos = AfxGetApp()->GetFirstDocTemplatePosition();
		CDocTemplate *pTemplate = AfxGetApp()->GetNextDocTemplate(pos);

		pos = pTemplate->GetFirstDocPosition();
		while (pos)
		{
			CDiagDoc *pDoc = (CDiagDoc *)pTemplate->GetNextDoc(pos);

			if (pDoc->IsMyFrame(pMiniFrame))
			{
				// found the doc it belongs to
				pos = pDoc->GetFirstViewPosition();

				pDoc->GetNextView(pos)->GetParentFrame()->BringWindowToTop();
				menu.LoadMenu(IDR_DIAGTYPE);
				submenu = menu.GetSubMenu(1);
				pos = NULL;
			}
		}
	}

	ASSERT(submenu != NULL);

	// Post the menu.
	if (submenu)
		submenu->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,
														point.x, point.y,
														AfxGetMainWnd(),NULL);
}

/////////////////////////////////////////////////////////////////////////////
// CTraceMiniFrame

IMPLEMENT_DYNCREATE(CTraceMiniFrame, CMiniFrameWnd)

CTraceMiniFrame::CTraceMiniFrame()
{
}

CTraceMiniFrame::~CTraceMiniFrame()
{
}


BEGIN_MESSAGE_MAP(CTraceMiniFrame, CMiniFrameWnd)
	//{{AFX_MSG_MAP(CTraceMiniFrame)
	ON_WM_CREATE()
	ON_WM_CLOSE()
	ON_WM_ACTIVATE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTraceMiniFrame message handlers
void CTraceMiniFrame::PostNcDestroy() 
{
	// TODO: Add your specialized code here and/or call the base class
	delete m_TraceSheet;

	CMiniFrameWnd::PostNcDestroy();
}

int CTraceMiniFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CMiniFrameWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	// TODO: Add your specialized creation code here

	// add protocol specific pages to sheet
	m_TraceSheet->AddTracePages();

	// create sheet
	if (!m_TraceSheet->Create(this,
														WS_SYSMENU | WS_CHILD | WS_VISIBLE,
														WS_EX_CONTROLPARENT))
	{
		// failed, so stop trace, I've never seen this happen, so ...
		GetDataSource()->StopTrace();
		return -1;
	}
			
	// resize frame to fit sheet
	CRect rect;

	m_TraceSheet->GetWindowRect(rect);
	rect.bottom += 2 * GetSystemMetrics(SM_CXFRAME);
	rect.bottom += GetSystemMetrics(SM_CYSMCAPTION);
	rect.right += 2 * GetSystemMetrics(SM_CYFRAME);

	// NT 4.0 is slightly different
	if ((BYTE)::GetVersion() < 4)
	{
		DWORD units  = GetDialogBaseUnits();

		rect.bottom += 5 * HIWORD(units) / 8;
	}

	// resize frame
	SetWindowPos(	NULL,
								0, 0,
								rect.Width(), rect.Height(),
								SWP_NOMOVE | SWP_NOZORDER);
	
	// move sheet to origin, it likes to put itself somewhere else
	m_TraceSheet->SetWindowPos(	NULL,
															0, 0,
															0, 0,
															SWP_NOSIZE | SWP_NOZORDER);

	// would be better if new traces place themselves like new documents do
	// i.e. not one on top the other
	CenterWindow();

	ShowWindow(SW_SHOW);

	return 0;
}

void CTraceMiniFrame::OnClose() 
{
	// TODO: Add your message handler code here and/or call default

	// window is not destoyed on close
	if (m_DataSource == NULL || !m_DataSource->IsTracing())
		ShowWindow(SW_HIDE);
}

// set CMainFrame::m_DataSourceBox to item corresponding with
// this mini frame and disable combobox
void CTraceMiniFrame::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) 
{
	CMiniFrameWnd::OnActivate(nState, pWndOther, bMinimized);
	
	// TODO: Add your message handler code here
	CComboBox& Box = static_cast<CMainFrame *>(AfxGetMainWnd())->m_DataSourceBox;

	for (short i = 0; i < Box.GetCount(); i++)
	{
		CDataSourceControl *pControl = (CDataSourceControl *)Box.GetItemDataPtr(i);

		if (this == pControl->GetMiniFrame())
		{
			Box.SetCurSel(i);
			break;
		}
	}
	
	// set focus to tab control for help to work well
	if (nState != WA_INACTIVE && !bMinimized && m_TraceSheet != NULL)
		m_TraceSheet->GetTabControl()->SetFocus();
}
