home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c480 / 18.ddi / MFC / SAMPLES / VBCHART / CHARTVW.CP_ / CHARTVW.CP
Encoding:
Text File  |  1993-02-08  |  21.4 KB  |  810 lines

  1. // chartvw.cpp : implementation of the CChartView class
  2. //
  3. // This is a part of the Microsoft Foundation Classes C++ library.
  4. // Copyright (C) 1992 Microsoft Corporation
  5. // All rights reserved.
  6. //
  7. // This source code is only intended as a supplement to the
  8. // Microsoft Foundation Classes Reference and Microsoft
  9. // QuickHelp and/or WinHelp documentation provided with the library.
  10. // See these sources for detailed information regarding the
  11. // Microsoft Foundation Classes product.
  12.  
  13.  
  14.  
  15. #include "stdafx.h"
  16. #include "vbchart.h"
  17.  
  18. #include "resource.h"
  19. #include "chartdoc.h"
  20. #include "chartvw.h"
  21. #include "gridentr.h"
  22.  
  23. #ifdef _DEBUG
  24. #undef THIS_FILE
  25. static char BASED_CODE THIS_FILE[] = __FILE__;
  26. #endif
  27.  
  28.  
  29. /////////////////////////////////////////////////////////////////////////////
  30. // CVbChartFrame
  31.  
  32.  
  33. IMPLEMENT_DYNCREATE(CVbChartFrame, CMDIChildWnd)
  34.  
  35. BOOL CVbChartFrame::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext)
  36. {
  37.     CRect rect;
  38.     GetClientRect(&rect);
  39.  
  40.     CSize size = rect.Size();
  41.     size.cx /= 2;
  42.     
  43.     BOOL bSuccess;
  44.     bSuccess  = m_Splitter.CreateStatic(this, 1, 2, WS_CHILD|WS_VISIBLE, AFX_IDW_PANE_FIRST);
  45.     bSuccess &= m_Splitter.CreateView(0, 0, RUNTIME_CLASS(CGridEntry), size, pContext);
  46.     bSuccess &= m_Splitter.CreateView(0, 1, RUNTIME_CLASS(CChartView), size, pContext);
  47.     
  48.     return TRUE;
  49. }
  50.  
  51. BEGIN_MESSAGE_MAP(CVbChartFrame, CMDIChildWnd)
  52.     //{{AFX_MSG_MAP(CVbChartFrame)
  53.     ON_COMMAND(ID_FILE_PRINT, OnFilePrint)
  54.     ON_COMMAND(ID_FILE_PRINT_PREVIEW, OnFilePrintPreview)
  55.     ON_UPDATE_COMMAND_UI(IDC_CHARTLIST, OnUpdateChartList)
  56.     ON_CBN_SELCHANGE(IDC_CHARTLIST, OnChartTypeChange)
  57.     //}}AFX_MSG_MAP
  58. END_MESSAGE_MAP()
  59.  
  60. /////////////////////////////////////////////////////////////////////////////
  61. // CVbChartFrame Commands
  62.  
  63. void CVbChartFrame::OnFilePrint()
  64. {
  65.     ((CChartView*)m_Splitter.GetPane(0, 1))->Print();   // Tell Chart to print  
  66. }
  67.  
  68. void CVbChartFrame::OnFilePrintPreview()
  69. {
  70.     ((CChartView*)m_Splitter.GetPane(0, 1))->Preview(); // Tell Chart to preview    
  71. }
  72.  
  73. void CVbChartFrame::OnChartTypeChange()
  74. {
  75.     CComboBox* pDrop = (CComboBox*)CWnd::FromHandle(
  76.                             (HWND)LOWORD(GetCurrentMessage()->lParam));
  77.  
  78.     ASSERT(pDrop != NULL);
  79.     ASSERT(pDrop->IsKindOf(RUNTIME_CLASS(CWnd)));
  80.  
  81.     CChartView* pChart = (CChartView*)m_Splitter.GetPane(0, 1);
  82.     ASSERT(pChart->IsKindOf(RUNTIME_CLASS(CChartView)));
  83.  
  84.     pChart->m_nChartType = (int)pDrop->GetItemData(pDrop->GetCurSel());
  85.     pChart->Invalidate();
  86. }
  87.  
  88. void CVbChartFrame::OnUpdateChartList(CCmdUI* pCmdUI)
  89. {
  90.     ASSERT(pCmdUI->m_pOther != NULL);
  91.  
  92.     CChartView* pChart = (CChartView*)m_Splitter.GetPane(0, 1);
  93.     ASSERT(pChart->IsKindOf(RUNTIME_CLASS(CChartView)));
  94.  
  95.     // pCmdUI->m_pOther is not the real CWnd, even if it has been attached
  96.     // to a permanent CWnd.  Therefore, compare hWnds, not pointers
  97.  
  98.     CComboBox* pCombo = (CComboBox*)pCmdUI->m_pOther;
  99.     if (pCombo->m_hWnd == ::GetFocus())
  100.         return;
  101.  
  102.     for (int i = 0; i < pCombo->GetCount(); i++)
  103.     {
  104.         if ((int)pCombo->GetItemData(i) == pChart->m_nChartType)
  105.         {
  106.             if (pCombo->GetCurSel() != i)
  107.                 pCombo->SetCurSel(i);
  108.             return;
  109.         }
  110.     }
  111. }
  112.  
  113. /////////////////////////////////////////////////////////////////////////////
  114. // CChartView
  115.  
  116. IMPLEMENT_DYNCREATE(CChartView, CView)
  117.  
  118. BEGIN_MESSAGE_MAP(CChartView, CView)
  119.     //{{AFX_MSG_MAP(CChartView)
  120.     //}}AFX_MSG_MAP
  121. END_MESSAGE_MAP()
  122.  
  123. /////////////////////////////////////////////////////////////////////////////
  124. // CChartView construction/destruction
  125.  
  126. CChartView::CChartView()
  127. {
  128.     m_nChartType = CHART_3DVBAR;
  129.     m_pnData = NULL;
  130. }
  131.  
  132. CChartView::~CChartView()
  133. {
  134. }
  135.  
  136.  
  137. // do not update graph if a GridEntry hint.
  138. void CChartView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
  139. {
  140.     delete [] m_pnData; // invalidate cache
  141.     m_pnData = NULL;
  142.  
  143.     if (pHint == NULL)
  144.     {
  145.         CView::OnUpdate(pSender, lHint, pHint);
  146.     }
  147. }
  148.  
  149. /////////////////////////////////////////////////////////////////////////////
  150. // CChartView drawing
  151.  
  152. void CChartView::OnDraw(CDC* pDC)
  153. {
  154.     CChartDoc* pDoc = GetDocument();
  155.  
  156.     if (m_pnData == NULL)
  157.         GetDataFromGrid();
  158.  
  159.     if (m_pnData == NULL)
  160.     {
  161.         pDC->TextOut(10, 10, "No Selection");
  162.         return;
  163.     }
  164.  
  165.     switch (m_nChartType)
  166.     {
  167.         case CHART_LINE:
  168.             if (m_nDataCols > 1)
  169.                 PlotLineChart(pDC);
  170.             else
  171.                 pDC->TextOut(10, 10, "No Selection");
  172.             break;
  173.  
  174.         case CHART_VBAR:
  175.             PlotBarChart(pDC, TRUE);
  176.             break;
  177.  
  178.         case CHART_HBAR:
  179.             PlotBarChart(pDC, FALSE);
  180.             break;
  181.  
  182.         case CHART_3DVBAR:
  183.             Plot3DBarChart(pDC, TRUE);
  184.             break;
  185.  
  186.         case CHART_3DHBAR:
  187.             Plot3DBarChart(pDC, FALSE);
  188.             break;
  189.  
  190.         case CHART_VGANTT:
  191.             PlotGanttChart(pDC, TRUE);
  192.             break;
  193.  
  194.         case CHART_HGANTT:
  195.             PlotGanttChart(pDC, FALSE);
  196.             break;
  197.  
  198.     }
  199. }
  200.  
  201. void CChartView::GetDataFromGrid()
  202. {
  203.     CSplitterWnd* pParent = (CSplitterWnd*) GetParent();
  204.     ASSERT(pParent->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));
  205.  
  206.     CGridEntry* pGridView = (CGridEntry*) pParent->GetPane(0, 0);
  207.     ASSERT(pGridView->IsKindOf(RUNTIME_CLASS(CGridEntry)));
  208.  
  209.     CVBControl* pGrid = pGridView->m_pGrid;
  210.  
  211.     m_nStartRow    = (int)pGrid->GetNumProperty("SelStartRow");
  212.     m_nStartCol    = (int)pGrid->GetNumProperty("SelStartCol");
  213.     int nSelEndRow = (int)pGrid->GetNumProperty("SelEndRow");
  214.     int nSelEndCol = (int)pGrid->GetNumProperty("SelEndCol");
  215.  
  216.     CString data = pGrid->GetStrProperty("Clip");
  217.     char *pData = data.GetBuffer(1);    // do not expand buffer unnecessarily
  218.  
  219.     // Allocate data matrix
  220.  
  221.     delete [] m_pnData;     // delete old data if extant
  222.     m_pnData = NULL;
  223.  
  224.     m_nDataRows = nSelEndRow - m_nStartRow + 1;
  225.     m_nDataCols = nSelEndCol - m_nStartCol + 1;
  226.  
  227.     if (m_nDataRows * m_nDataCols < 2)
  228.         return;
  229.  
  230.     m_pnData = new int[m_nDataRows * m_nDataCols];
  231.  
  232.     int* pnData = m_pnData;
  233.  
  234.     // Convert all of the data in selected region
  235.     for (int row = m_nStartRow; row <= nSelEndRow; row++)
  236.     {
  237.         for (int col = m_nStartCol; col <= nSelEndCol; col++)
  238.         {
  239.             char* pToken = strtok(pData, " \t\r\n");
  240.             pData = NULL;   // continue with same string on future calls
  241.  
  242.             if (pToken != NULL)
  243.                 *pnData = (int) strtol(pToken, NULL, 0);
  244.             else
  245.                 *pnData = 0;        // This is bad because data will be skipped
  246.  
  247.             if (*pnData < 0)        // do not allow negative numbers
  248.                 *pnData = 0;        // They mess up the graphs
  249.  
  250.             pnData++;
  251.         }
  252.     }
  253. }
  254.  
  255. static int nValidGraduations[] =
  256. {
  257.     1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 0
  258. };
  259.  
  260.  
  261. CRect CChartView::GetDisplayRect(CDC* pDC)
  262. {
  263.     CRect rect;
  264.  
  265.     if (pDC->IsPrinting())
  266.     {
  267.         rect.left = rect.top = 0;
  268.         rect.right  = pDC->GetDeviceCaps(HORZRES);
  269.         rect.bottom = pDC->GetDeviceCaps(VERTRES);
  270.     }
  271.     else
  272.     {
  273.         GetClientRect(&rect);
  274.     }
  275.     return rect;
  276. }
  277.  
  278. static COLORREF colorTable[] =
  279. {
  280.     RGB(255,   0,   0),     // Red
  281.     RGB(  0, 255,   0),     // Green
  282.     RGB(  0,   0, 255),     // Blue
  283.     RGB(255, 255,   0),     // Yellow
  284.     RGB(255,   0, 255),     // Magenta
  285.     RGB(  0, 255, 255),     // Cyan
  286.     RGB(  0,   0,   0),     // Black
  287.     RGB(128, 128, 128),     // Grey
  288.     RGB(128, 128,   0),     // Brown
  289.     RGB(128,   0, 128),     // Purple
  290.     RGB(  0, 128, 128)      // Aqua
  291. };
  292.  
  293. int CChartView::GetTickValue(int nMaxValue, int nNumDiv)
  294. {
  295.     // Determine values for vertical ticks
  296.     int nValuePerTick = (nMaxValue + nNumDiv - 1) / nNumDiv;
  297.     for (int i = 0; nValidGraduations[i] > 0; i++)
  298.     {
  299.         if (nValidGraduations[i] >= nValuePerTick)
  300.         {
  301.             return nValidGraduations[i];
  302.         }
  303.     }
  304.     return nValidGraduations[i-1];
  305. }
  306.  
  307. //
  308. // GetChartMetrics determines several things:
  309. // Returns Character Height
  310. // &sizeGraph contains overall dimensions of the graph (may not be window size)
  311. // &sizePixelsPerTick contains pixels per tick in both directions
  312. // &ptOrigin contains the origin point
  313. //
  314. // Parameters:
  315. //  pDC   - DC for display device
  316. //  nVDiv - Number of vertical Divisions
  317. //  nHDiv - Number of horizontal Divisions
  318. // 
  319. int CChartView::GetChartMetrics(CDC* pDC, int nHDiv, int nVDiv,
  320.     CSize& sizeGraph, CSize& sizePixelsPerTick, CPoint& ptOrigin)
  321. {
  322.     CSize sizeText = pDC->GetTextExtent("0", 1);
  323.  
  324.     // Determine minimum size for graph
  325.     sizeGraph = sizeText;
  326.  
  327.     // Minimum size of a Division == 5 chars
  328.     // Allow 5 chars for Vertical Captions and 2.5 chars on each side of chart
  329.  
  330.     sizeGraph.cx *= (5 * nHDiv) + 10;
  331.     sizeGraph.cy *= nVDiv + 3;
  332.  
  333.     // Grow size if permitted by display area
  334.     CRect rect = GetDisplayRect(pDC);
  335.     if (rect.Width() > sizeGraph.cx)
  336.         sizeGraph.cx = rect.Width();
  337.     if (rect.Height() > sizeGraph.cy)
  338.         sizeGraph.cy = rect.Height();
  339.  
  340.     sizePixelsPerTick.cx = (sizeGraph.cx - 10 * sizeText.cx) / nHDiv;
  341.     sizePixelsPerTick.cy = (sizeGraph.cy -  3 * sizeText.cy) / nVDiv;
  342.  
  343.     ptOrigin.x = sizeText.cx * 15 / 2;
  344.     ptOrigin.y = sizePixelsPerTick.cy * nVDiv + sizeText.cy * 3 / 2;
  345.  
  346.     return sizeText.cy;
  347. }
  348.  
  349. void CChartView::PlotCaptions(CDC* pDC, CPoint ptOrigin, 
  350.             CSize sizePixelsPerTick, int nValue, int nValuePerTick, 
  351.             int nNumTicks, int nCharHeight, BOOL bVert, BOOL bAlpha)
  352. {
  353.     char buf[80];
  354.  
  355.     int nTickSize = (bVert ? sizePixelsPerTick.cx : sizePixelsPerTick.cy) / 30;
  356.     if (nTickSize < 2)
  357.         nTickSize = 2;
  358.  
  359.     int nPos  = bVert ? ptOrigin.y : ptOrigin.x;
  360.     int nStep = bVert ? -sizePixelsPerTick.cy : sizePixelsPerTick.cx;
  361.  
  362.     pDC->SetTextAlign(bVert ? TA_RIGHT : TA_CENTER);
  363.  
  364.     for (int i = 0; i < nNumTicks; i++)
  365.     {
  366.         if (bAlpha)
  367.             wsprintf(buf, "%c", nValue);
  368.         else
  369.             wsprintf(buf, "%d", nValue);
  370.  
  371.         if (bVert)
  372.             pDC->TextOut(ptOrigin.x - 2 * nTickSize,
  373.                                         nPos - nCharHeight / 2, buf);
  374.         else
  375.             pDC->TextOut(nPos, ptOrigin.y + nCharHeight / 2, buf);
  376.  
  377.         nValue += nValuePerTick;
  378.         nPos   += nStep;
  379.     }
  380. }
  381.  
  382.  
  383. void CChartView::PlotAxes(CDC* pDC, CPoint ptOrigin, CSize sizePixelsPerTick,
  384.                 int nHorzTicks, int nVertTicks, int nDrawTicks)
  385. {
  386.     int nHeight = sizePixelsPerTick.cy * nVertTicks;
  387.     int nWidth  = sizePixelsPerTick.cx * nHorzTicks;
  388.  
  389.     // Draw Axes
  390.     pDC->MoveTo(ptOrigin);
  391.     pDC->LineTo(ptOrigin.x, ptOrigin.y - nHeight);
  392.     pDC->MoveTo(ptOrigin);
  393.     pDC->LineTo(ptOrigin.x + nWidth, ptOrigin.y);
  394.  
  395.     if (nDrawTicks & TICKS_VERT)
  396.     {
  397.         // Draw Vertical Ticks
  398.         int nTickSize = (sizePixelsPerTick.cx / 30);
  399.         if (nTickSize < 2)
  400.             nTickSize = 2;
  401.  
  402.         int nVPos = ptOrigin.y;
  403.         int xLeft  = ptOrigin.x - nTickSize;
  404.         int xRight = ptOrigin.x + nTickSize + 1;
  405.  
  406.         for (int i = 0; i <= nVertTicks; i++)
  407.         {
  408.             pDC->MoveTo(xLeft, nVPos);
  409.             pDC->LineTo(xRight, nVPos);
  410.             nVPos -= sizePixelsPerTick.cy;
  411.         }
  412.     }
  413.  
  414.     if (nDrawTicks & TICKS_HORZ)
  415.     {
  416.         // Draw Horizontal Ticks
  417.         int nTickSize = (sizePixelsPerTick.cy / 30);
  418.         if (nTickSize < 2)
  419.             nTickSize = 2;
  420.  
  421.         int nHPos = ptOrigin.x;
  422.         int yTop    = ptOrigin.y - nTickSize;
  423.         int yBottom = ptOrigin.y + nTickSize + 1;
  424.  
  425.         for (int i = 0; i <= nHorzTicks; i++)
  426.         {
  427.             pDC->MoveTo(nHPos, yTop);
  428.             pDC->LineTo(nHPos, yBottom);
  429.             nHPos += sizePixelsPerTick.cx;
  430.         }
  431.     }
  432. }
  433.     
  434. void CChartView::PlotLineChart(CDC* pDC)
  435. {
  436.     int nMaxValue = GetMaxValue();
  437.     
  438.     int nValuePerTick = GetTickValue(nMaxValue, 5);
  439.     
  440.     CSize sizeGraph;
  441.     CSize sizePixelsPerTick;
  442.     CPoint ptOrigin;
  443.     int nCharHeight = GetChartMetrics(pDC, m_nDataCols - 1, 5,
  444.                                 sizeGraph, sizePixelsPerTick, ptOrigin);
  445.  
  446.  
  447.     PlotAxes(pDC, ptOrigin, sizePixelsPerTick, m_nDataCols - 1, 5, TICKS_BOTH);
  448.  
  449.     PlotCaptions(pDC, ptOrigin, sizePixelsPerTick, 0, nValuePerTick,
  450.                         6, nCharHeight, TRUE, FALSE);
  451.  
  452.     PlotCaptions(pDC, ptOrigin, sizePixelsPerTick, 'A' + m_nStartCol - 1, 1,
  453.                         m_nDataCols, nCharHeight, FALSE, TRUE);
  454.         
  455.     int* pnData = m_pnData;
  456.     for (int nRow = 0; nRow < m_nDataRows; nRow++)
  457.     {
  458.         CPen pen(PS_SOLID, 3, colorTable[nRow]);
  459.         CPen* pOldPen = pDC->SelectObject(&pen);
  460.  
  461.         int nHPos = ptOrigin.x;
  462.         for (int nCol = 0; nCol < m_nDataCols; nCol++)
  463.         {
  464.             int nVPos = MulDiv(*pnData++, sizePixelsPerTick.cy,
  465.                                                 nValuePerTick);
  466.             if (nCol == 0)
  467.                 pDC->MoveTo(nHPos, ptOrigin.y - nVPos);
  468.             else
  469.                 pDC->LineTo(nHPos, ptOrigin.y - nVPos);
  470.             nHPos += sizePixelsPerTick.cx;
  471.         }
  472.         pDC->SelectObject(pOldPen);
  473.     }
  474. }
  475.  
  476. void CChartView::PlotBarChart(CDC* pDC, BOOL bVert)
  477. {
  478.     int nMaxValue = GetMaxValue();
  479.     
  480.     int nValuePerTick = GetTickValue(nMaxValue, 5);
  481.     
  482.     int nVTicks = bVert ? 5 : m_nDataCols;
  483.     int nHTicks = bVert ? m_nDataCols : 5;
  484.  
  485.     CSize sizeGraph;
  486.     CSize sizePixelsPerTick;
  487.     CPoint ptOrigin;
  488.     int nCharHeight = GetChartMetrics(pDC, nHTicks, nVTicks,
  489.                                 sizeGraph, sizePixelsPerTick, ptOrigin);
  490.  
  491.     PlotAxes(pDC, ptOrigin, sizePixelsPerTick, nHTicks, nVTicks, 
  492.                             bVert ? TICKS_VERT : TICKS_HORZ);
  493.  
  494.     PlotCaptions(pDC, ptOrigin, sizePixelsPerTick, 0, nValuePerTick,
  495.                         6, nCharHeight, bVert, FALSE);
  496.  
  497.     // offset Origin to plot column letters in middle of Division
  498.     CPoint ptTemp = ptOrigin;
  499.     if (!bVert)
  500.         ptTemp.y -= sizePixelsPerTick.cy / 2;
  501.     else
  502.         ptTemp.x += sizePixelsPerTick.cx / 2;
  503.  
  504.     PlotCaptions(pDC, ptTemp, sizePixelsPerTick, 'A' + m_nStartCol - 1, 1,
  505.                         m_nDataCols, nCharHeight, !bVert, TRUE);
  506.  
  507.     // m_nDataRows bars per division, plus 1 bar width for space
  508.     int nDataSetWidth = bVert ? sizePixelsPerTick.cx : sizePixelsPerTick.cy;
  509.     int nBarWidth     = nDataSetWidth / (m_nDataRows + 1);
  510.     int nSpaceWidth   = (nDataSetWidth - (m_nDataRows * nBarWidth)) / 2;
  511.  
  512.     // in the loop, xPos means distance from origin along Data Set axis
  513.     // not necessarily horizontal position
  514.  
  515.     for (int nCol = 0; nCol < m_nDataCols; nCol++)
  516.     {
  517.         int xPos = nDataSetWidth * nCol + nSpaceWidth;
  518.  
  519.         for (int nRow = 0; nRow < m_nDataRows; nRow++)
  520.         {
  521.             int nValue = m_pnData[nRow * m_nDataCols + nCol];
  522.             int yPos = MulDiv(nValue,
  523.                         bVert ? sizePixelsPerTick.cy : sizePixelsPerTick.cx,
  524.                         nValuePerTick);
  525.             CBrush brush(colorTable[nRow]);
  526.             CBrush* pOldBrush = pDC->SelectObject(&brush);
  527.  
  528.             if (bVert)
  529.                 pDC->Rectangle(ptOrigin.x + xPos, ptOrigin.y - yPos,
  530.                             ptOrigin.x + xPos + nBarWidth, ptOrigin.y);
  531.             else
  532.                 pDC->Rectangle(ptOrigin.x, ptOrigin.y - xPos - nBarWidth,
  533.                                ptOrigin.x + yPos, ptOrigin.y - xPos);
  534.  
  535.             pDC->SelectObject(pOldBrush);
  536.             xPos += nBarWidth;
  537.         }
  538.     }
  539. }
  540.  
  541. void CChartView::Plot3DBarChart(CDC* pDC, BOOL bVert)
  542. {
  543.     int nMaxValue = GetMaxValue();
  544.     
  545.     int nValuePerTick = GetTickValue(nMaxValue, 5);
  546.     
  547.     int nVTicks = bVert ? 5 : m_nDataCols;
  548.     int nHTicks = bVert ? m_nDataCols : 5;
  549.  
  550.     // Tell Chart Metrics that there is an extra division in both directions
  551.     CSize sizeGraph;
  552.     CSize sizePixelsPerTick;
  553.     CPoint ptOrigin;
  554.     int nCharHeight = GetChartMetrics(pDC, nHTicks + 1, nVTicks + 1,
  555.                                 sizeGraph, sizePixelsPerTick, ptOrigin);
  556.  
  557.     int nColWidth  = bVert ? sizePixelsPerTick.cx : sizePixelsPerTick.cy;
  558.     int nDivHeight = bVert ? sizePixelsPerTick.cy : sizePixelsPerTick.cx;
  559.  
  560.     // Determine offsets of each column
  561.     int deltaX = sizePixelsPerTick.cx / m_nDataRows;
  562.     int deltaY = sizePixelsPerTick.cy / m_nDataRows;
  563.  
  564.     PlotAxes(pDC, ptOrigin, sizePixelsPerTick, nHTicks, nVTicks, TICKS_NONE);
  565.     CPoint ptOffset = ptOrigin;
  566.     ptOffset.x += m_nDataRows * deltaX;
  567.     ptOffset.y -= m_nDataRows * deltaY;
  568.     PlotAxes(pDC, ptOffset, sizePixelsPerTick, nHTicks, nVTicks, TICKS_NONE);
  569.  
  570.     // Draw top and right edges of back of grid
  571.     int nTop   = ptOffset.y - sizePixelsPerTick.cy * nVTicks;
  572.     int nRight = ptOffset.x + sizePixelsPerTick.cx * nHTicks;
  573.     pDC->MoveTo(ptOrigin.x, nTop + m_nDataRows * deltaY);
  574.     pDC->LineTo(ptOffset.x, nTop);
  575.     pDC->LineTo(nRight, nTop);
  576.     pDC->LineTo(nRight, ptOffset.y);
  577.     pDC->LineTo(nRight - m_nDataRows * deltaX, ptOrigin.y);
  578.  
  579.     // Draw Value lines
  580.     if (bVert)
  581.     {           // Horizontal lines for vertical bars
  582.         int yPos = 0;
  583.         for (int nTick = 0; nTick < nVTicks; nTick++)
  584.         {
  585.             pDC->MoveTo(ptOrigin.x, ptOrigin.y - yPos);
  586.             pDC->LineTo(ptOffset.x, ptOffset.y - yPos);
  587.             pDC->LineTo(nRight,     ptOffset.y - yPos);
  588.             yPos += sizePixelsPerTick.cy;
  589.         }
  590.     }
  591.     else        // Vertical Lines for Horizontal Bars
  592.     {
  593.         int xPos = 0;
  594.         for (int nTick = 0; nTick < nHTicks; nTick++)
  595.         {
  596.             pDC->MoveTo(ptOrigin.x + xPos, ptOrigin.y);
  597.             pDC->LineTo(ptOffset.x + xPos, ptOffset.y);
  598.             pDC->LineTo(ptOffset.x + xPos, nTop);
  599.             xPos += sizePixelsPerTick.cx;
  600.         }
  601.     }       
  602.  
  603.     PlotCaptions(pDC, ptOrigin, sizePixelsPerTick, 0, nValuePerTick,
  604.                         6, nCharHeight, bVert, FALSE);
  605.  
  606.     // offset Origin to plot column letters in middle of Division
  607.     CPoint ptTemp = ptOrigin;
  608.     if (!bVert)
  609.         ptTemp.y -= sizePixelsPerTick.cy / 2;
  610.     else
  611.         ptTemp.x += sizePixelsPerTick.cx / 2;
  612.  
  613.     PlotCaptions(pDC, ptTemp, sizePixelsPerTick, 'A' + m_nStartCol - 1, 1,
  614.                         m_nDataCols, nCharHeight, !bVert, TRUE);
  615.  
  616.     // 2/3 of division used for bar--centered in division
  617.     int nDivWidth     = bVert ? sizePixelsPerTick.cx : sizePixelsPerTick.cy;
  618.     int nBarWidth     = nDivWidth * 2 / 3;
  619.     int nSpaceWidth   = nDivWidth / 6;
  620.  
  621.     // in the loop, xPos means distance from origin along Column axis
  622.     // not necessarily horizontal position
  623.  
  624.     for (int nRow = m_nDataRows; nRow--;)
  625.     {
  626.         int xPos    = (bVert ? deltaX : deltaY) * nRow + nSpaceWidth;
  627.         int yOffset = (bVert ? deltaY : deltaX) * nRow;
  628.  
  629.         CBrush brush(colorTable[nRow]);
  630.         CBrush* pOldBrush = pDC->SelectObject(&brush);
  631.  
  632.         for (int nCol = 0; nCol < m_nDataCols; nCol++)
  633.         {
  634.             int nValue = m_pnData[nRow * m_nDataCols + nCol];
  635.             int yPos = MulDiv(nValue,
  636.                         bVert ? sizePixelsPerTick.cy : sizePixelsPerTick.cx,
  637.                         nValuePerTick) + yOffset;
  638.  
  639.             CRect rcFace;
  640.  
  641.             if (bVert)
  642.             {
  643.                 rcFace.left   = ptOrigin.x + xPos;
  644.                 rcFace.top    = ptOrigin.y - yPos;
  645.                 rcFace.right  = rcFace.left + nBarWidth + 1;
  646.                 rcFace.bottom = ptOrigin.y - yOffset;
  647.             }
  648.             else
  649.             {
  650.                 rcFace.left   = ptOrigin.x + yOffset;
  651.                 rcFace.bottom = ptOrigin.y - xPos;
  652.                 rcFace.right  = ptOrigin.x + yPos + 1;
  653.                 rcFace.top    = rcFace.bottom - nBarWidth;
  654.             }
  655.  
  656.             // Paint front face
  657.             pDC->Rectangle(&rcFace);
  658.             rcFace.right--;
  659.  
  660.             // Paint top face
  661.             CPoint ptArray[4];
  662.             ptArray[0].x = rcFace.left;
  663.             ptArray[0].y = rcFace.top;
  664.             ptArray[1].x = ptArray[0].x + deltaX;
  665.             ptArray[1].y = ptArray[0].y - deltaY;
  666.             ptArray[3].x = rcFace.right;
  667.             ptArray[3].y = rcFace.top;
  668.             ptArray[2].x = ptArray[3].x + deltaX;
  669.             ptArray[2].y = ptArray[3].y - deltaY;
  670.             pDC->Polygon(ptArray, 4);
  671.  
  672.             // Leave points 2 & 3 the same
  673.             // for right face
  674.             ptArray[0].x = rcFace.right;
  675.             ptArray[0].y = rcFace.bottom;
  676.             ptArray[1].x = ptArray[0].x + deltaX;
  677.             ptArray[1].y = ptArray[0].y - deltaY;
  678.             pDC->Polygon(ptArray, 4);
  679.  
  680.             xPos += nDivWidth;
  681.         }
  682.         pDC->SelectObject(pOldBrush);
  683.     }
  684. }
  685.  
  686. void CChartView::PlotGanttChart(CDC* pDC, BOOL bVert)
  687. {
  688.     int nMaxValue = -32768;
  689.     int* pnData = m_pnData;
  690.     for (int nRow = 0; nRow < m_nDataRows; nRow++)
  691.     {
  692.         int nSum = 0;
  693.         for (int nCol = 0; nCol < m_nDataCols; nCol++)
  694.             nSum += *pnData++;
  695.         if (nSum > nMaxValue)
  696.             nMaxValue = nSum;
  697.     }
  698.  
  699.     int nValuePerTick = GetTickValue(nMaxValue, 5);
  700.     
  701.     int nVTicks = bVert ? 5 : m_nDataRows;
  702.     int nHTicks = bVert ? m_nDataRows : 5;
  703.  
  704.     CSize sizeGraph;
  705.     CSize sizePixelsPerTick;
  706.     CPoint ptOrigin;
  707.     int nCharHeight = GetChartMetrics(pDC, nHTicks, nVTicks,
  708.                                 sizeGraph, sizePixelsPerTick, ptOrigin);
  709.  
  710.     PlotAxes(pDC, ptOrigin, sizePixelsPerTick, nHTicks, nVTicks, 
  711.                             bVert ? TICKS_VERT : TICKS_HORZ);
  712.  
  713.     PlotCaptions(pDC, ptOrigin, sizePixelsPerTick, 0, nValuePerTick,
  714.                         6, nCharHeight, bVert, FALSE);
  715.  
  716.     // offset Origin to plot row numbers in middle of Division
  717.     CPoint ptTemp = ptOrigin;
  718.     if (!bVert)
  719.         ptTemp.y -= sizePixelsPerTick.cy / 2;
  720.     else
  721.         ptTemp.x += sizePixelsPerTick.cx / 2;
  722.  
  723.     PlotCaptions(pDC, ptTemp, sizePixelsPerTick, m_nStartRow, 1,
  724.                         m_nDataRows, nCharHeight, !bVert, FALSE);
  725.  
  726.     // 2/3 of division used for bar--centered in division
  727.     int nDivWidth     = bVert ? sizePixelsPerTick.cx : sizePixelsPerTick.cy;
  728.     int nBarWidth     = nDivWidth * 2 / 3;
  729.     int nSpaceWidth   = nDivWidth / 6;
  730.  
  731.     // in the loop, xPos means distance from origin along Data Set axis
  732.     // not necessarily horizontal position
  733.  
  734.     pnData = m_pnData;
  735.     for (nRow = 0; nRow < m_nDataRows; nRow++)
  736.     {
  737.         int xPos = nDivWidth * nRow + nSpaceWidth;
  738.  
  739.         int yPos = 0;
  740.         for (int nCol = 0; nCol < m_nDataCols; nCol++)
  741.         {
  742.             int nValue = MulDiv(*pnData++,
  743.                         bVert ? sizePixelsPerTick.cy : sizePixelsPerTick.cx,
  744.                         nValuePerTick);
  745.             CBrush brush(colorTable[nCol]);
  746.             CBrush* pOldBrush = pDC->SelectObject(&brush);
  747.  
  748.             if (bVert)
  749.                 pDC->Rectangle(ptOrigin.x + xPos, ptOrigin.y - yPos - nValue,
  750.                     ptOrigin.x + xPos + nBarWidth, ptOrigin.y - yPos + 1);
  751.             else
  752.                 pDC->Rectangle(ptOrigin.x + yPos, ptOrigin.y - xPos - nBarWidth,
  753.                     ptOrigin.x + yPos + nValue + 1, ptOrigin.y - xPos);
  754.  
  755.             pDC->SelectObject(pOldBrush);
  756.             yPos += nValue;
  757.         }
  758.     }
  759. }
  760.  
  761.  
  762. int CChartView::GetMaxValue()
  763. {
  764.     // Scan data to determine max value
  765.     int *pnData = m_pnData;
  766.     int nMax    = *pnData;
  767.  
  768.     for (int i = m_nDataRows*m_nDataCols; i--;)
  769.     {
  770.         if (*pnData > nMax)
  771.             nMax = *pnData;
  772.         pnData++;
  773.     }
  774.     return nMax;
  775. }
  776.  
  777.  
  778. /////////////////////////////////////////////////////////////////////////////
  779. // CChartView printing
  780.  
  781. BOOL CChartView::OnPreparePrinting(CPrintInfo* pInfo)
  782. {
  783.     pInfo->SetMaxPage(1);       // one page printing/preview
  784.     return DoPreparePrinting(pInfo);
  785. }
  786.  
  787. void CChartView::Print()
  788. {
  789.     OnFilePrint();
  790. }
  791.  
  792. void CChartView::Preview()
  793. {
  794.     OnFilePrintPreview();
  795. }
  796.  
  797.  
  798. /////////////////////////////////////////////////////////////////////////////
  799.  
  800. void CChartView::OnActivateView(BOOL bActivate, CView*, CView*)
  801. {
  802.     if (bActivate)
  803.     {               // activate sibling
  804.         GetParentFrame()->SetActiveView(
  805.                 (CView*)((CSplitterWnd*)GetParent())->GetPane(0, 0));
  806.     }
  807. }
  808.  
  809. /////////////////////////////////////////////////////////////////////////////
  810.