home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / winnt / perfmon / grafdata.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  39KB  |  1,469 lines

  1. /*****************************************************************************
  2.  *
  3.  *  Grafdata.c - This module handles the non-drawing functions of the graph,
  4.  *      such as allocating linked structures and their memory, freeing it,
  5.  *      unlinking, starting and stopping the timer,
  6.  *      setting up the first graph (CPU), and all the numeric functions for
  7.  *      the different counter types.
  8.  *
  9.  *  Microsoft Confidential
  10.  *  Copyright (c) 1992-1997 Microsoft Corporation
  11.  *
  12.  *
  13.  ****************************************************************************/
  14.  
  15.  
  16. //==========================================================================//
  17. //                                  Includes                                //
  18. //==========================================================================//
  19.  
  20. #include <stdio.h>      // for sprintf
  21.  
  22. #include "perfmon.h"       // main perfmon declarations
  23. #include "grafdata.h"      // external declarations for this file
  24. #include <float.h>         // for FLT_MAX constant
  25.  
  26. #include "addline.h"       // for AddLine, EditLine
  27. #include "counters.h"      // for CounterEntry
  28. #include "graph.h"         // for SizeGraphComponents
  29. #include "pmemory.h"       // for MemoryXXX (mallloc-type) routines
  30. #include "perfdata.h"      // for UpdateLines
  31. #include "playback.h"      // for PlayingBackLog, PlaybackLines
  32. #include "legend.h"
  33. #include "system.h"        // for SystemGet
  34. #include "utils.h"
  35. #include "line.h"          // for LineFree
  36. #include "valuebar.h"      // for StatusTimer
  37.  
  38. #include "fileopen.h"      // for FileGetName
  39. #include "fileutil.h"      // for FileRead...
  40. #include "menuids.h"       // for IDM_VIEWCHART
  41. #include "perfmops.h"      // for ExportFileHeader
  42. #include "status.h"        // for StatusLineReady
  43.  
  44. extern BOOL SaveFileHandler(HWND hWnd,DWORD type) ;
  45.  
  46. // this macro is used in doing a simple DDA (Digital Differential Analyzer)
  47. // * 10 + 5 is to make the result round up with .5
  48. #define DDA_DISTRIBUTE(TotalTics, numOfData) \
  49.    ((TotalTics * 10 / numOfData) + 5) / 10
  50.  
  51. #define szSmallValueFormat         TEXT("%10.3f")
  52. #define szLargeValueFormat         TEXT("%10.0f")
  53.  
  54. //==========================================================================//
  55. //                                Local Data                                //
  56. //==========================================================================//
  57.  
  58.  
  59.  
  60.  
  61. //==========================================================================//
  62. //                              Local Functions                             //
  63. //==========================================================================//
  64.  
  65.  
  66. /****************************************************************************
  67.  * eUpdateMinMaxAve -
  68.  ****************************************************************************/
  69. void eUpdateMinMaxAve (FLOAT eValue, PLINESTRUCT pLineStruct, INT iValidValues,
  70.    INT iTotalValidPoints, INT iDataPoint, INT gMaxPoints)
  71. {
  72. INT     i ;
  73. INT     iDataNum = iTotalValidPoints ;
  74. FLOAT   eMin,
  75.         eMax,
  76.         eSum,
  77.         eNewValue ;
  78.  
  79.  
  80.    eMax = eMin = eValue ;
  81.  
  82.    if (PlayingBackLog())
  83.       {
  84.       eSum = (FLOAT) 0.0 ;
  85.       }
  86.    else
  87.       {
  88.       eSum = eValue ;
  89.       }
  90.  
  91.    if (iValidValues == iTotalValidPoints)
  92.       {
  93.       for (i=0 ; i < iValidValues ; i++)
  94.          {
  95.          if (i == iDataPoint)
  96.             {
  97.             // skip the data point we are going to replace
  98.             continue ;
  99.             }
  100.  
  101.          eNewValue = pLineStruct->lnValues[i] ;
  102.          
  103.          eSum += eNewValue ;
  104.  
  105.          if (eNewValue > eMax)
  106.             {
  107.             eMax = eNewValue ;
  108.             }
  109.  
  110.          if (eNewValue < eMin)
  111.             {
  112.             eMin = eNewValue ;
  113.             }
  114.          }
  115.       }
  116.    else
  117.       {
  118.       // special case when we start the new line in the middle of the chart
  119.       for (i = iDataPoint, iTotalValidPoints-- ;
  120.           iTotalValidPoints > 0 ;
  121.           iTotalValidPoints-- )
  122.          {
  123.          i-- ;
  124.          if (i < 0)
  125.             {
  126.             // for the wrap-around case..
  127.             i = gMaxPoints - 1 ;
  128.             }
  129.  
  130.          if (i == iDataPoint)
  131.             {
  132.             // skip the data point we are going to replace
  133.             continue ;
  134.             }
  135.          eNewValue = pLineStruct->lnValues[i] ;
  136.  
  137.          eSum += eNewValue ;
  138.  
  139.          if (eNewValue > eMax)
  140.             {
  141.             eMax = eNewValue ;
  142.             }
  143.  
  144.          if (eNewValue < eMin)
  145.             {
  146.             eMin = eNewValue ;
  147.             }
  148.          }
  149.       }
  150.  
  151.    pLineStruct->lnMinValue = eMin ;
  152.    pLineStruct->lnMaxValue = eMax ;
  153.  
  154.    if (iDataNum)
  155.       {
  156.       pLineStruct->lnAveValue = eSum / (FLOAT) iDataNum ;
  157.       }
  158.    else
  159.       {
  160.       pLineStruct->lnAveValue = (FLOAT) 0.0 ;
  161.       }
  162. }
  163.  
  164.  
  165. // UpdateValueBarData is used to update the value bar data
  166. // It is called when the user switches to a diff. line from
  167. // the legned window.
  168. VOID UpdateValueBarData (PGRAPHSTRUCT pGraph)
  169.    {
  170.    PLINESTRUCT    pCurrentGraphLine ;   
  171.    INT            KnownValue,
  172.                   MaxValues,
  173.                   iValidValues,
  174.                   iDataPoint ;
  175.    FLOAT          eValue ;
  176.    pCurrentGraphLine = CurrentGraphLine (hWndGraph) ;
  177.  
  178.    if (!pCurrentGraphLine || pCurrentGraphLine->bFirstTime)
  179.       {
  180.       // we have not collect enough samples
  181.       return ;
  182.       }
  183.  
  184.    KnownValue = pGraph->gKnownValue ;
  185.    MaxValues = pGraph->gMaxValues ;
  186.  
  187.    // The valid values is the number of valid entries in the
  188.    // data buffer.  After we wrap the buffer, all the values are
  189.    // valid.
  190.    iValidValues = pGraph->gTimeLine.iValidValues ;
  191.  
  192.    // Get the index to the data point we are updating.
  193.  
  194.    iDataPoint = KnownValue % MaxValues ;
  195.    eValue = pCurrentGraphLine->lnValues[iDataPoint] ;
  196.  
  197.    // get the statistical data for this line
  198.    eUpdateMinMaxAve(eValue, pCurrentGraphLine,
  199.       iValidValues, pCurrentGraphLine->lnValidValues,
  200.       iDataPoint, MaxValues) ;
  201.    }  // UpdateValueBarData
  202.  
  203.  
  204. VOID UpdateLGData (PGRAPHSTRUCT pGraph)
  205.    {
  206.    PLINESTRUCT    pLine ;               
  207.    PLINESTRUCT    pCurrentGraphLine ;   
  208.    INT            KnownValue,
  209.                   MaxValues,
  210.                   iValidValues,
  211.                   iDataPoint ;
  212.    FLOAT          eValue ;
  213.    // Known Value is the where data is placed in the buffer.
  214.    pGraph->gKnownValue++;
  215.  
  216.    KnownValue = pGraph->gKnownValue ;
  217.  
  218.    // Update the high water mark for valid data in the lnValues
  219.    // (aka DataPoint) buffer.
  220.  
  221.  
  222.    MaxValues = pGraph->gMaxValues ;
  223.  
  224.    // The valid values is the number of valid entries in the
  225.    // data buffer.  After we wrap the buffer, all the values are
  226.    // valid.
  227.    iValidValues = pGraph->gTimeLine.iValidValues ;
  228.  
  229.    if (iValidValues < MaxValues)
  230.        iValidValues = (KnownValue % MaxValues) + 1 ;
  231.  
  232.    pGraph->gTimeLine.iValidValues = iValidValues ;
  233.  
  234.    // Get the index to the data point we are updating.
  235.  
  236.    iDataPoint = KnownValue % MaxValues ;
  237.  
  238.    // loop through lines,
  239.    // If one of the lines is highlighted then do the calculations
  240.    // for average, min, & max on that line.
  241.  
  242.    pCurrentGraphLine = CurrentGraphLine (hWndGraph) ;
  243.  
  244.    for (pLine=pGraph->pLineFirst; pLine; pLine=pLine->pLineNext)
  245.      { // for
  246.  
  247.      if (pLine->bFirstTime)
  248.          {
  249.          // skip until we have collect enough samples to plot the first data
  250.          continue ;
  251.          }
  252.  
  253.      if (pLine->lnValidValues < MaxValues)
  254.          {   
  255.          (pLine->lnValidValues)++ ;
  256.          }
  257.    
  258.      // Get the new value for this line.
  259.      eValue = CounterEntry (pLine) ;
  260.  
  261.      if (pLine == pCurrentGraphLine)
  262.         {  // if
  263.         // get the statistical data for this line
  264.         eUpdateMinMaxAve (eValue, pLine, 
  265.                                  iValidValues, pLine->lnValidValues,
  266.                                  iDataPoint, MaxValues) ;
  267.         }  // if
  268.       
  269.      // Now put the new value into the data array
  270.      pLine->lnValues[iDataPoint] = eValue ;
  271.      }
  272.    
  273.    GetLocalTime (&(pGraph->pDataTime[iDataPoint])) ; 
  274.    }  // UpdateLGData
  275.  
  276.  
  277.  
  278. BOOL HandleGraphTimer (void)
  279.    {
  280.    PGRAPHSTRUCT pGraph;
  281.  
  282.    //NOTE: get a strategy for these "no-go" states
  283.    if (!(pGraph = pGraphs) || !pGraphs->pSystemFirst)
  284.        return(FALSE);
  285.  
  286.  
  287.    if (!UpdateLines(&(pGraphs->pSystemFirst), pGraphs->pLineFirst))
  288.         return (TRUE) ;
  289.  
  290.    UpdateLGData(pGraph);
  291.  
  292.    return(TRUE);
  293.    }
  294.  
  295.  
  296. VOID ClearGraphTimer(PGRAPHSTRUCT pGraph)
  297.    {
  298.    KillTimer(pGraph->hWnd, GRAPH_TIMER_ID);
  299.    }
  300.  
  301.  
  302. VOID SetGraphTimer(PGRAPHSTRUCT pGraph)
  303.    {
  304.    SetTimer(pGraph->hWnd, GRAPH_TIMER_ID, pGraph->gInterval, NULL) ;
  305.    }
  306.  
  307. VOID ResetGraphTimer(PGRAPHSTRUCT pGraph)
  308. {
  309.     KillTimer(pGraph->hWnd, GRAPH_TIMER_ID);
  310.     SetGraphTimer(pGraph);
  311. }
  312.  
  313.  
  314. VOID GetGraphConfig(PGRAPHSTRUCT pGraph)
  315. {
  316.  
  317.     LoadRefreshSettings(pGraph);
  318.     LoadLineGraphSettings(pGraph);
  319.  
  320.  
  321.     // Init the structure
  322.     pGraph->pLineFirst = NULL;
  323.  
  324.     //NOTE: put the rest of this in Config
  325.  
  326.     pGraph->gOptions.bLegendChecked    = TRUE;
  327.     pGraph->gOptions.bMenuChecked      = TRUE;
  328.     pGraph->gOptions.bLabelsChecked    = TRUE;
  329.     pGraph->gOptions.bVertGridChecked  = FALSE;
  330.     pGraph->gOptions.bHorzGridChecked  = FALSE;
  331.     pGraph->gOptions.bStatusBarChecked = TRUE;
  332.     pGraph->gOptions.GraphVGrid        = TRUE;
  333.     pGraph->gOptions.GraphHGrid        = TRUE;
  334.     pGraph->gOptions.HistVGrid         = TRUE;
  335.     pGraph->gOptions.HistHGrid         = TRUE;
  336.  
  337.     pGraph->gOptions.iGraphOrHistogram = LINE_GRAPH;       // vs. BAR_GRAPH
  338.     pGraph->gOptions.iVertMax = DEF_GRAPH_VMAX;
  339.  
  340.     return;
  341. }
  342.  
  343.  
  344. BOOL InsertGraph (HWND hWnd)
  345.    {
  346.    PGRAPHSTRUCT    pGraph;
  347.  
  348.    pGraph = MemoryAllocate (sizeof (GRAPHSTRUCT)) ;
  349.    if (!pGraph)
  350.       return (FALSE) ;
  351.  
  352.  
  353.    pGraphs = pGraph;
  354.  
  355.    
  356.    GetGraphConfig(pGraph);
  357.    pGraph->bManualRefresh = FALSE ;
  358.  
  359.    pGraph->gMaxValues = DEFAULT_MAX_VALUES;
  360.    pGraph->pptDataPoints = 
  361.       (PPOINT) MemoryAllocate (sizeof (POINT) * pGraph->gMaxValues) ;
  362.  
  363.    pGraph->pDataTime =
  364.       (SYSTEMTIME *) MemoryAllocate (sizeof(SYSTEMTIME) * pGraph->gMaxValues) ;
  365.  
  366.    pGraph->hWnd = hWnd ;
  367.    pGraph->bModified = FALSE ;
  368.  
  369.    pGraph->Visual.iColorIndex = 0 ;
  370.    pGraph->Visual.iWidthIndex = 0 ;
  371.    pGraph->Visual.iStyleIndex = 0 ;
  372.  
  373.    return(TRUE) ;
  374.    }
  375.  
  376.  
  377.  
  378. void PlaybackSetGraphLines (HWND hWndChart, 
  379.                             PLINE pLineFirst, 
  380.                             int iDisplayTic,
  381.                             int iLogTic,
  382.                             BOOL CalcData)
  383.    {
  384.    PLINE          pLine ;
  385.    FLOAT          eValue ;
  386.  
  387.    for (pLine = pLineFirst ;
  388.         pLine ;
  389.         pLine = pLine->pLineNext)
  390.       {  // for
  391.       eValue = CounterEntry (pLine) ;
  392.       pLine->lnValues[iDisplayTic] = eValue ;
  393.       pLine->aiLogIndexes[iDisplayTic] = iLogTic ;
  394.  
  395.       // only need to do this on request.
  396.       if (CalcData)
  397.          {
  398.          eUpdateMinMaxAve (eValue, pLine, iDisplayTic, iDisplayTic,
  399.             iDisplayTic, iDisplayTic) ;
  400.          }  // if
  401.       }  // for
  402.    }  // PlaybackSetGraphLines
  403.  
  404.  
  405.  
  406. BOOL ChartInsertLine (PGRAPHSTRUCT pGraph, 
  407.                       PLINE pLine)
  408. /*
  409.    Effect:        Insert the line pLine into the graph pGraph and 
  410.                   allocate space for the graph's number of values.
  411.  
  412.    Returns:       Whether the line could be added and space allocated.
  413.  
  414.    See Also:      LineAllocate (line.c), ChartDeleteLine.
  415. */
  416.    {  // ChartInsertLine
  417.    PLINE          pLineEquivalent ;
  418.    INT            i ;
  419.    FLOAT          *pTempPts;
  420.    HPEN           tempPen ;
  421.  
  422.    pGraph->bModified = TRUE ;
  423.  
  424.    pLineEquivalent = FindEquivalentLine (pLine, pGraph->pLineFirst) ;
  425.    if (pLineEquivalent)
  426.       {
  427.       pLineEquivalent->Visual = pLine->Visual ;
  428.       pLineEquivalent->iScaleIndex = pLine->iScaleIndex ;
  429.       pLineEquivalent->eScale = pLine->eScale ;
  430.  
  431.       tempPen = pLineEquivalent->hPen ;
  432.       pLineEquivalent->hPen =  pLine->hPen ;
  433.       pLine->hPen = tempPen ;
  434.       return FALSE ;
  435.       }
  436.    else
  437.       {
  438.       if (!pGraph->pLineFirst && !PlayingBackLog())
  439.          {
  440.          SetGraphTimer (pGraph) ;
  441.          }
  442.  
  443.       if (!SystemAdd (&pGraph->pSystemFirst, pLine->lnSystemName))
  444.         return FALSE;
  445.  
  446.       LineAppend (&pGraph->pLineFirst, pLine) ;
  447.  
  448.       pLine->lnMinValue = FLT_MAX ;
  449.       pLine->lnMaxValue = - FLT_MAX ;
  450.       pLine->lnAveValue = 0.0F ;
  451.       pLine->lnValidValues = 0 ;
  452.  
  453.       pLine->lnValues = 
  454.          (FLOAT *) MemoryAllocate (sizeof (FLOAT) * pGraph->gMaxValues) ;
  455.       
  456.       for (i = pGraph->gMaxValues, pTempPts = pLine->lnValues ;
  457.            i > 0 ;
  458.            i-- )
  459.          {
  460.          *pTempPts++ = (FLOAT) 0.0 ;
  461.          }
  462.  
  463.       if (PlayingBackLog ())
  464.          {
  465.          pLine->aiLogIndexes =
  466.             (int *) MemoryAllocate (sizeof (LONG) * pGraph->gMaxValues) ;
  467.          }
  468.  
  469.       // Add the line to the legend, resize the legend window, and then
  470.       // select the new line as the current legend item. Do it in this 
  471.       // sequence to avoid the legend scroll bar momentarily appearing and
  472.       // then disappearing, since the resize will obviate the scroll bar.
  473.  
  474.       LegendAddItem (hWndGraphLegend, pLine) ;
  475.  
  476.       if (!bDelayAddAction)
  477.          {
  478.          SizeGraphComponents (hWndGraph) ;
  479.          LegendSetSelection (hWndGraphLegend, 
  480.                           LegendNumItems (hWndGraphLegend) - 1) ;
  481.  
  482.          if (PlayingBackLog ())
  483.             PlaybackChart (pGraph->hWnd) ;
  484.          }
  485.       }
  486.  
  487.    return (TRUE) ;
  488.    }  // ChartInsertLine
  489.  
  490.  
  491. VOID ChartDeleteLine (PGRAPHSTRUCT pGraph, 
  492.                       PLINESTRUCT pLine)
  493.    {
  494.    PLINESTRUCT npLine;
  495.  
  496.  
  497.    pGraph->bModified = TRUE ;
  498.  
  499.    if (pGraph->pLineFirst == pLine)
  500.        pGraph->pLineFirst = pLine->pLineNext;
  501.    else
  502.    {
  503.        for (npLine = pGraph->pLineFirst; npLine; npLine = npLine->pLineNext)
  504.        {
  505.            if (npLine->pLineNext == pLine)
  506.                npLine->pLineNext = pLine->pLineNext;
  507.        }
  508.    }
  509.  
  510.    if (!pGraph->pLineFirst)
  511.       {
  512.       ResetGraph (pGraph) ;
  513.       }
  514.    else
  515.       {
  516.       BuildValueListForSystems (
  517.          pGraph->pSystemFirst,
  518.          pGraph->pLineFirst) ;
  519.       }
  520.  
  521.    // Delete the legend entry for this line.
  522.    // If the line was highlighted then this will undo the highlight.
  523.  
  524.    LegendDeleteItem (hWndGraphLegend, pLine) ;
  525.  
  526.    LineFree (pLine) ;
  527.  
  528.    SizeGraphComponents (hWndGraph) ;
  529.  
  530.  
  531.    }
  532.  
  533.  
  534. FLOAT Counter_Queuelen(PLINESTRUCT pLine)
  535. {
  536.  
  537.     return((FLOAT)0.0);
  538. //    pLine;
  539. }
  540.  
  541.  
  542. void ClearGraphDisplay (PGRAPHSTRUCT pGraph)
  543.    {
  544.    PLINESTRUCT    pLine;
  545.  
  546.    // reset the timeline data
  547. //   pGraph->gKnownValue = -1 ;
  548.    pGraph->gKnownValue = 0 ;
  549.    pGraph->gTimeLine.iValidValues = 0 ;
  550.    pGraph->gTimeLine.xLastTime = 0 ;
  551.    memset (pGraph->pDataTime, 0, sizeof(SYSTEMTIME) * pGraph->gMaxValues) ;
  552.  
  553.    // loop through lines,
  554.    // If one of the lines is highlighted then do the calculations
  555.    // for average, min, & max on that line.
  556.  
  557.    for (pLine=pGraph->pLineFirst; pLine; pLine=pLine->pLineNext)
  558.       { // for
  559.  
  560.       pLine->bFirstTime = 2 ;
  561.       pLine->lnMinValue = FLT_MAX ;
  562.       pLine->lnMaxValue = - FLT_MAX ;
  563.       pLine->lnAveValue = 0.0F ;
  564.       pLine->lnValidValues = 0 ;
  565.       memset (pLine->lnValues, 0, sizeof(FLOAT) * pGraph->gMaxValues) ;
  566.       }
  567.  
  568.    StatusTimer (hWndGraphStatus, TRUE) ;
  569.    WindowInvalidate (hWndGraphDisplay) ;
  570.    }
  571.  
  572. void ResetGraphView (HWND hWndGraph)
  573.    {
  574.    PGRAPHSTRUCT      pGraph ;
  575.  
  576.    pGraph = GraphData (hWndGraph) ;
  577.  
  578.  
  579.    if (!pGraph)
  580.       {
  581.       return ;
  582.       }
  583.  
  584.    ChangeSaveFileName (NULL, IDM_VIEWCHART) ;
  585.  
  586.    if (pGraph->pSystemFirst)
  587.       {
  588.       ResetGraph (pGraph) ;
  589.       }
  590.    }  // ResetGraphView
  591.  
  592. void ResetGraph (PGRAPHSTRUCT pGraph)
  593.    {
  594.    ClearGraphTimer (pGraph) ;
  595.    ClearLegend (hWndGraphLegend) ;
  596.    if (pGraph->pLineFirst)
  597.       {
  598.       FreeLines (pGraph->pLineFirst) ;
  599.       pGraph->pLineFirst = NULL ;
  600.       }
  601.  
  602.    if (pGraph->pSystemFirst)
  603.       {
  604.       FreeSystems (pGraph->pSystemFirst) ;
  605.       pGraph->pSystemFirst = NULL ;
  606.       }
  607. //   pGraph->gKnownValue = -1 ;
  608.    pGraph->gKnownValue = 0 ;
  609.    pGraph->gTimeLine.iValidValues = 0 ;
  610.    pGraph->gTimeLine.xLastTime = 0 ;
  611.  
  612.    // reset visual data
  613.    pGraph->Visual.iColorIndex = 0 ;
  614.    pGraph->Visual.iWidthIndex = 0 ;
  615.    pGraph->Visual.iStyleIndex = 0 ;
  616.  
  617.    memset (pGraph->pDataTime, 0, sizeof(SYSTEMTIME) * pGraph->gMaxValues) ;
  618.  
  619.    SizeGraphComponents (hWndGraph) ;
  620.    InvalidateRect(hWndGraph, NULL, TRUE) ;
  621.    }
  622.  
  623.  
  624.  
  625. void PlaybackChart (HWND hWndChart)
  626.    {  // PlaybackChart
  627.    int            iDisplayTics ;       // num visual points to display
  628.    int            iDisplayTic ;
  629.    int            iLogTic ;
  630.    int            iLogTicsMove ;
  631.    BOOL           bFirstTime = TRUE;
  632.    int            iLogTicsRemaining ;
  633.  
  634.    if (!pGraphs->pLineFirst)
  635.       {
  636.       // no line to playback
  637.       return ;
  638.       }
  639.  
  640.    iLogTicsRemaining = PlaybackLog.iSelectedTics ;
  641.  
  642.  
  643.    // we only have iDisplayTics-1 points since
  644.    // we have to use the first two sample points to
  645.    // get the first data points.
  646.    if (iLogTicsRemaining <= pGraphs->gMaxValues)
  647.       {
  648.       iDisplayTics = iLogTicsRemaining ;
  649.       pGraphs->gTimeLine.iValidValues = iDisplayTics - 1 ;
  650.       }
  651.    else
  652.       {
  653.       iDisplayTics = pGraphs->gMaxValues ;
  654.       pGraphs->gTimeLine.iValidValues = iDisplayTics ;
  655.       }
  656.  
  657.    iDisplayTic = -1 ;
  658.    iLogTic = PlaybackLog.StartIndexPos.iPosition ;
  659.  
  660.    while (iDisplayTics)
  661.       {
  662.  
  663.       PlaybackLines (pGraphs->pSystemFirst, 
  664.                      pGraphs->pLineFirst, 
  665.                      iLogTic) ;
  666.  
  667.       if (bFirstTime)
  668.          {
  669.          bFirstTime = FALSE ;
  670.  
  671.          // get the second sample data to form the first data point
  672.          iLogTic++ ;
  673.          iLogTicsRemaining-- ;
  674.          PlaybackLines (pGraphs->pSystemFirst, 
  675.                         pGraphs->pLineFirst, 
  676.                         iLogTic) ;
  677.          }
  678.       iDisplayTic++ ;
  679.       PlaybackSetGraphLines (hWndChart, pGraphs->pLineFirst, 
  680.          iDisplayTic, iLogTic, (iDisplayTics == 1)) ;
  681.  
  682.       // setup DDA to get the index of the next sample point
  683.       iLogTicsMove = DDA_DISTRIBUTE (iLogTicsRemaining, iDisplayTics) ;
  684.       iLogTicsRemaining -= iLogTicsMove ;
  685.       iLogTic += iLogTicsMove ;
  686.  
  687.       iDisplayTics-- ;
  688.  
  689.       }  // while
  690.  
  691.    // point to the last value for valuebar display
  692.    pGraphs->gKnownValue = iDisplayTic ;
  693.  
  694.    }  // PlaybackChart
  695.  
  696.  
  697.  
  698. #if 0
  699. PLINESTRUCT CurrentGraphLine (HWND hWndGraph)
  700.    {  // CurrentGraphLine
  701.    UNREFERENCED_PARAMETER (hWndGraph) ;
  702.  
  703.    return (LegendCurrentLine (hWndGraphLegend)) ;
  704.    }
  705. #endif
  706.  
  707.  
  708. BOOL AddChart (HWND hWndParent)
  709.    {
  710.    PLINE pCurrentLine = CurrentGraphLine (hWndGraph) ;
  711.  
  712.    return (AddLine (hWndParent, 
  713.                     &(pGraphs->pSystemFirst), 
  714.                     &(pGraphs->Visual), 
  715.                     pCurrentLine ? pCurrentLine->lnSystemName : NULL,
  716.                     LineTypeChart)) ;
  717.    }
  718.  
  719.  
  720. BOOL EditChart (HWND hWndParent)
  721.    {  // EditChart
  722.    return (EditLine (hWndParent, 
  723.                      &(pGraphs->pSystemFirst), 
  724.                      CurrentGraphLine (hWndGraph),
  725.                      LineTypeChart)) ;
  726.    }
  727.  
  728. void GraphAddAction ()
  729.    {
  730.    PGRAPHSTRUCT      pGraph ;
  731.  
  732.    pGraph = GraphData (hWndGraph) ;
  733.  
  734.    SizeGraphComponents (hWndGraph) ;
  735.    
  736.    LegendSetSelection (hWndGraphLegend,
  737.       LegendNumItems (hWndGraphLegend) - 1) ;
  738.  
  739.    if (PlayingBackLog ())
  740.       PlaybackChart (pGraph->hWnd) ;
  741.    }
  742.  
  743. BOOL OpenChartVer1 (HANDLE hFile,
  744.                     DISKCHART *pDiskChart,
  745.                     PGRAPHSTRUCT pGraph)
  746.    {  // OpenChartVer1
  747.    bDelayAddAction = TRUE ;
  748.    pGraph->Visual = pDiskChart->Visual ;
  749.    pGraph->gOptions = pDiskChart->gOptions ;
  750.    pGraph->gMaxValues = pDiskChart->gMaxValues ;
  751.    pGraph->bManualRefresh = pDiskChart->bManualRefresh ;
  752.    pGraphs->gInterval = (INT) (pGraph->gOptions.eTimeInterval * (FLOAT) 1000.0) ;
  753.    ReadLines (hFile, pDiskChart->dwNumLines,
  754.                &(pGraph->pSystemFirst), &(pGraph->pLineFirst), IDM_VIEWCHART) ;
  755.    
  756.    bDelayAddAction = FALSE ;
  757.  
  758.    GraphAddAction () ;
  759.  
  760.    return (TRUE) ;
  761.    }  // OpenChartVer1
  762.  
  763.  
  764.  
  765. BOOL OpenChart (HWND hWndGraph,
  766.                 HANDLE hFile,
  767.                 DWORD dwMajorVersion,
  768.                 DWORD dwMinorVersion,
  769.                 BOOL bChartFile)
  770.    {  // OpenChart
  771.    PGRAPHSTRUCT   pGraph ;
  772.    DISKCHART      DiskChart ;
  773.    BOOL           bSuccess = TRUE ;
  774.  
  775.    pGraph = pGraphs ;
  776.    if (!pGraph)
  777.       {
  778.       bSuccess = FALSE ;
  779.       goto Exit0 ;
  780.       }
  781.  
  782.    if (!FileRead (hFile, &DiskChart, sizeof (DISKCHART)))
  783.       {
  784.       bSuccess = FALSE ;
  785.       goto Exit0 ;
  786.       }
  787.  
  788.  
  789.    switch (dwMajorVersion)
  790.       {
  791.       case (1):
  792.  
  793.          SetHourglassCursor() ;
  794.          
  795.          ResetGraphView (hWndGraph) ;
  796.  
  797.          OpenChartVer1 (hFile, &DiskChart, pGraph) ;
  798.  
  799.          // change to chart view if we are opening a 
  800.          // chart file
  801.          if (bChartFile && iPerfmonView != IDM_VIEWCHART)
  802.             {
  803.             SendMessage (hWndMain, WM_COMMAND, (LONG)IDM_VIEWCHART, 0L) ;
  804.             }
  805.  
  806.          if (iPerfmonView == IDM_VIEWCHART)
  807.             {
  808.             SetPerfmonOptions (&DiskChart.perfmonOptions) ;
  809.             }
  810.          
  811.          SetArrowCursor() ;
  812.  
  813.          break ;
  814.       }  // switch
  815.  
  816. Exit0:
  817.  
  818.    if (bChartFile)
  819.       {
  820.       CloseHandle (hFile) ;
  821.       }
  822.  
  823.    return (bSuccess) ;
  824.    }  // OpenChart
  825.  
  826. BOOL SaveChart (HWND hWndGraph, HANDLE hInputFile, BOOL bGetFileName)
  827.    {
  828.    PGRAPHSTRUCT   pGraph ;
  829.    PLINE          pLine ;
  830.    HANDLE         hFile ;
  831.    DISKCHART      DiskChart ;
  832.    PERFFILEHEADER FileHeader ;
  833.    TCHAR          szFileName [256] ;
  834.    BOOL           newFileName = FALSE ;
  835.  
  836.    if (hInputFile)
  837.       {
  838.       // use the input file handle if it is available
  839.       // this is the case for saving workspace data
  840.       hFile = hInputFile ;
  841.       }
  842.    else
  843.       {
  844.       if (pChartFullFileName)
  845.          {
  846.          lstrcpy (szFileName, pChartFullFileName) ;
  847.          }
  848.       if (bGetFileName || pChartFullFileName == NULL)
  849.          {
  850. //         if (!pChartFullFileName)
  851. //            {
  852. //            StringLoad (IDS_GRAPH_FNAME, szFileName) ;
  853. //            }
  854.  
  855.          if (!FileGetName (hWndGraph, IDS_CHARTFILE, szFileName))
  856.             {
  857.             return (FALSE) ;
  858.             }
  859.          newFileName = TRUE ;
  860.          }
  861.  
  862.       hFile = FileHandleCreate (szFileName) ;
  863.  
  864.       if (hFile && newFileName)
  865.          {
  866.          ChangeSaveFileName (szFileName, IDM_VIEWCHART) ;
  867.          }
  868.       else if (!hFile)
  869.          {
  870.          DlgErrorBox (hWndGraph, ERR_CANT_OPEN, szFileName) ;
  871.          }
  872.       }
  873.  
  874.    if (!hFile)
  875.       return (FALSE) ;
  876.  
  877.    pGraph = pGraphs ;
  878.    if (!pGraph)
  879.       {
  880.       if (!hInputFile)
  881.          {
  882.          CloseHandle (hFile) ;
  883.          }
  884.       return (FALSE) ;
  885.       }
  886.  
  887.    if (!hInputFile)
  888.       {
  889.       // only need to write file header if not workspace 
  890.       memset (&FileHeader, 0, sizeof (FileHeader)) ;
  891.       lstrcpy (FileHeader.szSignature, szPerfChartSignature) ;
  892.       FileHeader.dwMajorVersion = ChartMajorVersion ;
  893.       FileHeader.dwMinorVersion = ChartMinorVersion ;
  894.    
  895.       if (!FileWrite (hFile, &FileHeader, sizeof (PERFFILEHEADER)))
  896.          {
  897.          goto Exit0 ;
  898.          }
  899.       }
  900.  
  901.    DiskChart.Visual = pGraph->Visual ;
  902.    DiskChart.gOptions = pGraph->gOptions ;
  903.    DiskChart.gMaxValues = pGraph->gMaxValues ;
  904.    DiskChart.dwNumLines = NumLines (pGraph->pLineFirst) ;
  905.    DiskChart.bManualRefresh = pGraph->bManualRefresh ;
  906.    DiskChart.perfmonOptions = Options ;
  907.  
  908.    if (!FileWrite (hFile, &DiskChart, sizeof (DISKCHART)))
  909.       {
  910.       goto Exit0 ;
  911.       }
  912.  
  913.    for (pLine = pGraph->pLineFirst ;
  914.         pLine ;
  915.         pLine = pLine->pLineNext)
  916.       {  // for
  917.       if (!WriteLine (pLine, hFile))
  918.          {
  919.          goto Exit0 ;
  920.          }
  921.       }  // for
  922.  
  923.    if (!hInputFile)
  924.       {
  925.       CloseHandle (hFile) ;
  926.       }
  927.  
  928.    return (TRUE) ;
  929.  
  930. Exit0:
  931.    if (!hInputFile)
  932.       {
  933.       CloseHandle (hFile) ;
  934.  
  935.       // only need to report error if not workspace 
  936.       DlgErrorBox (hWndGraph, ERR_SETTING_FILE, szFileName) ;
  937.       }
  938.    return (FALSE) ;
  939.  
  940.    }  // SaveChart
  941.  
  942. #define TIME_TO_WRITE 2
  943. BOOL ExportChartLabels (HANDLE hFile, PGRAPHSTRUCT pGraph)
  944. {
  945.    TCHAR          UnicodeBuff [LongTextLen] ;
  946.    CHAR           TempBuff [LongTextLen * 2] ;
  947.    int            StringLen ;
  948.    PLINESTRUCT    pLine;
  949.    int            TimeToWriteFile ;
  950.    int            iIndex ;
  951.    LPTSTR         lpItem ;
  952.  
  953.    for (iIndex = 0 ; iIndex < 5 ; iIndex++)
  954.       {
  955.       // for iIndex == 0, get counter name 
  956.       // for iIndex == 1, get instance name
  957.       // for iIndex == 2, get parent name
  958.       // for iIndex == 3, get object name 
  959.       // for iIndex == 4, get computer name
  960.       if (iIndex == 4)
  961.          {
  962.          // the last label field, write date/time labels
  963.          strcpy (TempBuff, LineEndStr) ;
  964.          StringLen = strlen (TempBuff) ;
  965.          StringLoad (IDS_EXPORT_DATE, UnicodeBuff) ;
  966.          ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  967.          strcat (TempBuff, pDelimiter) ;
  968.          StringLen = strlen (TempBuff) ;
  969.       
  970.          StringLoad (IDS_EXPORT_TIME, UnicodeBuff) ;
  971.          ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  972.          strcat (TempBuff, pDelimiter) ;
  973.          StringLen = strlen (TempBuff) ;
  974.          }
  975.       else
  976.          {
  977.          strcpy (TempBuff, LineEndStr) ;
  978.          strcat (TempBuff, pDelimiter) ;
  979.          strcat (TempBuff, pDelimiter) ;
  980.          StringLen = strlen (TempBuff) ;
  981.          }
  982.  
  983.       TimeToWriteFile = 0 ;
  984.  
  985.       for (pLine=pGraph->pLineFirst; pLine; pLine=pLine->pLineNext)
  986.          {
  987.          switch (iIndex)
  988.             {
  989.             case 0:
  990.                lpItem = (LPTSTR) pLine->lnCounterName ;
  991.                break ;
  992.  
  993.             case 1:
  994.                lpItem = (LPTSTR) pLine->lnInstanceName ;
  995.                break ;
  996.  
  997.             case 2:
  998.                lpItem = (LPTSTR) pLine->lnPINName ;
  999.                break ;
  1000.  
  1001.             case 3:
  1002.                lpItem = (LPTSTR) pLine->lnObjectName ;
  1003.                break ;
  1004.  
  1005.             case 4:
  1006.                lpItem = (LPTSTR) pLine->lnSystemName ;
  1007.                break ;
  1008.             }
  1009.  
  1010.          if (lpItem)
  1011.             {
  1012.             ConvertUnicodeStr (&TempBuff[StringLen], lpItem) ;
  1013.             }
  1014.          else
  1015.             {
  1016.             TempBuff[StringLen] = '\0' ;
  1017.             }
  1018.          strcat (TempBuff, pDelimiter);
  1019.          StringLen = strlen (TempBuff) ;
  1020.    
  1021.          if (++TimeToWriteFile > TIME_TO_WRITE)
  1022.             {
  1023.             // better write the buffers before they overflow.
  1024.             // there are better ways to check for overflow 
  1025.             // but this is good enough
  1026.    
  1027.             if (!FileWrite (hFile, TempBuff, StringLen))
  1028.                {
  1029.                goto Exit0 ;
  1030.                }
  1031.             StringLen = TimeToWriteFile = 0 ;
  1032.             }
  1033.          }     // for each line
  1034.  
  1035.       if (StringLen)
  1036.          {
  1037.          // write the last block of data
  1038.          if (!FileWrite (hFile, TempBuff, StringLen))
  1039.             {
  1040.             goto Exit0 ;
  1041.             }
  1042.          }
  1043.       }     // for iIndex
  1044.  
  1045.    return (TRUE) ;
  1046.  
  1047. Exit0:
  1048.    return (FALSE) ;
  1049.  
  1050. }  // ExportChartLabels
  1051.  
  1052. BOOL ExportLogChart (HANDLE hFile, PGRAPHSTRUCT pGraph)
  1053. {
  1054.    TCHAR          UnicodeBuff [LongTextLen] ;
  1055.    CHAR           TempBuff [LongTextLen * 2] ;
  1056.    int            StringLen ;
  1057.    PLINESTRUCT    pLine;
  1058.    int            TimeToWriteFile ;
  1059.    FLOAT          eValue ;
  1060.    int            iLogTic ;
  1061.    BOOL           bFirstTime = TRUE ;
  1062.    SYSTEMTIME     LogSystemTime ;
  1063.    LOGPOSITION    LogPosition ;
  1064.  
  1065.    iLogTic = PlaybackLog.StartIndexPos.iPosition ;
  1066.  
  1067.    // we have to export every point from the log file 
  1068.  
  1069.    for ( ; iLogTic <= PlaybackLog.StopIndexPos.iPosition ; iLogTic++)
  1070.       {
  1071.  
  1072.       PlaybackLines (pGraphs->pSystemFirst, 
  1073.                      pGraphs->pLineFirst, 
  1074.                      iLogTic) ;
  1075.  
  1076.       if (!bFirstTime)
  1077.          {
  1078.          // export the values
  1079.          TimeToWriteFile = 0 ;
  1080.  
  1081.          
  1082.          if (!LogPositionN (iLogTic, &LogPosition))
  1083.             {
  1084.             goto Exit0 ;
  1085.             }
  1086.  
  1087.          LogPositionSystemTime (&LogPosition, &LogSystemTime) ;
  1088.  
  1089.          strcpy (TempBuff, LineEndStr) ;
  1090.          StringLen = strlen (TempBuff) ;
  1091.  
  1092.          SystemTimeDateString (&LogSystemTime, UnicodeBuff) ;
  1093.          ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  1094.          strcat (TempBuff, pDelimiter) ;
  1095.          StringLen = strlen (TempBuff) ;
  1096.  
  1097.          SystemTimeTimeString (&LogSystemTime, UnicodeBuff, FALSE) ;
  1098.          ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  1099.          strcat (TempBuff, pDelimiter) ;
  1100.          StringLen = strlen (TempBuff) ;
  1101.  
  1102.          for (pLine=pGraph->pLineFirst; pLine; pLine=pLine->pLineNext)
  1103.             {
  1104.       
  1105.             eValue = CounterEntry (pLine) ;
  1106.  
  1107.             TSPRINTF (UnicodeBuff,
  1108.                       eValue > (FLOAT)999999.0 ?
  1109.                            szLargeValueFormat : szSmallValueFormat,
  1110.                       eValue) ;
  1111.             ConvertDecimalPoint (UnicodeBuff) ;
  1112.             ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  1113.             strcat (TempBuff, pDelimiter) ;
  1114.             StringLen = strlen (TempBuff) ;
  1115.  
  1116.             if (++TimeToWriteFile > TIME_TO_WRITE)
  1117.                {
  1118.                if (!FileWrite (hFile, TempBuff, StringLen))
  1119.                   {
  1120.                   goto Exit0 ;
  1121.                   }
  1122.                StringLen = TimeToWriteFile = 0 ;
  1123.                TempBuff[0] = '\0' ;
  1124.                }
  1125.             }
  1126.  
  1127.          if (StringLen)
  1128.             {
  1129.             if (!FileWrite (hFile, TempBuff, StringLen))
  1130.                {
  1131.                goto Exit0 ;
  1132.                }
  1133.             }
  1134.          }
  1135.       else
  1136.          {
  1137.          // skip the first data point since we
  1138.          // need 2 points to form the first value
  1139.          bFirstTime = FALSE ;
  1140.          }
  1141.       }
  1142.  
  1143.    return (TRUE) ;
  1144.  
  1145. Exit0:
  1146.    return (FALSE) ;
  1147.  
  1148. }  // ExportLogChart
  1149.  
  1150. BOOL ExportLineValue (HANDLE hFile, PGRAPHSTRUCT pGraph,
  1151.    int CurrentIndex, int iDataPoint)
  1152. {
  1153.    TCHAR          UnicodeBuff [MiscTextLen] ;
  1154.    CHAR           TempBuff [LongTextLen] ;
  1155.    int            StringLen ;
  1156.    PLINESTRUCT    pLine;
  1157.    int            MaxValues ;
  1158.    int            TimeToWriteFile ;
  1159.    SYSTEMTIME     *pSystemTime ;
  1160.    BOOL           ValidValue ;
  1161.  
  1162.    pSystemTime = pGraph->pDataTime ;
  1163.    pSystemTime += CurrentIndex ;
  1164.  
  1165.    if (pSystemTime->wYear == 0 && pSystemTime->wYear == 0)
  1166.       {
  1167.       // ignore value that has 0 system time
  1168.       return (TRUE) ;
  1169.       }
  1170.  
  1171.    MaxValues = pGraph->gMaxValues ;
  1172.    strcpy (TempBuff, LineEndStr) ;
  1173.    StringLen = strlen (TempBuff) ;
  1174.  
  1175.    SystemTimeDateString (pSystemTime, UnicodeBuff) ;
  1176.    ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  1177.    strcat (TempBuff, pDelimiter) ;
  1178.    StringLen = strlen (TempBuff) ;
  1179.  
  1180.    SystemTimeTimeString (pSystemTime, UnicodeBuff, FALSE) ;
  1181.    ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  1182.    strcat (TempBuff, pDelimiter) ;
  1183.    StringLen = strlen (TempBuff) ;
  1184.  
  1185.    TimeToWriteFile = 0 ;
  1186.    for (pLine=pGraph->pLineFirst; pLine; pLine=pLine->pLineNext)
  1187.       {
  1188.       if (!pLine->bFirstTime)
  1189.          {
  1190.          ValidValue = FALSE ;
  1191.          // check if this is a valid value
  1192.          if (pLine->lnValidValues == MaxValues)
  1193.             {
  1194.             // this is the simple case where we have filled up 
  1195.             // the whole buffer
  1196.             ValidValue = TRUE ;
  1197.             }
  1198.          else if (pLine->lnValidValues <= iDataPoint)
  1199.             {
  1200.             if (CurrentIndex <= iDataPoint &&
  1201.                CurrentIndex > iDataPoint - pLine->lnValidValues)
  1202.                {
  1203.                ValidValue = TRUE ;
  1204.                }
  1205.             }
  1206.          else
  1207.             {
  1208.             if (CurrentIndex <= iDataPoint ||
  1209.                CurrentIndex > (MaxValues - pLine->lnValidValues + iDataPoint))
  1210.                {
  1211.                // this is the case when we start the new line in the middle
  1212.                // and data buffer has been wrap-around.
  1213.                ValidValue = TRUE ;
  1214.                }
  1215.             }
  1216.  
  1217.          // only export the data when we determine it is valid
  1218.          if (ValidValue)
  1219.             {
  1220.             TSPRINTF (UnicodeBuff,
  1221.                pLine->lnValues[CurrentIndex] > (FLOAT)999999.0 ?
  1222.                szLargeValueFormat : szSmallValueFormat,
  1223.                pLine->lnValues[CurrentIndex]) ;
  1224.             ConvertDecimalPoint (UnicodeBuff) ;
  1225.             ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
  1226.             }
  1227.          }
  1228.       strcat (TempBuff, pDelimiter) ;
  1229.       StringLen = strlen (TempBuff) ;
  1230.  
  1231.       if (++TimeToWriteFile > TIME_TO_WRITE)
  1232.          {
  1233.          // better write the buffers before they overflow.
  1234.          // there are better ways to check for overflow 
  1235.          // but this is good enough
  1236.    
  1237.          if (!FileWrite (hFile, TempBuff, StringLen))
  1238.             {
  1239.             goto Exit0 ;
  1240.             }
  1241.          StringLen = TimeToWriteFile = 0 ;
  1242.          TempBuff[0] = '\0' ;
  1243.          }
  1244.       }
  1245.  
  1246.    if (StringLen)
  1247.       {
  1248.       // write the last block of data
  1249.       if (!FileWrite (hFile, TempBuff, StringLen))
  1250.          {
  1251.          goto Exit0 ;
  1252.          }
  1253.       }
  1254.  
  1255.    return (TRUE) ;
  1256.  
  1257. Exit0:
  1258.    return (FALSE) ;
  1259.  
  1260. }     // ExportLineValue
  1261.  
  1262. BOOL ExportCurrentChart (HANDLE hFile, PGRAPHSTRUCT pGraph)
  1263. {
  1264.    int            KnownValue,
  1265.                   MaxValues,
  1266.                   iValidValues,
  1267.                   iDataPoint ;
  1268.    BOOL           SimpleCase = FALSE ;
  1269.    int            iIndex ;
  1270.  
  1271.    MaxValues = pGraph->gMaxValues ;
  1272.    KnownValue = pGraph->gKnownValue ;
  1273.    iValidValues = pGraph->gTimeLine.iValidValues ;
  1274.  
  1275.    if (iValidValues < MaxValues)
  1276.       {
  1277.       // data have not wrapped around, so the oldest time
  1278.       // is started at 0.
  1279.       SimpleCase = TRUE ;
  1280.       iValidValues = (KnownValue % MaxValues) + 1 ;
  1281.       }
  1282.  
  1283.    iDataPoint = KnownValue % MaxValues ;
  1284.  
  1285.    if (!SimpleCase)
  1286.       {
  1287.       for (iIndex = iDataPoint+1 ; iIndex < MaxValues ; iIndex++)
  1288.          {
  1289.          if (!ExportLineValue (hFile, pGraph, iIndex, iDataPoint))
  1290.             {
  1291.             goto Exit0 ;
  1292.             }
  1293.          }
  1294.       }
  1295.  
  1296.    for (iIndex = 0 ; iIndex <= iDataPoint ; iIndex++)
  1297.       {
  1298.       if (!ExportLineValue (hFile, pGraph, iIndex, iDataPoint))
  1299.          {
  1300.          goto Exit0 ;
  1301.          }
  1302.       }
  1303.  
  1304.    return (TRUE) ;
  1305.  
  1306. Exit0:
  1307.    return (FALSE) ;
  1308.  
  1309. }  // ExportCurrentChart
  1310.  
  1311.  
  1312. void ExportChart (void)
  1313. {
  1314.  
  1315.    PGRAPHSTRUCT   pGraph ;
  1316.    HANDLE         hFile = 0 ;
  1317.    LPTSTR         pFileName = NULL ;
  1318.    INT            ErrCode = 0 ;
  1319.  
  1320.    if (!(pGraph = pGraphs))
  1321.       {
  1322.       return ;
  1323.       }
  1324.  
  1325.    // see if there is anything to export..
  1326.    if (!(pGraph->pLineFirst))
  1327.       {
  1328.       return ;
  1329.       }
  1330.  
  1331.    SetHourglassCursor() ;
  1332.    
  1333.    if (ErrCode = ExportFileOpen (hWndGraph, &hFile, pGraph->gInterval, &pFileName))
  1334.       {
  1335.       goto Exit0 ;
  1336.       }
  1337.  
  1338.    if (!pFileName)
  1339.       {
  1340.       // this is the case when user cancel.
  1341.       goto Exit0 ;
  1342.       }
  1343.  
  1344.    // export the column labels
  1345.    if (!ExportChartLabels (hFile, pGraph))
  1346.       {
  1347.    
  1348.       ErrCode = ERR_EXPORT_FILE ;
  1349.       goto Exit0 ;
  1350.       }
  1351.  
  1352.    // export the lines
  1353.    if (PlayingBackLog())
  1354.       {
  1355.       if (!ExportLogChart (hFile, pGraph))
  1356.          {
  1357.          ErrCode = ERR_EXPORT_FILE ;
  1358.          goto Exit0 ;
  1359.          }
  1360.       }
  1361.    else
  1362.       {
  1363.       if (!ExportCurrentChart (hFile, pGraph))
  1364.          {
  1365.          ErrCode = ERR_EXPORT_FILE ;
  1366.          goto Exit0 ;
  1367.          }
  1368.       }
  1369. Exit0:
  1370.  
  1371.    SetArrowCursor() ;
  1372.  
  1373.    if (hFile)
  1374.       {
  1375.       CloseHandle (hFile) ;
  1376.       }
  1377.  
  1378.    if (pFileName)
  1379.       {
  1380.       if (ErrCode)
  1381.          {
  1382.          DlgErrorBox (hWndGraph, ErrCode, pFileName) ;
  1383.          }
  1384.  
  1385.       MemoryFree (pFileName) ;
  1386.       }
  1387.  
  1388. }     // ExportChart
  1389.  
  1390.  
  1391. typedef struct CHARTDATAPOINTSTRUCT
  1392.    {
  1393.    int         iLogIndex ;
  1394.    int         xDispDataPoint ;
  1395.    } CHARTDATAPOINT, *PCHARTDATAPOINT ;
  1396.  
  1397. void PlaybackChartDataPoint (PCHARTDATAPOINT pChartDataPoint)
  1398.    {  // PlaybackChartDataPoint
  1399.    int            iDisplayTics ;       // num visual points to display
  1400.    int            iDisplayTic ;
  1401.    int            iLogTic ;
  1402.    int            iLogTicsMove ;
  1403.    BOOL           bFirstTime = TRUE;
  1404.    int            iLogTicsRemaining ;
  1405.    int            numOfData, xDispDataPoint, rectWidth, xPos ;
  1406.    PGRAPHSTRUCT   pGraph ;
  1407.  
  1408.    pGraph = GraphData (hWndGraph) ;
  1409.  
  1410.    iLogTicsRemaining = PlaybackLog.iSelectedTics ;
  1411.  
  1412.  
  1413.    // we only have iDisplayTics-1 points since
  1414.    // we have to use the first two sample points to
  1415.    // get the first data points.
  1416.    if (iLogTicsRemaining <= pGraphs->gMaxValues)
  1417.       {
  1418.       iDisplayTics = iLogTicsRemaining ;
  1419.       }
  1420.    else
  1421.       {
  1422.       iDisplayTics = pGraphs->gMaxValues ;
  1423.       }
  1424.  
  1425.    iDisplayTic = -1 ;
  1426.    iLogTic = PlaybackLog.StartIndexPos.iPosition ;
  1427.  
  1428.    numOfData      = pGraph->gMaxValues - 1 ;
  1429.    rectWidth      = pGraph->rectData.right - pGraph->rectData.left ;
  1430.    xDispDataPoint = pGraph->rectData.left ;
  1431.  
  1432.    while (iDisplayTics && numOfData)
  1433.       {
  1434.  
  1435.       if (!bFirstTime)
  1436.          {
  1437.          iDisplayTic++ ;
  1438.          }
  1439.       else
  1440.          {
  1441.          bFirstTime = FALSE ;
  1442.  
  1443.          // get the second sample data to form the first data point
  1444.          iLogTic++ ;
  1445.          iLogTicsRemaining-- ;
  1446.  
  1447.          iDisplayTic++ ;
  1448.          }
  1449.  
  1450.       pChartDataPoint[iDisplayTic].iLogIndex = iLogTic ;
  1451.       pChartDataPoint[iDisplayTic].xDispDataPoint = xDispDataPoint ;
  1452.  
  1453.       // setup DDA to get the index of the next sample point
  1454.       iLogTicsMove = DDA_DISTRIBUTE (iLogTicsRemaining, iDisplayTics) ;
  1455.       iLogTicsRemaining -= iLogTicsMove ;
  1456.       iLogTic += iLogTicsMove ;
  1457.  
  1458.  
  1459.       xPos = DDA_DISTRIBUTE (rectWidth, numOfData) ;
  1460.       xDispDataPoint += xPos ;
  1461.       numOfData-- ;
  1462.       rectWidth -= xPos ;
  1463.  
  1464.       iDisplayTics-- ;
  1465.  
  1466.       }  // while
  1467.  
  1468.    }  // PlaybackChartDataPoint
  1469.