|
|
1.1 root 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: {
1.1.1.2 root 398: int i, nSize, nTextHeight;
1.1 root 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)));
1.1.1.2 root 430: yTickGuess = (float)(((yTickGuess / pow(10.0, (yTickMag - 1.0)) + 0.5)
431: / 10.0) * pow(10.0, yTickMag));
1.1 root 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;
1.1.1.2 root 501: int nWidth, i, nTextHeight, nCurrentHeight, nSize;
1.1 root 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:
1.1.1.2 root 512: flFraction = (float)(rectData.Width() / (pChartData->GetCount()));
1.1 root 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: {
1.1.1.2 root 561: int nWidth, nSize, nTextHeight, i;
562: int nCurrentHeight, nBottom;
1.1 root 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);
1.1.1.2 root 578: flFraction = (float)(rectData.Width() / (pChartData->GetCount()));
1.1 root 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)
1.1.1.3 ! root 658: return TRUE;
1.1 root 659:
660: pDC = new CDC;
1.1.1.3 ! root 661: ASSERT(printDialog.GetPrinterDC() != NULL);
1.1 root 662: pDC->Attach(printDialog.GetPrinterDC());
663:
664: xPage = pDC->GetDeviceCaps(HORZRES);
665: yPage = pDC->GetDeviceCaps(VERTRES);
666:
1.1.1.3 ! root 667: if (pDC->SetAbortProc(lpfnAbortProc) < 0)
! 668: {
! 669: delete pDC;
! 670: return FALSE;
! 671: }
! 672:
1.1 root 673: // The chart main window has to be disabled while printing so that
674: // the user can't change data.
675: //
676: EnableWindow(FALSE);
677:
678: // Set up the printer abort box and its window procedure control
679: // variables
680: //
681: bUserAbort = FALSE;
682: m_pPrintDlg = new CPrintDlgBox;
683: hPrintDlg = m_pPrintDlg->m_hWnd;
684:
1.1.1.3 ! root 685: if (pDC->StartDoc(szMessage) >= 0 && pDC->StartPage() >= 0)
1.1 root 686: {
687: oldX = m_cxClient;
688: m_cxClient = xPage;
689: oldY = m_cyClient;
690: m_cyClient = yPage;
691:
692: RenderChart(pDC);
693:
1.1.1.3 ! root 694: if (pDC->EndPage() >= 0)
1.1 root 695: {
696: pDC->EndDoc();
697: }
698: else
699: {
700: bError = TRUE;
701: }
702: }
703: else
704: {
705: bError = TRUE;
706: }
707:
1.1.1.3 ! root 708: if (bError)
! 709: pDC->AbortDoc();
! 710:
1.1 root 711: m_cxClient = oldX;
712: m_cyClient = oldY;
713:
714: // Now that we're done, we can now allow the user access to the frame
715: // window again. bUserAbort is set in the AbortProc just before this
716: // function.
717: //
718: if (!bUserAbort)
719: {
720: EnableWindow(TRUE);
721: }
722:
723: delete pDC;
724: delete m_pPrintDlg;
725:
726: return !bError && !bUserAbort;
727: }
728:
729: // CmdFileSave:
730: // User wants to save current chart data in the current file
731: //
732: void CChartWnd::CmdFileSave()
733: {
734: if (!m_bUntitled)
735: {
736: SaveFile(FALSE);
737: UpdateMenu();
738: }
739: }
740:
741: // CmdFileSaveAs:
742: // User wants to save current chart data in a named file
743: //
744: void CChartWnd::CmdFileSaveAs()
745: {
746: SaveFile(TRUE);
747: UpdateMenu();
748: }
749:
750: // CmdFileOpen
751: // User wants to open an existing file and read in chart data
752: //
753: void CChartWnd::CmdFileOpen()
754: {
755: // Create data structure if there is none.
756: //
757: if (m_pChartObject != NULL)
758: {
759: if (m_pChartObject->m_bDirty)
760: {
761: if (MessageBox("Save existing data?", "Chart",
762: MB_YESNO | MB_ICONQUESTION) == IDYES)
763: {
764: SaveFile(m_bUntitled);
765: }
766: }
767: }
768:
769: // Get the data.
770: //
771: ReadFile();
772:
773: // If read failed, or there's no data to read, clean up.
774: //
775:
776: if (!m_bChartSerializedOK)
777: {
778: // chart deserialization failed and chart is in an
779: // inconsistent state -- don't delete. Just null
780: // the pointer and suffer a memory leak.
781:
782: m_pChartObject = NULL;
783: }
784: else
785: {
786: if ((m_pChartObject->m_pChartData == NULL) ||
787: m_pChartObject->m_pChartData->IsEmpty())
788: {
789: delete m_pChartObject;
790: m_pChartObject = NULL;
791: }
792: }
793:
794: // Update the frame menu and client area
795: //
796: UpdateMenu();
797: Invalidate(TRUE);
798: }
799:
800: // OnAbout:
801: //
802: void CChartWnd::OnAbout()
803: {
804: CModalDialog aboutBox("AboutBox");
805: aboutBox.DoModal();
806: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.