www.pudn.com > LinePlotTest.zip > LinePlot.cpp


// LinePlot.cpp : implementation file 
///////////////////////////////////////////////////////////////////////////// 
 
///////////////////////////////////////////////////////////////////////////// 
//  CLinePlot, CPlotData - a simple 2D graph control 
// 
//  Author: Paul Grenz 
//  Email:  pgrenz@irlabs.com 
// 
//  You may freely use or modify this code provided this 
//  message is included in all derived versions. 
// 
//  History - 2004/10/28: Initial release to codeguru.com 
// 
// 
//  This class implements a simple 2D graph control which supports 
//  multiple plots, locking and unlocking of axes, printing, and  
//  zooming. 
///////////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "LinePlotTest.h" 
#include "LinePlot.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
///////////////////////////////////////////////////////////////////////////// 
// CPlotData class 
///////////////////////////////////////////////////////////////////////////// 
 
CPlotData::CPlotData(CString szName, COLORREF crColor, int nStyle, std::vector *pvecData) 
{ 
	m_szName = szName; 
  m_nStyle = nStyle; 
  m_oIsDirty = true; 
  m_rcKeyArea.SetRectEmpty(); 
  m_crColor = crColor; 
  m_eXDataMin = 0.0f; 
  m_eXDataMax = 0.0f; 
  m_eYDataMin = 0.0f; 
  m_eYDataMax = 0.0f; 
  m_eYDataMean = 0.0f; 
  m_pptData = NULL; 
  m_pptScreen = NULL; 
  m_uiPointCount = 0; 
 
  //  copy the point data over, calculating max & min. 
  UINT uiPointCount = pvecData->size(); 
  if (uiPointCount>0) 
  { 
    m_pptData = new FLOATPOINT[uiPointCount]; 
    m_pptScreen = new POINT[uiPointCount]; 
    m_uiPointCount = uiPointCount; 
    if (m_pptData!=NULL && m_pptScreen!=NULL) 
    { 
      UpdateData(pvecData); 
    } 
  } 
} 
///////////////////////////////////////////////////////////////////////////// 
 
CPlotData::CPlotData(CString szName, COLORREF crColor, int nStyle, FLOATPOINT *pptData, UINT uiPointCount) 
{ 
	m_szName = szName; 
  m_nStyle = nStyle; 
  m_oIsDirty = true; 
  m_rcKeyArea.SetRectEmpty(); 
  m_crColor = crColor; 
  m_eXDataMin = 0.0f; 
  m_eXDataMax = 0.0f; 
  m_eYDataMin = 0.0f; 
  m_eYDataMax = 0.0f; 
  m_eYDataMean = 0.0f; 
  m_pptData = NULL; 
  m_pptScreen = NULL; 
  m_uiPointCount = 0; 
 
  //  copy the point data over, calculating max & min. 
  if (uiPointCount>0) 
  { 
    m_pptData = new FLOATPOINT[uiPointCount]; 
    m_pptScreen = new POINT[uiPointCount]; 
    m_uiPointCount = uiPointCount; 
    if (m_pptData!=NULL && m_pptScreen!=NULL) 
    { 
      UpdateData(pptData); 
    } 
  } 
} 
///////////////////////////////////////////////////////////////////////////// 
 
CPlotData::CPlotData(const CPlotData &pd) 
{ 
	m_szName = pd.m_szName; 
  m_nStyle = pd.m_nStyle; 
  m_oIsDirty = pd.m_oIsDirty; 
  m_rcKeyArea = pd.m_rcKeyArea; 
  m_crColor = pd.m_crColor; 
  m_eXDataMin = pd.m_eXDataMin; 
  m_eXDataMax = pd.m_eXDataMax; 
  m_eYDataMin = pd.m_eYDataMin; 
  m_eYDataMax = pd.m_eYDataMax; 
  m_eYDataMean = pd.m_eYDataMean; 
  m_pptData = NULL; 
  m_pptScreen = NULL; 
  m_uiPointCount = 0; 
 
  //  copy the point data over, calculating max & min. 
  if (pd.m_uiPointCount>0) 
  { 
    m_pptData = new FLOATPOINT[pd.m_uiPointCount]; 
    m_pptScreen = new POINT[pd.m_uiPointCount]; 
    m_uiPointCount = pd.m_uiPointCount; 
    if (m_pptData!=NULL && m_pptScreen!=NULL) 
    { 
      UpdateData(pd.m_pptData); 
    } 
  } 
} 
///////////////////////////////////////////////////////////////////////////// 
 
