home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / C++-7 / DISK8 / MFC / SAMPLES / CHART / CHARTWND.CP$ / chartwnd
Encoding:
Text File  |  1992-03-18  |  17.3 KB  |  803 lines

  1. // chartwnd.cpp : Defines the class behaviors for the Chart frame window.
  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 documentation provided with the library.
  10. // See these sources for detailed information regarding the
  11. // Microsoft Foundation Classes product.
  12. //
  13.  
  14. #include "chart.h"
  15.  
  16. #include <math.h>
  17.  
  18. #include <commdlg.h>
  19.  
  20. // hPrintDlg and bUserAbort are used to handle the print abort
  21. // modeless dialog
  22. //
  23. static HWND hPrintDlg;
  24. BOOL bUserAbort;
  25.  
  26. // CChartWnd static member variables that define the bounds of
  27. // CChartWnd's custom anisotropic coordinate system
  28. //
  29. CRect CChartWnd::rectPage(0, 1000, 1000, 0);
  30. CRect CChartWnd::rectData(150, 100, 850, 800);
  31.  
  32. static short nBlue, nGreen, nRed, nCurrentColor;
  33.  
  34. /////////////////////////////////////////////////////////////////////////////
  35. // CChartWnd
  36.  
  37. BEGIN_MESSAGE_MAP(CChartWnd, CFrameWnd)
  38.     ON_WM_MOUSEMOVE()
  39.     ON_WM_PAINT()
  40.     ON_WM_CREATE()
  41.     ON_WM_CLOSE()
  42.  
  43.     ON_COMMAND(IDM_NEW, OnNew)
  44.     ON_COMMAND(IDM_OPEN, CmdFileOpen)
  45.     ON_COMMAND(IDM_SAVE, CmdFileSave)
  46.     ON_COMMAND(IDM_SAVEAS, CmdFileSaveAs)
  47.     ON_COMMAND(IDM_CHANGE, OnChange)
  48.     ON_COMMAND(IDM_PRINT, OnPrint)
  49.     ON_COMMAND(IDM_EXIT, OnClose)
  50.  
  51.     ON_COMMAND(IDM_BAR, OnBar)
  52.     ON_COMMAND(IDM_LINE, OnLine)
  53.  
  54.     ON_COMMAND(IDM_ABOUT, OnAbout)
  55. END_MESSAGE_MAP()
  56.  
  57. // Constructor:
  58. //
  59. CChartWnd::CChartWnd()
  60. {
  61.     m_bUntitled = TRUE;
  62.     m_szFileName = "";
  63.     m_bChartSerializedOK = FALSE;
  64.     Create("Chart");
  65. }
  66.  
  67. // Destructor:
  68. //
  69. CChartWnd::~CChartWnd()
  70. {
  71.     if (m_pChartObject != NULL)
  72.     {
  73.         delete m_pChartObject;  
  74.         m_pChartObject = NULL;
  75.     }
  76. }
  77.  
  78. // Create:
  79. // Load accelerator keys for this window and create a frame window.
  80. // The frame window uses a custom window class because it has a
  81. // non-standard icon.
  82. // The accelerator table, main menu, and icon are defined in chart.rc.
  83. //
  84. BOOL CChartWnd::Create(LPCSTR szTitle,
  85.     LONG style /* = WS_OVERLAPPEDWINDOW */,
  86.     const RECT& rect /* = rectDefault */,
  87.     CWnd* parent /* = NULL */)
  88. {
  89.     LoadAccelTable("MainAccelTable");
  90.  
  91.     const char* pszWndClass =
  92.             AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
  93.                                 NULL,
  94.                                 (HBRUSH)(COLOR_WINDOW+1),
  95.                                 LoadIcon(AfxGetInstanceHandle(), "chart"));
  96.  
  97.     return CFrameWnd::Create(pszWndClass, szTitle, style,
  98.                              rect, parent, "MainMenu");
  99. }
  100.  
  101. // OnCreate:
  102. // Get cursors and initialize the main menu
  103. //
  104. int CChartWnd::OnCreate(LPCREATESTRUCT)
  105. {
  106.     m_pChartObject = NULL;
  107.     m_hCross = LoadCursor(NULL, IDC_CROSS);
  108.     m_hArrow = LoadCursor(NULL, IDC_ARROW);
  109.  
  110.     UpdateMenu();
  111.  
  112.     return 0;
  113. }
  114.  
  115. // UpdateMenu:
  116. // Update main menu item state based on chart state
  117. //
  118. void CChartWnd::UpdateMenu()
  119. {
  120.     CMenu* menu = GetMenu();
  121.  
  122.     UINT nData = (m_pChartObject? MF_ENABLED : MF_GRAYED);
  123.  
  124.     menu->EnableMenuItem(IDM_SAVEAS, nData);
  125.     menu->EnableMenuItem(IDM_CHANGE, nData);
  126.     menu->EnableMenuItem(IDM_PRINT,  nData);
  127.  
  128.     menu->EnableMenuItem(IDM_BAR,    nData);
  129.     menu->EnableMenuItem(IDM_LINE,   nData);
  130.  
  131.     if (m_pChartObject != NULL)
  132.     {
  133.         nData = (m_pChartObject->m_nType == IDM_BAR);
  134.         menu->CheckMenuItem(IDM_BAR,  nData? MF_CHECKED : MF_UNCHECKED);
  135.         menu->CheckMenuItem(IDM_LINE, nData? MF_UNCHECKED : MF_CHECKED);
  136.         menu->EnableMenuItem(IDM_SAVE,
  137.                              m_bUntitled ? MF_GRAYED : MF_ENABLED);
  138.  
  139.                     
  140.     }
  141.     else
  142.     {
  143.         menu->CheckMenuItem(IDM_BAR,  MF_UNCHECKED);
  144.         menu->CheckMenuItem(IDM_LINE, MF_UNCHECKED);
  145.         menu->EnableMenuItem(IDM_SAVE, MF_GRAYED);
  146.     }
  147.  
  148.     DrawMenuBar();
  149. }
  150.  
  151. // OnNew:
  152. // Create a new empty chart
  153. //
  154. void CChartWnd::OnNew()
  155. {
  156.     if (m_pChartObject != NULL)
  157.     {
  158.         if (m_pChartObject->m_bDirty)
  159.         {
  160.  
  161.             if (MessageBox("Save existing data?", "Chart",
  162.                             MB_YESNO | MB_ICONQUESTION) == IDYES)
  163.             {
  164.                 SaveFile(m_bUntitled);
  165.             }
  166.         }
  167.  
  168.         delete m_pChartObject;
  169.         m_pChartObject = NULL;
  170.         m_bUntitled = TRUE;
  171.         m_szFileName = "";
  172.         m_pChartObject = NULL;
  173.     }
  174.     OnChange();
  175. }
  176.  
  177. // OnChange:
  178. // User wants to enter (or change) chart data.
  179. //
  180. void CChartWnd::OnChange()
  181. {
  182.     // Create data structure if there is none.
  183.     //
  184.     if (m_pChartObject == NULL)
  185.     {
  186.         m_pChartObject = new CChartObject();
  187.     }
  188.  
  189.     // Get the data.
  190.     //
  191.     CEntryDialog entryDlg(this);
  192.     entryDlg.DoModal(m_pChartObject);
  193.  
  194.     ASSERT(m_pChartObject->m_pChartData != NULL);
  195.  
  196.     if (m_pChartObject->m_pChartData->IsEmpty())
  197.     {
  198.         delete m_pChartObject;
  199.         m_pChartObject = NULL;
  200.     }
  201.  
  202.     // Update menu state based on the data
  203.     //
  204.     UpdateMenu();
  205.     Invalidate(TRUE);
  206. }
  207.  
  208. // OnPrint:
  209. // User wants to print the chart
  210. //
  211. void CChartWnd::OnPrint()
  212. {
  213.     if (!DoPrint())
  214.     {
  215.         MessageBox("Not able to print chart.", "Chart",
  216.            MB_OK | MB_ICONEXCLAMATION);
  217.     }
  218. }
  219.  
  220. // OnBar:
  221. // Make the chart a bar chart
  222. //
  223. void CChartWnd::OnBar()
  224. {
  225.     ASSERT(m_pChartObject != NULL);
  226.  
  227.     m_pChartObject->m_nType = IDM_BAR;
  228.  
  229.     UpdateMenu();
  230.     Invalidate(TRUE);
  231. }
  232.  
  233. // OnLine:
  234. // Make the chart a line chart
  235. //
  236. void CChartWnd::OnLine()
  237. {
  238.     ASSERT(m_pChartObject != NULL);
  239.  
  240.     m_pChartObject->m_nType = IDM_LINE;
  241.  
  242.     UpdateMenu();
  243.     Invalidate(TRUE);
  244. }
  245.  
  246. // PrepareDC:
  247. // Prepare a DC for drawing the chart
  248. void CChartWnd::PrepareDC(CDC* pDC)
  249. {
  250.     pDC->SetMapMode(MM_ANISOTROPIC);
  251.     pDC->SetWindowExt(rectPage.right, rectPage.top);
  252.     pDC->SetViewportExt(m_cxClient, -m_cyClient);
  253.     pDC->SetViewportOrg(0, m_cyClient);
  254.     pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
  255.     pDC->SetBkColor(::GetSysColor(COLOR_WINDOW));
  256. }
  257.  
  258. // OnMouseMove:
  259. // We do some hit-testing here to make the cursor a crosshairs while inside
  260. // the data graphic, and an arrow otherwise.
  261. //
  262. void CChartWnd::OnMouseMove(UINT, CPoint mousePos)
  263. {
  264.     if (m_pChartObject == NULL)
  265.     {
  266.         SetCursor(m_hArrow);
  267.         return;
  268.     }
  269.  
  270.     // We use a custom logical coordinate system.  0,0 is the bottom-left
  271.     // corner of the client area, and 1000,1000 is the top-right.
  272.     // Convert the mouse coordinates to this scheme.
  273.     //
  274.     CDC* pDC;
  275.     pDC = GetDC();
  276.     PrepareDC(pDC);
  277.     pDC->DPtoLP(&mousePos, 1);
  278.  
  279.     if (rectData.PtInRect(mousePos))
  280.     {
  281.         SetCursor(m_hCross);
  282.     }
  283.     else
  284.     {
  285.         SetCursor(m_hArrow);
  286.     }
  287.  
  288.     ReleaseDC(pDC);
  289. }
  290.     
  291. // OnPaint:
  292. // Paint the chart...
  293. //
  294. void CChartWnd::OnPaint()
  295. {
  296.     CPaintDC dc(this);
  297.     CRect screenPos;
  298.  
  299.     GetClientRect(screenPos);
  300.  
  301.     m_cxClient = screenPos.Width();
  302.     m_cyClient = screenPos.Height();
  303.  
  304.     if (m_pChartObject != NULL)
  305.     {
  306.         RenderChart(&dc);
  307.     }
  308. }
  309.  
  310. // OnClose:
  311. // User picked 'Close' on system menu
  312. //
  313. void CChartWnd::OnClose()
  314. {
  315.     if (m_pChartObject != NULL)
  316.     {
  317.         if (m_pChartObject->m_bDirty)
  318.         {
  319.             int fResponse;
  320.  
  321.             fResponse = MessageBox("Save file before exit?", "Chart",
  322.                             MB_YESNOCANCEL | MB_ICONQUESTION);
  323.  
  324.             if (fResponse == IDCANCEL)
  325.             {
  326.                 return;
  327.             }
  328.             else if (fResponse == IDYES)
  329.             {
  330.                 SaveFile(m_bUntitled);
  331.             }
  332.         }
  333.         delete m_pChartObject;
  334.     }
  335.  
  336.     m_pChartObject = NULL;
  337.  
  338.     DestroyWindow();
  339. }
  340.  
  341. // GetHighValue:
  342. // Returns largest value in chart data.
  343. //
  344. short CChartWnd::GetHighValue()
  345. {
  346.     short i, count, nLargest, nCurrent;
  347.     POSITION pos;
  348.     CChartData* ptr;
  349.  
  350.     ASSERT(m_pChartObject != NULL);
  351.     CObList* pChartData = m_pChartObject->m_pChartData;
  352.  
  353.     ASSERT(pChartData != NULL);
  354.  
  355.     ptr = (CChartData*)(pChartData->GetHead());
  356.  
  357.     nLargest = ptr->height;
  358.  
  359.     pos = pChartData->GetHeadPosition();
  360.     count = pChartData->GetCount();
  361.     for (i = 0; i < count; i++)
  362.     {
  363.         ptr = (CChartData*)pChartData->GetNext(pos);
  364.  
  365.         if ((nCurrent = ptr->height) > nLargest)
  366.         {
  367.             nLargest = nCurrent;
  368.         }
  369.     }
  370.  
  371.     return nLargest;
  372. }
  373.  
  374. // SetNewColors:
  375. // Progresses nCurrentColor through the possible colors.
  376. //
  377. void CChartWnd::SetNewColors()
  378. {
  379.     nCurrentColor++;
  380.  
  381.     ASSERT(m_pChartObject != NULL);
  382.     ASSERT(m_pChartObject->m_pChartData != NULL);
  383.  
  384.     nCurrentColor %= (m_pChartObject->m_pChartData->GetCount()+1);
  385.  
  386.     nBlue = (nCurrentColor & 1) ? 255 : 0;
  387.     nGreen = (nCurrentColor & 2) ? 255 : 0;
  388.     nRed = (nCurrentColor & 4) ? 255 : 0;
  389.  
  390. }
  391.  
  392. // RenderChart:
  393. // Draw the chart in a device context.  This routine handles both
  394. // screen DCs and printer DCs.
  395. //
  396. void CChartWnd::RenderChart(CDC* pDC)
  397. {
  398.     short i, nSize, nTextHeight;
  399.     float yTickMag, yTickGuess;
  400.     char szBuffer[80];
  401.  
  402.     ASSERT(m_pChartObject != NULL);
  403.  
  404.     if (m_pChartObject->m_pChartData->IsEmpty())
  405.     {
  406.         return;
  407.         // return if there are no entries in the list
  408.     }
  409.  
  410.     // We use a custom logical coordinate system.  0,0 is the bottom-left
  411.     // corner of the client area, and 1000,1000 is the top-right.
  412.     //
  413.     PrepareDC(pDC);
  414.  
  415.     // A rectangle around the chart.
  416.     //
  417.     pDC->Rectangle(rectData);
  418.  
  419.     // A blue dotted pen to draw the grid on the chart.
  420.     //
  421.     CPen newPen(PS_DOT, 2, RGB(0, 0, 255));
  422.     CPen* penOrig = pDC->SelectObject(&newPen);
  423.  
  424.     // Figure out a reasonable step size for the grid.
  425.     //
  426.     m_fTallest = GetHighValue();
  427.     yTickGuess = m_fTallest / 10;
  428.     yTickMag = (float)(((log10(yTickGuess) / log10(2.7182818))) /
  429.                         (log10(10.0) / log10(2.7182818)));
  430.     yTickGuess = (float)(((yTickGuess / pow(10, (yTickMag - 1)) + 0.5) / 10) *
  431.         pow(10, yTickMag));
  432.  
  433.     // Draw grid.
  434.     //
  435.     m_fTallest = yTickGuess * 10;
  436.     int iTickDelta = rectData.Height()/10;
  437.  
  438.     for (i = 1; i <= 10; i++)
  439.     {
  440.         if (i != 10)
  441.         {       
  442.             pDC->MoveTo(rectData.left, rectData.top + (iTickDelta*i));
  443.             pDC->LineTo(rectData.right, rectData.top + (iTickDelta*i));
  444.         }
  445.  
  446.         sprintf(szBuffer,"%3.2f", (yTickGuess*i));
  447.  
  448.         nSize = pDC->GetTextExtent(szBuffer,strlen(szBuffer)).cx;
  449.         nTextHeight = pDC->GetTextExtent(szBuffer, strlen(szBuffer)).cy;
  450.         if (((nSize+25) < rectData.left) && (nTextHeight < iTickDelta))
  451.         {
  452.             pDC->TextOut((125-nSize),
  453.                          rectData.top+(iTickDelta*i)+(nTextHeight/2),
  454.                         szBuffer, strlen(szBuffer));
  455.         }
  456.     }
  457.  
  458.     // Done with the grid; re-select the original pen
  459.  
  460.     pDC->SelectObject(penOrig);
  461.  
  462.     // Create and select the brush to draw the chart data itself
  463.     //
  464.     CBrush* pOldBrush;
  465.     CBrush newBrush(RGB(nRed, nGreen, nBlue));
  466.     pOldBrush = pDC->SelectObject(&newBrush);
  467.  
  468.     switch (m_pChartObject->m_nType)
  469.     {
  470.     case IDM_LINE:
  471.         DrawLineChart(pDC);
  472.         break;
  473.  
  474.     case IDM_BAR:
  475.         DrawBarChart(pDC);
  476.         break;
  477.     }
  478.  
  479.     // Delete the brush, after selecting the old one back in the DC.
  480.     //
  481.     pDC->SelectObject(pOldBrush);
  482.  
  483.     // Draw the title, if there's room.
  484.     //
  485.     nSize = pDC->GetTextExtent(m_pChartObject->m_Title,
  486.         m_pChartObject->m_Title.GetLength()).cx;
  487.  
  488.     if ((nSize/2) < 500)
  489.     {
  490.         pDC->TextOut((500 - (nSize/2)), 900, m_pChartObject->m_Title,
  491.             m_pChartObject->m_Title.GetLength());
  492.     }
  493. }
  494.  
  495. // DrawBarChart:
  496. // Render the chart as a bar chart
  497. //
  498. void CChartWnd::DrawBarChart(CDC* pDC)
  499. {
  500.     float flFraction;
  501.     short nWidth, i, nTextHeight, nCurrentHeight, nSize;
  502.     CRect rectBar;
  503.     POSITION pos;
  504.     CChartData* ptr;
  505.     char szBuffer[80];
  506.  
  507.     ASSERT(m_pChartObject != NULL);
  508.     ASSERT(m_pChartObject->m_pChartData != NULL);
  509.  
  510.     CObList* pChartData = m_pChartObject->m_pChartData;
  511.  
  512.     flFraction = (rectData.Width() / (pChartData->GetCount()));
  513.     nWidth = (short) flFraction;
  514.     pos = pChartData->GetHeadPosition();
  515.  
  516.     nCurrentColor = 0;
  517.     int cChart = pChartData->GetCount();
  518.  
  519.     for (i = 0; i < cChart; i++)
  520.     {
  521.         char szLabel[40];
  522.         ptr = (CChartData*)pChartData->GetNext(pos);
  523.         strcpy(szLabel, ptr->szName);
  524.         int nNewHeight = ptr->height;
  525.  
  526.         // Calculate the size of the bar.
  527.         //
  528.         flFraction = nNewHeight / m_fTallest;
  529.         nCurrentHeight = (short) (flFraction * rectData.Height());
  530.  
  531.         rectBar.left = rectData.left + (nWidth * i);
  532.         rectBar.top = nCurrentHeight + rectData.top;
  533.         rectBar.right = rectData.left + nWidth * (i+1);
  534.         rectBar.bottom = rectBar.top - nCurrentHeight;
  535.  
  536.         SetNewColors();
  537.         CBrush chartBrush(RGB(nRed, nGreen, nBlue));
  538.         CBrush* oldBrush = pDC->SelectObject(&chartBrush);
  539.  
  540.         pDC->Rectangle(rectBar);
  541.  
  542.         sprintf(szBuffer, "%s", szLabel);
  543.         nSize = pDC->GetTextExtent(szBuffer,strlen(szBuffer)).cx;
  544.         nTextHeight = pDC->GetTextExtent(szBuffer, strlen(szBuffer)).cy;
  545.         if (((nSize+20) < nWidth) && (nTextHeight < 95))
  546.             pDC->TextOut(rectBar.left+(nWidth/2)-(nSize/2),
  547.                          rectBar.bottom-5,
  548.                          szBuffer, strlen(szBuffer));
  549.  
  550.         // Delete the brush, after selecting the old one back into the DC.
  551.         //
  552.         pDC->SelectObject(oldBrush);
  553.     }
  554. }
  555.  
  556. // DrawLineChart:
  557. // Render the chart data as a line chart
  558. //
  559. void CChartWnd::DrawLineChart(CDC* pDC)
  560. {
  561.     short nWidth, nSize, nTextHeight, i;
  562.     short nCurrentHeight, nBottom;
  563.  
  564.     CPoint ptStart;
  565.     CPoint ptEnd;
  566.  
  567.     LONG nTotalHeight;
  568.     float flFraction, flOffset;
  569.     POSITION pos;
  570.     CChartData* ptr;
  571.     char szBuffer[80];
  572.  
  573.     ASSERT(m_pChartObject != NULL);
  574.  
  575.     CObList* pChartData = m_pChartObject->m_pChartData;
  576.  
  577.     ASSERT(pChartData != NULL);
  578.     flFraction = (rectData.Width() / (pChartData->GetCount()));
  579.     nWidth = (short) flFraction;
  580.  
  581.     ptr = (CChartData*)pChartData->GetHead();
  582.     ptStart.x = rectData.left + (nWidth/2);
  583.     nTotalHeight = (LONG)(((LONG)ptr->height) * rectData.Height());
  584.     flOffset = (nTotalHeight / m_fTallest);
  585.     ptStart.y = rectData.top + (int)flOffset;
  586.     pos = pChartData->GetHeadPosition();
  587.  
  588.     int cChart = pChartData->GetCount();
  589.     for (i = 0; i < cChart; i++)
  590.     {
  591.         char szLabel[40];
  592.         ptr = (CChartData*)pChartData->GetNext(pos);
  593.         strcpy(szLabel, ptr->szName);
  594.         int nNewHeight = ptr->height;
  595.  
  596.         flFraction = nNewHeight / m_fTallest;
  597.         nCurrentHeight = (short) (flFraction * rectData.Height());
  598.  
  599.         ptEnd.x = nWidth/2 + rectData.left + nWidth*i;
  600.         ptEnd.y = rectData.top + nCurrentHeight;
  601.         nBottom = ptEnd.y - nCurrentHeight;
  602.  
  603.         pDC->MoveTo(ptStart);
  604.         pDC->LineTo(ptEnd);
  605.  
  606.         ptStart = ptEnd;
  607.  
  608.         sprintf(szBuffer, "%s", szLabel);
  609.         nSize = pDC->GetTextExtent(szBuffer, strlen(szBuffer)).cx;
  610.         nTextHeight = pDC->GetTextExtent(szBuffer, strlen(szBuffer)).cy;
  611.         if (((nSize+20) < nWidth) && (nTextHeight < 95))
  612.         {
  613.             pDC->TextOut((ptEnd.x-(nSize/2)), (nBottom-5),
  614.                 szBuffer, strlen(szBuffer));
  615.         }
  616.     }
  617. }
  618.  
  619. // AbortProc:
  620. // While printing, the Printing... dialog (PrintDlgBox in chart.rc) is
  621. // displayed, which has a Cancel button on it.  This routine replaces the
  622. // normal message-handling mechanism, until the printing is done or the
  623. // Cancel button is pressed.
  624. //
  625. BOOL FAR PASCAL _export AbortProc(HDC, int)
  626. {
  627.     MSG msg;
  628.  
  629.     while(!bUserAbort && PeekMessage(&msg,NULL,0,0,PM_REMOVE))
  630.     {
  631.         if (!hPrintDlg || !IsDialogMessage(hPrintDlg, &msg))
  632.         {
  633.             TranslateMessage(&msg);
  634.             DispatchMessage(&msg);
  635.         }
  636.     }
  637.  
  638.     return !bUserAbort;
  639. }
  640.  
  641.  
  642. // DoPrint: 
  643. // Get a printer DC and render the chart on it.  Uses COMMDLG
  644. // printer dialog.
  645. //
  646. BOOL CChartWnd::DoPrint()
  647. {
  648.     BOOL (FAR PASCAL _export * lpfnAbortProc)(HDC hPrinterDC, int nCode);
  649.     static char szMessage[] = "Printing chart...";
  650.     short xPage, yPage, oldX, oldY;
  651.     BOOL bError = FALSE;
  652.     CDC* pDC = NULL;
  653.  
  654.     lpfnAbortProc = AbortProc;
  655.  
  656.     CPrintDialog printDialog(FALSE);
  657.     if (printDialog.DoModal() == IDCANCEL)
  658.         return FALSE;
  659.  
  660.     pDC = new CDC;
  661.     pDC->Attach(printDialog.GetPrinterDC());
  662.  
  663.     xPage = pDC->GetDeviceCaps(HORZRES);
  664.     yPage = pDC->GetDeviceCaps(VERTRES);
  665.  
  666.     // The chart main window has to be disabled while printing so that
  667.     // the user can't change data.
  668.     //
  669.     EnableWindow(FALSE);
  670.  
  671.     // Set up the printer abort box and its window procedure control
  672.     // variables
  673.     //
  674.     bUserAbort = FALSE;
  675.     m_pPrintDlg = new CPrintDlgBox;
  676.     hPrintDlg = m_pPrintDlg->m_hWnd;
  677.  
  678.     if (pDC->SetAbortProc(lpfnAbortProc) < 0)
  679.     {
  680.         delete pDC;
  681.         return FALSE;
  682.     }
  683.  
  684.     if (pDC->StartDoc(szMessage) > 0)
  685.     {
  686.         oldX = m_cxClient;
  687.         m_cxClient = xPage;
  688.         oldY = m_cyClient;
  689.         m_cyClient = yPage;
  690.  
  691.         RenderChart(pDC);
  692.  
  693.         if (pDC->EndPage() > 0)
  694.         {
  695.             pDC->EndDoc();
  696.         }
  697.         else
  698.         {
  699.             bError = TRUE;
  700.         }
  701.     }
  702.     else
  703.     {
  704.         bError = TRUE;
  705.     }
  706.  
  707.     m_cxClient = oldX;
  708.     m_cyClient = oldY;
  709.  
  710.     // Now that we're done, we can now allow the user access to the frame
  711.     // window again.  bUserAbort is set in the AbortProc just before this
  712.     // function.
  713.     //
  714.     if (!bUserAbort)
  715.     {
  716.         EnableWindow(TRUE);
  717.     }
  718.  
  719.     delete pDC;
  720.     delete m_pPrintDlg;
  721.  
  722.     return !bError && !bUserAbort;
  723. }
  724.  
  725. // CmdFileSave:
  726. // User wants to save current chart data in the current file
  727. //
  728. void CChartWnd::CmdFileSave()
  729. {
  730.     if (!m_bUntitled)
  731.     {
  732.         SaveFile(FALSE);
  733.         UpdateMenu();
  734.     }
  735. }
  736.  
  737. // CmdFileSaveAs:
  738. // User wants to save current chart data in a named file
  739. //
  740. void CChartWnd::CmdFileSaveAs()
  741. {
  742.     SaveFile(TRUE);
  743.     UpdateMenu();
  744. }
  745.  
  746. // CmdFileOpen
  747. // User wants to open an existing file and read in chart data
  748. //
  749. void CChartWnd::CmdFileOpen()
  750. {
  751.     // Create data structure if there is none.
  752.     //
  753.     if (m_pChartObject != NULL)
  754.     {
  755.         if (m_pChartObject->m_bDirty)
  756.         {
  757.             if (MessageBox("Save existing data?", "Chart",
  758.                         MB_YESNO | MB_ICONQUESTION) == IDYES)
  759.             {
  760.                 SaveFile(m_bUntitled);
  761.             }
  762.         }
  763.     }
  764.  
  765.     // Get the data.
  766.     //
  767.     ReadFile();
  768.  
  769.     // If read failed, or there's no data to read, clean up.
  770.     //
  771.  
  772.     if (!m_bChartSerializedOK)
  773.     {
  774.         // chart deserialization failed and chart is in an
  775.         // inconsistent state -- don't delete.  Just null
  776.         // the pointer and suffer a memory leak.
  777.  
  778.         m_pChartObject = NULL;
  779.     }
  780.     else
  781.     {
  782.         if ((m_pChartObject->m_pChartData == NULL) ||
  783.              m_pChartObject->m_pChartData->IsEmpty())
  784.         {
  785.             delete m_pChartObject;
  786.             m_pChartObject = NULL;
  787.         }
  788.     }
  789.  
  790.     // Update the frame menu and client area
  791.     //
  792.     UpdateMenu();
  793.     Invalidate(TRUE);
  794. }
  795.  
  796. // OnAbout:
  797. //
  798. void CChartWnd::OnAbout()
  799. {
  800.     CModalDialog aboutBox("AboutBox");
  801.     aboutBox.DoModal();
  802. }
  803.