|
|
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: {
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: #ifndef _NTWIN
685: if (pDC->StartDoc(szMessage) > 0)
686: #else
687: DOCINFO di;
688: di.cbSize = sizeof(di);
689: di.lpszDocName = szMessage;
690: di.lpszOutput = NULL;
691: if (pDC->StartDoc(&di) > 0)
692: #endif
693: {
694: oldX = m_cxClient;
695: m_cxClient = xPage;
696: oldY = m_cyClient;
697: m_cyClient = yPage;
698:
699: RenderChart(pDC);
700:
701: if (pDC->EndPage() > 0)
702: {
703: pDC->EndDoc();
704: }
705: else
706: {
707: bError = TRUE;
708: }
709: }
710: else
711: {
712: bError = TRUE;
713: }
714:
715: m_cxClient = oldX;
716: m_cyClient = oldY;
717:
718: // Now that we're done, we can now allow the user access to the frame
719: // window again. bUserAbort is set in the AbortProc just before this
720: // function.
721: //
722: if (!bUserAbort)
723: {
724: EnableWindow(TRUE);
725: }
726:
727: delete pDC;
728: delete m_pPrintDlg;
729:
730: return !bError && !bUserAbort;
731: }
732:
733: // CmdFileSave:
734: // User wants to save current chart data in the current file
735: //
736: void CChartWnd::CmdFileSave()
737: {
738: if (!m_bUntitled)
739: {
740: SaveFile(FALSE);
741: UpdateMenu();
742: }
743: }
744:
745: // CmdFileSaveAs:
746: // User wants to save current chart data in a named file
747: //
748: void CChartWnd::CmdFileSaveAs()
749: {
750: SaveFile(TRUE);
751: UpdateMenu();
752: }
753:
754: // CmdFileOpen
755: // User wants to open an existing file and read in chart data
756: //
757: void CChartWnd::CmdFileOpen()
758: {
759: // Create data structure if there is none.
760: //
761: if (m_pChartObject != NULL)
762: {
763: if (m_pChartObject->m_bDirty)
764: {
765: if (MessageBox("Save existing data?", "Chart",
766: MB_YESNO | MB_ICONQUESTION) == IDYES)
767: {
768: SaveFile(m_bUntitled);
769: }
770: }
771: }
772:
773: // Get the data.
774: //
775: ReadFile();
776:
777: // If read failed, or there's no data to read, clean up.
778: //
779:
780: if (!m_bChartSerializedOK)
781: {
782: // chart deserialization failed and chart is in an
783: // inconsistent state -- don't delete. Just null
784: // the pointer and suffer a memory leak.
785:
786: m_pChartObject = NULL;
787: }
788: else
789: {
790: if ((m_pChartObject->m_pChartData == NULL) ||
791: m_pChartObject->m_pChartData->IsEmpty())
792: {
793: delete m_pChartObject;
794: m_pChartObject = NULL;
795: }
796: }
797:
798: // Update the frame menu and client area
799: //
800: UpdateMenu();
801: Invalidate(TRUE);
802: }
803:
804: // OnAbout:
805: //
806: void CChartWnd::OnAbout()
807: {
808: CModalDialog aboutBox("AboutBox");
809: aboutBox.DoModal();
810: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.