CPlotData& CPlotData::operator=(const CPlotData& pd) 
{ 
	m_szName = pd.m_szName; 
  m_nStyle = pd.m_nStyle; 
  m_oIsDirty = pd.m_oIsDirty; 
  m_rcKeyArea = pd.m_rcKeyArea; 
  m_crColor = pd.m_crColor; 
  m_eXDataMin = pd.m_eXDataMin; 
  m_eXDataMax = pd.m_eXDataMax; 
  m_eYDataMin = pd.m_eYDataMin; 
  m_eYDataMax = pd.m_eYDataMax; 
  m_eYDataMean = pd.m_eYDataMean; 
  if (m_pptData!=NULL) 
    delete[] m_pptData; 
  if (m_pptScreen !=NULL) 
    delete[] m_pptScreen; 
  m_uiPointCount = 0; 
 
  //  copy the point data over, calculating max & min. 
  if (pd.m_uiPointCount>0) 
  { 
    m_pptData = new FLOATPOINT[pd.m_uiPointCount]; 
    m_pptScreen = new POINT[pd.m_uiPointCount]; 
    m_uiPointCount = pd.m_uiPointCount; 
    if (m_pptData!=NULL && m_pptScreen!=NULL) 
    { 
      UpdateData(pd.m_pptData); 
    } 
  } 
  return *this; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
bool CPlotData::UpdateData(FLOATPOINT *pptData) 
{ 
  //  init the min and max. 
  m_eXDataMin = pptData[0].x; 
  m_eXDataMax = pptData[0].x; 
  m_eYDataMin = pptData[0].y; 
  m_eYDataMax = pptData[0].y; 
  double xAccum = 0.0f; 
  m_eYDataMean = 0; 
  //  calc the min and max. 
  for (UINT ii=0; iim_eXDataMax) ? (pptData[ii].x) : (m_eXDataMax); 
    m_eYDataMin = (pptData[ii].ym_eYDataMax) ? (pptData[ii].y) : (m_eYDataMax); 
  } 
  //  set the mean. 
  m_eYDataMean = (float)( xAccum / (double)(m_uiPointCount) ); 
  //  force a recalc of the screen polyline. 
  m_oIsDirty = true; 
  return true; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
bool CPlotData::UpdateData(std::vector *pvecData) 
{ 
  //  init the min and max. 
  m_eXDataMin = pvecData->at(0).x; 
  m_eXDataMax = pvecData->at(0).x; 
  m_eYDataMin = pvecData->at(0).y; 
  m_eYDataMax = pvecData->at(0).y; 
  double xAccum = 0.0f; 
  m_eYDataMean = 0; 
  //  calc the min and max. 
  for (UINT ii=0; iiat(ii); 
    xAccum += m_pptData[ii].y; 
    m_eXDataMin = (pvecData->at(ii).xat(ii).x) : (m_eXDataMin); 
    m_eXDataMax = (pvecData->at(ii).x>m_eXDataMax) ? (pvecData->at(ii).x) : (m_eXDataMax); 
    m_eYDataMin = (pvecData->at(ii).yat(ii).y) : (m_eYDataMin); 
    m_eYDataMax = (pvecData->at(ii).y>m_eYDataMax) ? (pvecData->at(ii).y) : (m_eYDataMax); 
  } 
  //  set the mean. 
  m_eYDataMean = (float)( xAccum / (double)(m_uiPointCount) ); 
  //  force a recalc of the screen polyline. 
  m_oIsDirty = true; 
  return true; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
CPlotData::~CPlotData() 
{ 
  Clear(); 
} 
///////////////////////////////////////////////////////////////////////////// 
 
void CPlotData::Clear() 
{ 
  //  clean up the memory. 
  if (m_pptData!=NULL) 
  { 
    delete m_pptData; 
  } 
  if (m_pptScreen!=NULL) 
  { 
    delete m_pptScreen; 
  } 
} 
///////////////////////////////////////////////////////////////////////////// 
 
///////////////////////////////////////////////////////////////////////////// 
//  CLinePlot control 
///////////////////////////////////////////////////////////////////////////// 
 
BEGIN_MESSAGE_MAP(CLinePlot, CWnd) 
	//{{AFX_MSG_MAP(CLinePlot) 
	ON_WM_PAINT() 
	ON_WM_ERASEBKGND() 
	ON_WM_LBUTTONDOWN() 
	ON_WM_MOUSEMOVE() 
	ON_WM_LBUTTONUP() 
	ON_WM_SIZE() 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
///////////////////////////////////////////////////////////////////////////// 
 
CLinePlot::CLinePlot() 
{ 
  m_szXCaption = "X Axis"; 
  m_szYCaption = "Y Axis"; 
  m_nXIntervals = 21; 
  m_nYIntervals = 10; 
  m_uiXPrecision = 1; 
  m_uiYPrecision = 1; 
  m_eXMin = -5.0f; 
  m_eXMax = 100; 
  m_eYMin = -10.0f; 
  m_eYMax = 10.0f; 
  memset(m_prcUndoLimits, 0, LINE_PLOT_UNDO_SIZE*sizeof(FLOATRECT)); 
  m_nUndoIndex = -1; 
  m_eXRatio = 1.0f; 
  m_eYRatio = 1.0f; 
  m_rcXLock.SetRectEmpty(); 
  m_rcYLock.SetRectEmpty(); 
  m_rcKeyArea.SetRectEmpty(); 
  m_rcPlotArea.SetRectEmpty(); 
  m_rcXMin.SetRectEmpty(); 
  m_rcXMax.SetRectEmpty(); 
  m_rcYMin.SetRectEmpty(); 
  m_rcYMax.SetRectEmpty(); 
  m_nCursor = -1; 
  m_nSelected = -1; 
  m_nMouseOverKey = -1; 
  m_uiMouseOver = PLOT_OVER_NOTHING; 
  m_rcZoom.SetRectEmpty(); 
  m_oIsMouseDown = false; 
  m_oHasPassedOverPlot = false; 
  m_oIsXLocked = false; 
  m_oIsYLocked = false; 
 
  //  add one to the zoom undo to start. 
  FLOATRECT rcLimits; 
  rcLimits.left = m_eXMin; 
  rcLimits.top = m_eYMax; 
  rcLimits.right = m_eXMax; 
  rcLimits.bottom = m_eYMin; 
  AddToZoomUndo(rcLimits); 
} 
///////////////////////////////////////////////////////////////////////////// 
 
CLinePlot::~CLinePlot() 
{ 
  Clear(); 
} 
///////////////////////////////////////////////////////////////////////////// 
 
BOOL CLinePlot::Create(const RECT &rect, UINT uiFlags, CWnd *pParentWnd, UINT uiID) 
{ 
	BOOL oOkay = CWnd::Create(NULL, _T("LinePlot"), uiFlags, rect, pParentWnd, uiID); 
 
  if (oOkay==TRUE) 
  { 
    //  create an edit box. 
    DWORD dwEditStyle = WS_BORDER | WS_CHILD | ES_RIGHT | ES_AUTOHSCROLL; //|ES_MULTILINE 
    m_edtDataEntry.Create(dwEditStyle, CRect(0, 0, 1, 1), this, ID_EDT_DATA_ENTRY); 
    //  set the scale font as the font for the edit box. 
    //m_edtDataEntry.SetFont(&m_fonScale); 
  } 
 
  return oOkay; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
BOOL CLinePlot::OnEraseBkgnd(CDC* pDC)  
{ 
  //  to eliminate flicker.... 
	return 1; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
void CLinePlot::DrawPlotData(CDC* pDC, UINT uiIndex, CRect &rcBounds) 
{ 
  //  define a clipping region so we don't 
  //  draw the data outside of the graph area. 
  pDC->IntersectClipRect(rcBounds); 
 
  //  should we recalculate the screen coordinates? 
  if (m_lstPlotData[uiIndex].m_oIsDirty==true) 
  { 
    //  create the screen data points. 
    double xXScreenRatio = rcBounds.Width() / (m_eXMax - m_eXMin); 
    double xYScreenRatio = rcBounds.Height() / (m_eYMax - m_eYMin); 
    for (UINT jj=0; jj < m_lstPlotData[uiIndex].m_uiPointCount; jj++) 
    { 
      m_lstPlotData[uiIndex].m_pptScreen[jj].x = rcBounds.left + 
          (int)( ( m_lstPlotData[uiIndex].m_pptData[jj].x - m_eXMin )  
          * xXScreenRatio ); 
      m_lstPlotData[uiIndex].m_pptScreen[jj].y = rcBounds.bottom -  
          (int)( ( m_lstPlotData[uiIndex].m_pptData[jj].y  - m_eYMin )  
          * xYScreenRatio ); 
    } 
    //  the screen points are up-to-date. 
    m_lstPlotData[uiIndex].m_oIsDirty = false; 
  } 
 
  UINT ii = 0; 
  int nLastWidth = 1; 
 
  switch (m_lstPlotData[uiIndex].m_nStyle) 
  { 
    case LpLine: 
    { 
      //  create a pen to draw with. 
      CPen penPlot(PS_SOLID, 1, m_lstPlotData[uiIndex].m_crColor); 
	    CPen *ppenOld = pDC->SelectObject(&penPlot); 
      //  draw the polyline 
      pDC->Polyline(m_lstPlotData[uiIndex].m_pptScreen,  
          m_lstPlotData[uiIndex].m_uiPointCount); 
      pDC->SelectObject(ppenOld); 
      break; 
    } 
    case LpBar: 
    { 
      //  draw the bars 
      RECT rcBar; 
      //  save the background color. 
      COLORREF crBack = pDC->GetBkColor(); 
      for (ii=0; iiFillSolidRect(&rcBar, m_lstPlotData[uiIndex].m_crColor); 
      } 
      //  restore the background color. 
      pDC->SetBkColor(crBack); 
      break; 
    } 
    default: 
      break; 
  } 
} 
///////////////////////////////////////////////////////////////////////////// 
 
CRect CLinePlot::DrawFramework(CDC *pDC, CRect rcBounds, int nFontHeight, bool oIsPrinting) 
{ 
	CPen *ppenOld; 
	CFont *pfonOld; 
  CFont fonScale; 
  CFont fonHorz; 
  CFont fonVert; 
 
  //  create the fonts to use here. 
  LOGFONT lf; 
  GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &lf); 
  //lf.lfWeight = FW_NORMAL; 
  lf.lfHeight = nFontHeight; 
  fonScale.CreateFontIndirect(&lf); 
  lf.lfWeight = FW_BOLD; 
  lf.lfHeight *= 1.25f; 
  fonHorz.CreateFontIndirect(&lf); 
  lf.lfEscapement = 900; 
  fonVert.CreateFontIndirect(&lf); 
 
  //  create a white brush for the background. 
  CBrush bkBrush(RGB(255, 255, 255)); 
  //  create a gray pen for the ruler lines. 
  CPen penGray(PS_SOLID, 1, RGB(220, 220, 220)); 
  //  create a black brush for the data rectangle. 
  CPen penBlack(PS_SOLID, 1, RGB(0, 0, 0)); 
 
  //  get the height of the caption for the font height. 
  int nCaptionHeight = nFontHeight + 4; 
  int nSmCaptionHeight = (int)(nCaptionHeight * 0.75f); //  GetSystemMetrics(SM_CYSMCAPTION); 
  int nSpacer = nCaptionHeight/2; 
 
  //  calculate the usable area to draw in. 
  CRect rcPlotArea(rcBounds); 
  rcPlotArea.DeflateRect(5, 5, 5, 5); 
 
  //  calculate the rect for the Y caption. 
  CRect rcYCaption(rcPlotArea); 
  rcYCaption.right = rcYCaption.left + nCaptionHeight; 
 
  //  calculate the rect for the X caption. 
  CRect rcXCaption(rcPlotArea); 
  rcXCaption.left = rcXCaption.left + nCaptionHeight; 
  rcXCaption.top = rcXCaption.bottom - nCaptionHeight; 
 
  //  adjust the client area. 
  rcPlotArea.left += nCaptionHeight; // + nSpacer; 
  rcPlotArea.bottom -= nCaptionHeight + nSpacer; 
 
  //  calculate the widest rect for the y scale. 
  CString szTestNumMin; 
  szTestNumMin.Format("%0.f", m_eYMin); 
  int nNumCharsMin = szTestNumMin.GetLength(); 
  CString szTestNumMax; 
  szTestNumMax.Format("%0.f", m_eYMax); 
  int nNumCharsMax = szTestNumMin.GetLength(); 
  int nNumChars = (nNumCharsMinSelectObject(&fonScale); 
  //  calculate the nominal height and width of each scale entry. 
  SIZE siScale = pDC->GetTextExtent(szScaleTest); 
 
  //  create a starting rect for the scale. 
  CRect rcStartScale(rcPlotArea); 
  rcStartScale.right = rcStartScale.left + siScale.cx; 
  rcStartScale.top = rcStartScale.bottom - siScale.cy; 
 
  //  shrink the client rect by the height & width of the scale + a little more.  
  rcPlotArea.left += siScale.cx + nSpacer/2; 
  rcPlotArea.bottom -= siScale.cy + nSpacer/2; 
  rcPlotArea.right -= nCaptionHeight; 
  rcPlotArea.top += nSpacer; 
 
  //  create a rect to hold the stats at the top. 
  CRect rcStats(rcPlotArea); 
  rcStats.bottom = rcStats.top + siScale.cy; 
 
  //  shrink the client rect away from the stats rect. 
  rcPlotArea.top += siScale.cy; 
 
  //  should we calculate a key rect? 
  if (m_lstPlotData.size()>1) 
  { 
    //  what is the longest key? 
    int nMaxKey = 5; 
	  for (int ii=0; ii nMaxKey) ? 
        (m_lstPlotData[ii].m_szName.GetLength()) : (nMaxKey); 
    } 
    int nKeyWidth = (nFontHeight*nMaxKey)/2; 
    //  create a rect to hold the key 
    m_rcKeyArea = rcPlotArea; 
    m_rcKeyArea.left = m_rcKeyArea.right-nKeyWidth; 
    m_rcKeyArea.right = m_rcKeyArea.right+10; 
 
    //  center & shrink the key rect to hold only the number of plots. 
    int nYCenter = (m_rcKeyArea.top + m_rcKeyArea.bottom) / 2; 
    m_rcKeyArea.top = nYCenter - ((siScale.cy+4) * m_lstPlotData.size()) / 2; 
    m_rcKeyArea.bottom = nYCenter + ((siScale.cy+4) * m_lstPlotData.size()) / 2; 
 
    //  shrink the client rect away from the key rect. 
    rcPlotArea.right -= (nKeyWidth+10); 
    //  shrink the stats rect away from the key rect. 
    rcStats.right -= (nKeyWidth+10); 
  } 
 
  //  adjust the space for the x caption so that it is centered. 
  rcXCaption.left = rcPlotArea.left; 
  rcXCaption.right = rcPlotArea.right; 
 
  //  adjust the space for the y caption so that it is centered. 
  rcYCaption.top = rcPlotArea.top; 
  rcYCaption.bottom = rcPlotArea.bottom; 
 
  //  calculate the rect for the min x. 
  m_rcXMin = rcStartScale; 
  m_rcXMin.OffsetRect(nSpacer/2, 0); 
  m_rcXMin.InflateRect(2, 2, 2, 2); 
 
  //  calculate the rect for the max x. 
  m_rcXMax = rcStartScale; 
  m_rcXMax.OffsetRect(rcPlotArea.right - m_rcXMax.right, 0); 
  m_rcXMax.InflateRect(2, 2, 2, 2); 
 
  //  calculate the rect for the x-locked icon in the corner. 
  m_rcXLock.left = rcYCaption.left; 
  m_rcXLock.top = m_rcXMin.top; 
  m_rcXLock.right = rcYCaption.right; 
  m_rcXLock.bottom = m_rcXMin.bottom; 
 
  //  calculate the rect for the min y. 
  m_rcYMin = rcStartScale; 
  m_rcYMin.OffsetRect(0, rcPlotArea.bottom - m_rcYMin.bottom); 
  m_rcYMin.InflateRect(2, 2, 2, 2); 
 
  //  calculate the rect for the max y. 
  m_rcYMax = rcStartScale; 
  m_rcYMax.OffsetRect(0, rcPlotArea.top - m_rcYMax.bottom); 
  m_rcYMax.InflateRect(2, 2, 2, 2); 
 
  //  calculate the rect for the y-locked icon in the corner. 
  m_rcYLock.left = m_rcYMin.right - m_rcXLock.Width(); 
  m_rcYLock.top = rcXCaption.top; 
  m_rcYLock.right = m_rcYMin.right; 
  m_rcYLock.bottom = rcXCaption.bottom; 
 
  //  calculate how big each screen interval is. 
  float eScreenXInterval = (float)(rcPlotArea.Width()) / (float)(m_nXIntervals); 
  float eScreenYInterval = (float)(rcPlotArea.Height()) / (float)(m_nYIntervals); 
 
  //  calculate how big each actual interval is. 
  float eXInterval = (m_eXMax - m_eXMin) / (float)(m_nXIntervals); 
  float eYInterval = (m_eYMax - m_eYMin) / (float)(m_nYIntervals); 
 
  //  now we can calculate the ratio of actual data range  
  //  to screen area. 
  m_eXRatio = (float)(rcPlotArea.Width()) / (m_eXMax - m_eXMin); 
  m_eYRatio = (float)(rcPlotArea.Height()) / (m_eYMax - m_eYMin); 
 
  //  calculate the format for the scale. 
  CString szXFormat; 
  szXFormat.Format("%%0.%uf", m_uiXPrecision); 
  CString szYFormat; 
  szYFormat.Format("%%0.%uf", m_uiYPrecision); 
 
  /* 
  ...... now start drawing! 
  */ 
 
  //  fill the total background. 
  pDC->FillSolidRect(&rcBounds, GetSysColor(COLOR_WINDOW)); 
 
  //  draw the sunken edge around the whole control. 
  if (oIsPrinting==false) 
    pDC->DrawEdge(rcBounds, EDGE_SUNKEN, BF_RECT); 
 
  //  do we have enough room to draw? 
  if (rcPlotArea.left < rcPlotArea.right) 
  { 
 
    //  draw the y caption 
	  pfonOld = pDC->SelectObject(&fonVert); 
    pDC->SetTextAlign(TA_CENTER | TA_BOTTOM); 
    pDC->TextOut(rcYCaption.right, (rcYCaption.top+rcYCaption.bottom)/2, m_szYCaption); 
 
    //  draw the x caption 
	  pfonOld = pDC->SelectObject(&fonHorz); 
    pDC->SetTextAlign(TA_CENTER | TA_BOTTOM); 
    pDC->TextOut((rcXCaption.left+rcXCaption.right)/2, rcXCaption.bottom, m_szXCaption); 
 
    //  setup to draw the scale. 
    pDC->SetTextAlign(TA_RIGHT | TA_BOTTOM); 
    POINT ptXScale; 
    POINT ptYScale; 
    CString szScale; 
    pfonOld = pDC->SelectObject(&fonScale); 
    ppenOld = pDC->SelectObject(&penGray); 
 
    //  draw each x scale entry & line. 
    for (int ii=0; iiTextOut(ptXScale.x, ptXScale.y, szScale); 
      pDC->MoveTo(ptXScale.x, rcPlotArea.top); 
      pDC->LineTo(ptXScale.x, rcPlotArea.bottom); 
    } 
 
    //  is the mouse over the x min? 
    if (m_uiMouseOver&PLOT_OVER_X_MIN && oIsPrinting==false) 
    { 
      pDC->DrawEdge(m_rcXMin, BDR_RAISEDOUTER, BF_RECT); 
    } 
 
    //  is mouse over the x max? 
    if (m_uiMouseOver&PLOT_OVER_X_MAX && oIsPrinting==false) 
    { 
      pDC->DrawEdge(m_rcXMax, BDR_RAISEDOUTER, BF_RECT); 
    } 
 
    //  draw each y scale entry & line. 
    for (int jj=0; jjTextOut(ptYScale.x, ptYScale.y, szScale); 
      pDC->MoveTo(rcPlotArea.left, ptYScale.y); 
      pDC->LineTo(rcPlotArea.right, ptYScale.y); 
    } 
 
    //  is the mouse over the y min? 
    if (m_uiMouseOver&PLOT_OVER_Y_MIN && oIsPrinting==false) 
    { 
      pDC->DrawEdge(m_rcYMin, BDR_RAISEDOUTER, BF_RECT); 
    } 
 
    //  is the mouse over the y max? 
    if (m_uiMouseOver&PLOT_OVER_Y_MAX && oIsPrinting==false) 
    { 
      pDC->DrawEdge(m_rcYMax, BDR_RAISEDOUTER, BF_RECT); 
    } 
 
    pDC->SelectObject(&penBlack); 
 
    //  draw black lines around the data. 
    pDC->MoveTo(rcPlotArea.left, ptYScale.y); 
    pDC->LineTo(rcPlotArea.right, ptYScale.y); 
    pDC->LineTo(rcPlotArea.right, rcPlotArea.bottom); 
    pDC->LineTo(rcPlotArea.left, rcPlotArea.bottom); 
    pDC->LineTo(rcPlotArea.left, ptYScale.y); 
 
    //  is the mouse over the x-lock area? 
    if (m_uiMouseOver&PLOT_OVER_X_LOCK && oIsPrinting==false) 
    { 
      if (m_oIsMouseDown==true) 
        pDC->DrawEdge(m_rcXLock, BDR_SUNKENINNER, BF_RECT); 
      else 
        pDC->DrawEdge(m_rcXLock, BDR_RAISEDOUTER, BF_RECT); 
    } 
 
    //  get the center of the x-lock area. 
    POINT ptXLock = m_rcXLock.CenterPoint(); 
    //  draw the bottom of the lock. 
    CRect rcXLockBottom; 
    rcXLockBottom.left = ptXLock.x - 5; 
    rcXLockBottom.right = ptXLock.x + 6; 
    rcXLockBottom.top = ptXLock.y - 2; 
    rcXLockBottom.bottom = ptXLock.y + 7; 
    pDC->FillSolidRect(rcXLockBottom, RGB(0, 0, 0)); 
    pDC->SetBkColor(GetSysColor(COLOR_WINDOW)); 
    //  draw the lock top. 
    pDC->MoveTo(ptXLock.x - 4, ptXLock.y); 
    pDC->LineTo(ptXLock.x - 2, ptXLock.y - 7); 
    pDC->LineTo(ptXLock.x + 2, ptXLock.y - 7); 
    if (m_oIsXLocked==true) 
      pDC->LineTo(ptXLock.x + 4, ptXLock.y); 
    else 
      pDC->LineTo(ptXLock.x + 4, ptXLock.y-6); 
 
    //  is the mouse over the y-lock area? 
    if (m_uiMouseOver&PLOT_OVER_Y_LOCK && oIsPrinting==false) 
    { 
      if (m_oIsMouseDown==true) 
        pDC->DrawEdge(m_rcYLock, BDR_SUNKENINNER, BF_RECT); 
      else 
        pDC->DrawEdge(m_rcYLock, BDR_RAISEDOUTER, BF_RECT); 
    } 
 
    //  get the center of the y-lock area. 
    POINT ptYLock = m_rcYLock.CenterPoint(); 
    //  draw the bottom of the lock. 
    CRect rcYLockBottom; 
    rcYLockBottom.left = ptYLock.x - 5; 
    rcYLockBottom.right = ptYLock.x + 6; 
    rcYLockBottom.top = ptYLock.y - 2; 
    rcYLockBottom.bottom = ptYLock.y + 7; 
    pDC->FillSolidRect(rcYLockBottom, RGB(0, 0, 0)); 
    pDC->SetBkColor(GetSysColor(COLOR_WINDOW)); 
    //  draw the lock top. 
    pDC->MoveTo(ptYLock.x - 4, ptYLock.y); 
    pDC->LineTo(ptYLock.x - 2, ptYLock.y - 7); 
    pDC->LineTo(ptYLock.x + 2, ptYLock.y - 7); 
    if (m_oIsYLocked==true) 
      pDC->LineTo(ptYLock.x + 4, ptYLock.y); 
    else 
      pDC->LineTo(ptYLock.x + 4, ptYLock.y-6); 
 
    //  should we draw a key? 
    if (m_lstPlotData.size()>1) 
    { 
      //  setup to draw the keys. 
      POINT ptLeftBottom; 
      CString szName; 
      pDC->SetTextAlign(TA_LEFT | TA_BOTTOM); 
      CRect rcKey; 
      CRect rcColor; 
      //  draw all the keys 
	    for (int ii=0; iiFillSolidRect(&rcColor, crKey); 
        pDC->SetBkColor(GetSysColor(COLOR_WINDOW)); 
        //  draw the key text. 
        pDC->TextOut(ptLeftBottom.x, ptLeftBottom.y, szName); 
        //  is this one selected? 
        if (ii==m_nSelected && oIsPrinting==false) 
        { 
          //  draw a rect around the key. 
          pDC->DrawEdge(rcKey, BDR_SUNKENINNER, BF_RECT); 
        } 
        //  is the mouse over a key? 
        else if (ii==m_nMouseOverKey &&  
            (m_uiMouseOver&PLOT_OVER_KEY) && oIsPrinting==false) 
        { 
          //  draw a rect around the key. 
          pDC->DrawEdge(rcKey, BDR_RAISEDOUTER, BF_RECT); 
        } 
      } 
    } 
 
    //  draw the position & stats. 
    CString szPosition; 
    int nCount = m_lstPlotData.size(); 
    if (nCount>0 && m_nSelected>=0 && m_nSelected= 0 && (UINT)(m_nCursor) < m_lstPlotData[m_nSelected].m_uiPointCount) 
      { 
        FLOATPOINT ptDisplay; 
        ptDisplay.x = m_lstPlotData[m_nSelected].m_pptData[m_nCursor].x; 
        ptDisplay.y = m_lstPlotData[m_nSelected].m_pptData[m_nCursor].y; 
        szPosition.Format("Position: %0.3f    Value: %0.3f", 
            ptDisplay.x, ptDisplay.y); 
        pDC->SetTextAlign(TA_LEFT | TA_BOTTOM); 
        pDC->TextOut(rcStats.left+1, rcStats.bottom-1, szPosition); 
      } 
      float eYMin = m_lstPlotData[m_nSelected].m_eYDataMin; 
      float eYMax = m_lstPlotData[m_nSelected].m_eYDataMax; 
      float eYMean = m_lstPlotData[m_nSelected].m_eYDataMean; 
      szPosition.Format("Mean: %0.3f    Min: %0.3f    Max: %0.3f", 
          eYMean, eYMin, eYMax); 
      pDC->SetTextAlign(TA_RIGHT | TA_BOTTOM); 
      pDC->TextOut(rcStats.right-1, rcStats.bottom-1, szPosition); 
    } 
  } 
   
  //  delete the fonts. 
  DeleteObject(&fonScale); 
  DeleteObject(&fonHorz); 
  DeleteObject(&fonVert); 
 
 
  //  return the area we have to draw the plot in. 
  return rcPlotArea; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
void CLinePlot::DrawData(CDC *pDC, CRect rcBounds, bool oIsPrinting)  
{ 
  //  draw all the plots in the correct order. 
	for (int ii=0; iiSelectObject(&penDotBlack); 
 
  //  should we draw a zoom rect? 
  if (m_oIsMouseDown==true && oIsPrinting==false) 
  { 
    pDC->MoveTo(m_rcZoom.left, m_rcZoom.top); 
    pDC->LineTo(m_rcZoom.right, m_rcZoom.top); 
    pDC->LineTo(m_rcZoom.right, m_rcZoom.bottom); 
    pDC->LineTo(m_rcZoom.left, m_rcZoom.bottom); 
    pDC->LineTo(m_rcZoom.left, m_rcZoom.top); 
  } 
 
  //  draw the cursor position. 
  else if (nCount>0 && m_nSelected>=0 &&  
      m_nSelected=0 && (UINT)(m_nCursor) < m_lstPlotData[m_nSelected].m_uiPointCount) 
    { 
      //  set the cursor position on the plot. 
      POINT ptCursor; 
      ptCursor.x = m_lstPlotData[m_nSelected].m_pptScreen[m_nCursor].x; 
      ptCursor.y = m_lstPlotData[m_nSelected].m_pptScreen[m_nCursor].y; 
      pDC->MoveTo(m_rcPlotArea.left, ptCursor.y); 
      pDC->LineTo(m_rcPlotArea.right, ptCursor.y); 
      pDC->MoveTo(ptCursor.x, rcBounds.top); 
      pDC->LineTo(ptCursor.x, rcBounds.bottom); 
    } 
  } 
  pDC->SelectObject(ppenOld); 
} 
///////////////////////////////////////////////////////////////////////////// 
 
void CLinePlot::OnPaint()  
{ 
	CPaintDC dc(this); // device context for painting 
 
  //  get the area to draw in. 
	CRect rcClient; 
	GetClientRect(&rcClient); 
 
  //  get the height of the font to use. 
  int nFontHeight = GetSystemMetrics(SM_CYCAPTION); 
 
  //  create a memory dc for double buffering. 
  CMemDC dcMem(&dc, &rcClient); 
 
	dcMem.SaveDC(); 
 
  //  draw all the support graphics. 
  m_rcPlotArea = DrawFramework(&dcMem, rcClient, nFontHeight, false); 
  //  draw the plots. 
  if (m_rcPlotArea.left < m_rcPlotArea.right) 
  { 
    DrawData(&dcMem, m_rcPlotArea, false); 
  } 
 
	dcMem.RestoreDC(-1); 
} 
///////////////////////////////////////////////////////////////////////////// 
 
void CLinePlot::Print() 
{ 
  // print dialog options 
  CPrintDialog dlgPrint(FALSE, 
      PD_ALLPAGES | PD_USEDEVMODECOPIES | PD_NOPAGENUMS | PD_NOSELECTION | PD_DISABLEPRINTTOFILE, 
      this); 
 
  //  show the print dialog. 
  if(dlgPrint.DoModal() == IDOK) 
  { 
    //  get handle to printer object. 
    HDC hdcPrinter = dlgPrint.GetPrinterDC(); 
    if (hdcPrinter!=NULL) 
    { 
      // get printer dc 
      CDC dcPrinter; 
      dcPrinter.Attach(hdcPrinter); 
 
      //  fill the docinfo structure. 
      DOCINFO diPrinter; 
      ::ZeroMemory(&diPrinter, sizeof(DOCINFO)); 
      diPrinter.cbSize = sizeof(DOCINFO); 
      diPrinter.lpszOutput = NULL; 
      diPrinter.lpszDocName = m_szName.GetBuffer(1); 
 
      if (dcPrinter.StartDoc(&diPrinter) >= 0) 
      { 
        // start page 
        VERIFY(dcPrinter.StartPage() >= 0); 
 
        //  get the printer width. 
        int nPrinterWidth = 0; 
        nPrinterWidth = dcPrinter.GetDeviceCaps(HORZRES); 
 
        //  get the printer height. 
        int nPrinterHeight = 0; 
        nPrinterHeight = dcPrinter.GetDeviceCaps(VERTRES); 
 
        //  claculate a size for the text. 
        int nFontHeight = nPrinterHeight / 80.0f; 
 
        //  get the area to draw in. 
	      CRect rcClient(0, 0, nPrinterWidth, nPrinterHeight); 
 
	      dcPrinter.SaveDC(); 
 
        //  draw all the support graphics. 
        CRect rcPlotArea = DrawFramework(&dcPrinter, rcClient, nFontHeight, true); 
        //  draw the plots. 
        DrawData(&dcPrinter, rcPlotArea, true); 
 
	      dcPrinter.RestoreDC(-1); 
 
        VERIFY(dcPrinter.EndPage() >= 0); 
        VERIFY(dcPrinter.EndDoc() >= 0); 
 
      } 
      else //  there was a printer error of some sort.... 
      { 
        VERIFY(dcPrinter.AbortDoc( ) >= 0 ); 
        AfxMessageBox("Cannot Start Print Job."); 
      } 
      //  clean up 
      dcPrinter.Detach(); 
      VERIFY( DeleteDC(hdcPrinter)); 
    } 
  } 
} 
///////////////////////////////////////////////////////////////////////////// 
 
void CLinePlot::OnSize(UINT nType, int cx, int cy)  
{ 
	CWnd::OnSize(nType, cx, cy); 
  Refresh(); 
} 
///////////////////////////////////////////////////////////////////////////// 
 
void CLinePlot::Refresh() 
{ 
  //  force a recalculation of all the plots next repaint. 
	for (int ii=0; ii m_eXMax) 
      m_eXMax = PlotData.m_eXDataMax; 
    for (UINT ii=0; ii < m_lstPlotData.size(); ii++) 
      m_lstPlotData[ii].m_oIsDirty = true; 
  } 
  //  should the y-axis limits be adjusted? 
  if (m_oIsYLocked == false) 
  { 
	  if (PlotData.m_eYDataMin < m_eYMin) 
      m_eYMin = PlotData.m_eYDataMin; 
    if (PlotData.m_eYDataMax > m_eYMax) 
      m_eYMax = PlotData.m_eYDataMax; 
    for (UINT ii=0; ii < m_lstPlotData.size(); ii++) 
      m_lstPlotData[ii].m_oIsDirty = true; 
  } 
  if (m_hWnd!=NULL) 
	  Invalidate(); 
  EmitSelectionChanged(); 
	return m_nSelected; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
int CLinePlot::Add(CString szName, COLORREF crColor, enumPlotStyle nStyle, std::vector *pvecData) 
{ 
  //  create a new plot 
	CPlotData PlotData(szName, crColor, (int)(nStyle), pvecData); 
  m_lstPlotData.push_back(PlotData); 
  //  change the selected one to this one. 
  m_nSelected = m_lstPlotData.size()-1; 
  //  should the x-axis limits be adjusted? 
  if (m_oIsXLocked == false) 
  { 
	  if (PlotData.m_eXDataMin < m_eXMin) 
      m_eXMin = PlotData.m_eXDataMin; 
    if (PlotData.m_eXDataMax > m_eXMax) 
      m_eXMax = PlotData.m_eXDataMax; 
    for (UINT ii=0; ii < m_lstPlotData.size(); ii++) 
      m_lstPlotData[ii].m_oIsDirty = true; 
  } 
  //  should the y-axis limits be adjusted? 
  if (m_oIsYLocked == false) 
  { 
	  if (PlotData.m_eYDataMin < m_eYMin) 
      m_eYMin = PlotData.m_eYDataMin; 
    if (PlotData.m_eYDataMax > m_eYMax) 
      m_eYMax = PlotData.m_eYDataMax; 
    for (UINT ii=0; ii < m_lstPlotData.size(); ii++) 
      m_lstPlotData[ii].m_oIsDirty = true; 
  } 
  if (m_hWnd!=NULL) 
	  Invalidate(); 
  EmitSelectionChanged(); 
	return m_nSelected; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
bool CLinePlot::Remove(int nIndex) 
{ 
  //  remove a plot. 
  bool oRetVal = false; 
  if (nIndex<0 || nIndex>=m_lstPlotData.size()) 
    return false; 
  else 
  { 
    m_lstPlotData.erase(m_lstPlotData.begin()+nIndex); 
    //  change the selected by one, if necessary. 
    if (m_nSelected >= nIndex) 
    { 
      m_nSelected--; 
      int nMaxIndex = m_lstPlotData.size() - 1; 
      //  make sure the selection makes sense. 
      if (m_nSelected > nMaxIndex) 
        m_nSelected = nMaxIndex; 
      else if (m_nSelected < 0) 
        m_nSelected = 0; 
      EmitSelectionChanged(); 
    } 
    if (m_hWnd!=NULL) 
	    Invalidate(); 
    return true; 
  } 
} 
///////////////////////////////////////////////////////////////////////////// 
 
void CLinePlot::OnMouseMove(UINT nFlags, CPoint point)  
{ 
  UINT uiMouseOver = PLOT_OVER_NOTHING; 
  int nMouseOverKey = -1; 
 
  //  are we drawing a zoom rect? 
  if (m_oIsMouseDown==true) 
  { 
    if (m_rcPlotArea.PtInRect(point)==TRUE) 
      m_oHasPassedOverPlot = true; 
    m_rcZoom.right = point.x; 
    m_rcZoom.bottom = point.y; 
    if (m_hWnd!=NULL) 
      Invalidate(); 
  } 
 
  //  is the mouse over the x min button? 
  else if (m_rcXMin.PtInRect(point)==TRUE) 
  { 
    uiMouseOver |= PLOT_OVER_X_MIN; 
  } 
  //  is the mouse over the x max button? 
  if (m_rcXMax.PtInRect(point)==TRUE) 
  { 
    uiMouseOver |= PLOT_OVER_X_MAX; 
  } 
  //  is the mouse over the y min button? 
  if (m_rcYMin.PtInRect(point)==TRUE) 
  { 
    uiMouseOver |= PLOT_OVER_Y_MIN; 
  } 
  //  is the mouse over the y max button? 
  if (m_rcYMax.PtInRect(point)==TRUE) 
  { 
    uiMouseOver |= PLOT_OVER_Y_MAX; 
  } 
  //  is the mouse over the x-lock button? 
  else if (m_rcXLock.PtInRect(point)==TRUE) 
  { 
    uiMouseOver |= PLOT_OVER_X_LOCK; 
  } 
  //  is the mouse over the y-lock button? 
  else if (m_rcYLock.PtInRect(point)==TRUE) 
  { 
    uiMouseOver |= PLOT_OVER_Y_LOCK; 
  } 
  //  is the mouse over the key area? 
  nMouseOverKey = -1; 
  CRect rcKey; 
  for (int ii=0; ii0 && m_nSelected>=0 && m_nSelected=0 && (m_uiMouseOver&PLOT_OVER_KEY)) 
  { 
    if (m_nSelected!=m_nMouseOverKey) 
    { 
      m_nSelected = m_nMouseOverKey; 
      if (m_hWnd!=NULL) 
        Invalidate(); 
      // let the parent know that the selection changed. 
      EmitSelectionChanged(); 
    } 
  } 
 
  //  is the mouse over the x-lock button? 
  else if (m_uiMouseOver&PLOT_OVER_X_LOCK) 
  { 
    if (m_hWnd!=NULL) 
      Invalidate(); 
  } 
  //  is the mouse over the y-lock button? 
  else if (m_uiMouseOver&PLOT_OVER_Y_LOCK) 
  { 
    if (m_hWnd!=NULL) 
      Invalidate(); 
  } 
 
	CWnd::OnLButtonDown(nFlags, point); 
} 
///////////////////////////////////////////////////////////////////////////// 
 
void CLinePlot::OnLButtonUp(UINT nFlags, CPoint point)  
{ 
  CString szFormat; 
  CString szData; 
 
  //  are we drawing a zoom rect? 
  if (m_oIsMouseDown==true && m_oHasPassedOverPlot==true) 
  { 
    //  we are no longer drawing a zoom rect. 
    m_rcZoom.right = point.x; 
    m_rcZoom.bottom = point.y; 
 
    //  was it drawn backwards or forwards? 
    if (m_rcZoom.bottomm_eXMax) ? (m_eXMax) : (eXNewMax); 
 
        //  assign to variables 
        m_eXMin = eXNewMin; 
        m_eXMax = eXNewMax; 
      } 
 
      //  if the y-axis is locked, do nothing. 
      if (m_oIsYLocked==false) 
      { 
        //  determine the ratio of the ranges. 
        //  assume the inside dimensions are correct. 
        float eYDataRange = m_eYMax - m_eYMin + 1.0f; 
        float eYPixelRange = (float)(m_rcPlotArea.Height()); 
        float eYRatio = eYDataRange / eYPixelRange; 
 
        //  get distance of bottom edge of rect from inside bottom edge. 
        float eYNewMin = m_eYMin + (m_rcPlotArea.bottom - m_rcZoom.bottom) * eYRatio; 
        //  make sure the new min is not less than the current min. 
        eYNewMin = (eYNewMinm_eYMax) ? (m_eYMax) : (eYNewMax); 
 
        //  assign to variables 
        m_eYMin = eYNewMin; 
        m_eYMax = eYNewMax; 
      } 
 
      //  force a recalculation all the plots next time. 
      Refresh(); 
      //  tell the parent that the limits are different. 
      EmitLimitsChanged(); 
    } 
  } 
 
  //  not drawing a zoom rect now. 
  m_oIsMouseDown = false; 
  m_oHasPassedOverPlot = false; 
 
  //  is the mouse over the x min? 
  if ((m_uiMouseOver & PLOT_OVER_X_MIN) && m_oIsXLocked!=true) 
  { 
    szFormat.Format("%%0.%uf", m_uiXPrecision); 
    szData.Format(szFormat, m_eXMin); 
    m_edtDataEntry.SetWindowText(szData); 
    m_edtDataEntry.MoveWindow(m_rcXMin); 
    m_edtDataEntry.ShowWindow(TRUE); 
    m_edtDataEntry.SetFocus(); 
  } 
 
  //  is the mouse over the x max? 
  else if ((m_uiMouseOver & PLOT_OVER_X_MAX) && m_oIsXLocked!=true) 
  { 
    szFormat.Format("%%0.%uf", m_uiXPrecision); 
    szData.Format(szFormat, m_eXMax); 
    m_edtDataEntry.SetWindowText(szData); 
    m_edtDataEntry.MoveWindow(m_rcXMax); 
    m_edtDataEntry.ShowWindow(TRUE); 
    m_edtDataEntry.SetFocus(); 
  } 
 
  //  is the mouse over the y min? 
  else if ((m_uiMouseOver & PLOT_OVER_Y_MIN) && m_oIsYLocked!=true) 
  { 
    szFormat.Format("%%0.%uf", m_uiYPrecision); 
    szData.Format(szFormat, m_eYMin); 
    m_edtDataEntry.SetWindowText(szData); 
    m_edtDataEntry.MoveWindow(m_rcYMin); 
    m_edtDataEntry.ShowWindow(TRUE); 
    m_edtDataEntry.SetFocus(); 
  } 
 
  //  is the mouse over the y max? 
  else if ((m_uiMouseOver & PLOT_OVER_Y_MAX) && m_oIsYLocked!=true) 
  { 
    szFormat.Format("%%0.%uf", m_uiYPrecision); 
    szData.Format(szFormat, m_eYMax); 
    m_edtDataEntry.SetWindowText(szData); 
    m_edtDataEntry.MoveWindow(m_rcYMax); 
    m_edtDataEntry.ShowWindow(TRUE); 
    m_edtDataEntry.SetFocus(); 
  } 
 
  //  is the mouse over the x-lock button? 
  else if (m_uiMouseOver&PLOT_OVER_X_LOCK) 
  { 
    m_oIsXLocked = !m_oIsXLocked; 
    if (m_hWnd!=NULL) 
      Invalidate(); 
  } 
  //  is the mouse over the y-lock button? 
  else if (m_uiMouseOver&PLOT_OVER_Y_LOCK) 
  { 
    m_oIsYLocked = !m_oIsYLocked; 
    if (m_hWnd!=NULL) 
      Invalidate(); 
  } 
 
  else 
  { 
    //  if there is an edit window visible, hide it 
    HideEditWindow(); 
    if (m_hWnd!=NULL) 
      Invalidate(); 
  } 
 
  CWnd::OnLButtonUp(nFlags, point); 
} 
///////////////////////////////////////////////////////////////////////////// 
     
void CLinePlot::EmitSelectionChanged() 
{ 
  CWnd *pwndParent = GetParent(); 
  NMHDR msgNotif; 
  msgNotif.code = NM_PLOT_SEL_CHANGE; 
  msgNotif.hwndFrom = this->GetSafeHwnd(); 
  msgNotif.idFrom = this->GetDlgCtrlID(); 
  pwndParent->SendMessage(WM_NOTIFY, 0, (UINT)&msgNotif); 
} 
///////////////////////////////////////////////////////////////////////////// 
     
void CLinePlot::EmitLimitsChanged() 
{ 
  CWnd *pwndParent = GetParent(); 
  NMHDR msgNotif; 
  msgNotif.code = NM_PLOT_LIMITS_CHANGE; 
  msgNotif.hwndFrom = this->GetSafeHwnd(); 
  msgNotif.idFrom = this->GetDlgCtrlID(); 
  pwndParent->SendMessage(WM_NOTIFY, 0, (UINT)&msgNotif); 
} 
///////////////////////////////////////////////////////////////////////////// 
     
void CLinePlot::EmitMouseMove() 
{ 
  CWnd *pwndParent = GetParent(); 
  NMHDR msgNotif; 
  msgNotif.code = NM_PLOT_MOUSE_MOVE; 
  msgNotif.hwndFrom = this->GetSafeHwnd(); 
  msgNotif.idFrom = this->GetDlgCtrlID(); 
  pwndParent->SendMessage(WM_NOTIFY, 0, (UINT)&msgNotif); 
} 
///////////////////////////////////////////////////////////////////////////// 
 
void CLinePlot::AddToZoomUndo(FLOATRECT &rcLimits) 
{ 
  //  is the undo list full? 
  if (m_nUndoIndex==LINE_PLOT_UNDO_SIZE-1) 
  { 
    //  move everything back one... 
    for (UINT ii = 0; ii0) ?  
    (m_nUndoIndex-1) : (m_nUndoIndex); 
 
  return m_prcUndoLimits[nCurrIndex]; 
} 
///////////////////////////////////////////////////////////////////////////// 
     
void CLinePlot::HideEditWindow() 
{ 
  //  is the edit window visible? 
  if (m_edtDataEntry.IsWindowVisible()==TRUE) 
  { 
    //  hide the edit window. 
    m_edtDataEntry.ShowWindow(FALSE); 
 
    //  get the value from the window. 
    CString szText; 
    m_edtDataEntry.GetWindowText(szText); 
    float eValue  = (float)(atof(szText.GetBuffer(1))); 
 
    //  where is its center? 
    CRect rcWindow; 
    m_edtDataEntry.GetWindowRect(&rcWindow); 
    ScreenToClient(&rcWindow); 
    CPoint ptCenter = rcWindow.CenterPoint(); 
 
    //  based on the center, update the correct min or max. 
    if (m_rcXMin.PtInRect(ptCenter)==TRUE) 
    { 
      m_eXMin = eValue; 
      EmitLimitsChanged(); 
    } 
    else if (m_rcXMax.PtInRect(ptCenter)==TRUE) 
    { 
      m_eXMax = eValue; 
      EmitLimitsChanged(); 
    } 
    else if (m_rcYMin.PtInRect(ptCenter)==TRUE) 
    { 
      m_eYMin = eValue; 
      EmitLimitsChanged(); 
    } 
    else if (m_rcYMax.PtInRect(ptCenter)==TRUE) 
    { 
      m_eYMax = eValue; 
      EmitLimitsChanged(); 
    } 
 
    Refresh(); 
  } 
} 
///////////////////////////////////////////////////////////////////////////// 
 
CString CLinePlot::GetName(int nIndex) 
{ 
	return m_lstPlotData[nIndex].m_szName; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
bool CLinePlot::SetName(int nIndex, CString szName) 
{ 
  m_lstPlotData[nIndex].m_szName = szName; 
  if (m_hWnd!=NULL) 
	  Invalidate(); 
  return true; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
CLinePlot::enumPlotStyle CLinePlot::GetStyle(int nIndex) 
{ 
	return (CLinePlot::enumPlotStyle)(m_lstPlotData[nIndex].m_nStyle); 
} 
///////////////////////////////////////////////////////////////////////////// 
 
bool CLinePlot::SetStyle(int nIndex, enumPlotStyle nStyle) 
{ 
  m_lstPlotData[nIndex].m_nStyle = nStyle; 
  if (m_hWnd!=NULL) 
	  Invalidate(); 
  return true; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
COLORREF CLinePlot::GetColor(int nIndex) 
{ 
	return m_lstPlotData[nIndex].m_crColor; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
bool CLinePlot::SetColor(int nIndex, COLORREF crColor) 
{ 
  m_lstPlotData[nIndex].m_crColor = crColor; 
  if (m_hWnd!=NULL) 
	  Invalidate(); 
  return true; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
FLOATPOINT *CLinePlot::GetData(int nIndex) 
{ 
  return m_lstPlotData[nIndex].m_pptData; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
bool CLinePlot::SetData(int nIndex, FLOATPOINT *pptData) 
{ 
  m_lstPlotData[nIndex].UpdateData(pptData); 
  if (m_hWnd!=NULL) 
	  Invalidate(); 
  return true; 
} 
///////////////////////////////////////////////////////////////////////////// 
	   
UINT CLinePlot::GetPointCount(int nIndex) 
{ 
  return m_lstPlotData[nIndex].m_uiPointCount; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
bool CLinePlot::SetXMin(float &eXMin)  
{  
  if (m_oIsXLocked==true) 
    return false; 
  else 
  { 
    if (m_eXMin!=eXMin) 
    { 
      m_eXMin = eXMin; 
      EmitLimitsChanged(); 
      if (m_hWnd!=NULL) 
	      Invalidate(); 
    } 
    return true; 
  } 
} 
///////////////////////////////////////////////////////////////////////////// 
 
bool CLinePlot::SetXMax(float &eXMax)  
{  
  if (m_oIsXLocked==true) 
    return false; 
  else 
  { 
    if (m_eXMax!=eXMax) 
    { 
      m_eXMax = eXMax; 
      EmitLimitsChanged(); 
      if (m_hWnd!=NULL) 
	      Invalidate(); 
    } 
    return true; 
  } 
} 
///////////////////////////////////////////////////////////////////////////// 
 
bool CLinePlot::SetYMin(float &eYMin)  
{  
  if (m_oIsYLocked==true) 
    return false; 
  else 
  { 
    if (m_eYMin!=eYMin) 
    { 
      m_eYMin = eYMin; 
      EmitLimitsChanged(); 
      if (m_hWnd!=NULL) 
	      Invalidate(); 
    } 
    return true; 
  } 
} 
///////////////////////////////////////////////////////////////////////////// 
 
bool CLinePlot::SetYMax(float &eYMax)  
{  
  if (m_oIsYLocked==true) 
    return false; 
  else 
  { 
    if (m_eYMax!=eYMax) 
    { 
      m_eYMax = eYMax; 
      EmitLimitsChanged(); 
      if (m_hWnd!=NULL) 
	      Invalidate(); 
    } 
    return true; 
  } 
} 
///////////////////////////////////////////////////////////////////////////// 
 
bool CLinePlot::SetIsXLocked(bool &oLock) 
{  
  if (m_oIsXLocked!=oLock) 
  { 
    m_oIsXLocked = oLock;  
    if (m_hWnd!=NULL) 
	    Invalidate(); 
  } 
  return true; 
} 
///////////////////////////////////////////////////////////////////////////// 
 
bool CLinePlot::SetIsYLocked(bool &oLock) 
{ 
  if (m_oIsYLocked!=oLock) 
  { 
    m_oIsYLocked = oLock;  
    if (m_hWnd!=NULL) 
	    Invalidate(); 
  } 
  return true; 
} 
/////////////////////////////////////////////////////////////////////////////