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 / grafdisp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-13  |  39.1 KB  |  1,389 lines

  1. //==========================================================================//
  2. //                                  Includes                                //
  3. //==========================================================================//
  4.  
  5.  
  6. #include <stdio.h>
  7. #include "perfmon.h"
  8. #include "grafdisp.h"      // external declarations for this file
  9.  
  10. #include "grafdata.h"      // for InsertGraph, et al.
  11. #include "graph.h"
  12. #include "legend.h"
  13. #include "line.h"          // for LineCreatePen
  14. #include "perfmops.h"      // for DoWindowDrag
  15. #include "playback.h"      // for PlayingBackLog
  16. #include "valuebar.h"
  17. #include "utils.h"
  18. #include "timeline.h"      // for IsTLineWindowUp & TLineRedraw 
  19. #include "counters.h"      // for CounterEntry
  20.  
  21. //==========================================================================//
  22. //                                  Constants                               //
  23. //==========================================================================//
  24.  
  25. // this macro is used in doing a simple DDA (Digital Differential Analyzer)
  26. // * 10 + 5 is to make the result round up with .5
  27. #define DDA_DISTRIBUTE(TotalTics, numOfData) \
  28.    ((TotalTics * 10 / numOfData) + 5) / 10
  29.  
  30. HDC   hGraphDisplayDC ;
  31. //=============================//
  32. // GraphDisplay Class          //
  33. //=============================//
  34.  
  35.  
  36. TCHAR   szGraphDisplayWindowClass[] = TEXT("PerfChart") ;
  37. #define dwGraphDisplayClassStyle    (CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC)
  38. #define iGraphDisplayClassExtra     (0)
  39. #define iGraphDisplayWindowExtra    (0)
  40. #define dwGraphDisplayWindowStyle   (WS_CHILD | WS_VISIBLE)
  41.  
  42.  
  43.  
  44. //==========================================================================//
  45. //                              Local Functions                             //
  46. //==========================================================================//
  47. BOOL UpdateTimeLine (HDC hDC, PGRAPHSTRUCT pGraph, BOOL getLastTimeLocation) ;
  48.  
  49. #if 0
  50. PGRAPHSTRUCT GraphData (HWND hWndGraphDisplay)
  51. /*
  52.    Effect:        Return the graph associated with graph display window
  53.                   hWndGraphData. At the present time, we only have one
  54.                   graph data window and one graph structure. In the
  55.                   future, we may have several of each. The graph structure
  56.                   is conceptually instance data of the graph display
  57.                   window. Use of this function allows for easier additions
  58.                   to the code.
  59. */
  60.    {
  61.    return (pGraphs) ;
  62.    }
  63. #endif
  64.  
  65. INT ScaleAndInvertY (FLOAT ey, 
  66.                      PLINESTRUCT pLineStruct,
  67.                      PGRAPHSTRUCT pGraph)
  68. /*
  69.    Effect:        Given data value ey, scale and fit the value to fit
  70.                   within the graph data area of the window, considering
  71.                   the scale set for the line and the current size of the
  72.                   data rectangle.
  73. */   
  74.    {  // ScaleAndInvertY
  75.    INT     yGraphDataHeight,               // Height of graph area
  76.            yInverted ;                     // Scaled & Inverted Y.
  77.    FLOAT   eppd,
  78.            eyScaled ;
  79.  
  80.  
  81.    // Take care of any scaling now, at output time.
  82.    ey *= pLineStruct->eScale ;
  83.  
  84.    // Calculate the Cy of the graph area.
  85.    yGraphDataHeight = pGraph->rectData.bottom - pGraph->rectData.top ;
  86.  
  87.    // Calculate the pixels per data point.
  88.    eppd = (FLOAT) ((FLOAT) yGraphDataHeight / (FLOAT) pGraph->gOptions.iVertMax) ;
  89.    eyScaled = eppd * ey ;
  90.    yInverted = (INT) (((FLOAT) yGraphDataHeight) - eyScaled) ;
  91.  
  92.    yInverted += pGraph->rectData.top ;
  93.    
  94.    // Clamp the range to fit with in the graph portion of the windows
  95.    yInverted = PinInclusive (yInverted, 
  96.                              pGraph->rectData.top, pGraph->rectData.bottom) ;
  97.    return (yInverted) ;
  98.    }  // ScaleAndInvertY
  99.  
  100.  
  101.  
  102. BOOL DrawGrid (HDC hDC, 
  103.                PGRAPHSTRUCT pGraph,
  104.                LPRECT lpRect,
  105.                BOOL bForPaint)
  106. /*
  107.    Effect:        Draw the grid lines in the graph display window.
  108.                   These grid lines are in the graph data area only,
  109.                   which is indicated by pGraph->rectData.
  110.  
  111.    Called By:     OnPaint only.
  112. */
  113.    {  // DrawGrid
  114.    int            iGrid, iLines ;
  115.    int            xGrid, yGrid ;
  116.    POINT          aPoints [4 * iGraphMaxTics] ;
  117.    DWORD          aCounts [2 * iGraphMaxTics] ;
  118.    HPEN           hPenPrevious ;
  119.    int            bottomAdjust ;
  120.  
  121.    if (!pGraph->gOptions.bHorzGridChecked && 
  122.        !pGraph->gOptions.bVertGridChecked)   
  123.       return (FALSE) ;
  124.  
  125.  
  126.    hPenPrevious = SelectPen (hDC, IsPrinterDC (hDC) ?
  127.       GetStockObject (BLACK_PEN) : pGraph->hGridPen) ;
  128.  
  129.    iLines = 0 ;
  130.  
  131.    if (pGraph->gOptions.bHorzGridChecked)
  132.       {
  133.       for (iGrid = 1 ;
  134.            iGrid < pGraph->yNumTics ;
  135.            iGrid++)
  136.          {  // for
  137.          yGrid = pGraph->ayTics[iGrid] + pGraph->rectData.top ;
  138.          if (yGrid >= lpRect->top &&
  139.              yGrid <= lpRect->bottom)
  140.             {  // if
  141.             aPoints[2 * iLines].x = lpRect->left ;
  142.             aPoints[2 * iLines].y = yGrid ;
  143.             aPoints[2 * iLines + 1].x = lpRect->right ;
  144.             aPoints[2 * iLines + 1].y = yGrid ;
  145.  
  146.             aCounts[iLines] = 2 ;
  147.             iLines++ ;
  148.             }  // if
  149.          }  // for
  150.       }  // if
  151.  
  152.    if (pGraph->gOptions.bVertGridChecked)
  153.       {
  154.       bottomAdjust = lpRect->bottom + (bForPaint ? 1 : 0) ;
  155.       for (iGrid = 1 ;
  156.            iGrid < pGraph->xNumTics ;
  157.            iGrid++)
  158.          {  // for
  159.          xGrid = pGraph->axTics[iGrid] + pGraph->rectData.left ;
  160.          if (xGrid >= lpRect->left &&
  161.              xGrid <= lpRect->right)
  162.             {  // if
  163.             aPoints[2 * iLines].x = xGrid ;
  164.             aPoints[2 * iLines].y = lpRect->top ;
  165.             aPoints[2 * iLines + 1].x = xGrid ;
  166.             aPoints[2 * iLines + 1].y = bottomAdjust ;
  167.  
  168.             aCounts[iLines] = 2 ;
  169.             iLines++ ;
  170.             }  // if
  171.          }  // for
  172.       }  // if
  173.  
  174.    if (iLines)
  175.       PolyPolyline (hDC, aPoints, aCounts, iLines) ;
  176.  
  177.    SelectPen (hDC, hPenPrevious) ;
  178.  
  179.    return (TRUE) ;
  180.    }  // DrawGrid
  181.  
  182.  
  183.  
  184. BOOL DrawBarChartData (HDC hDC, 
  185.                        PGRAPHSTRUCT pGraph)
  186.    {  // DrawBarChartData
  187.    PLINESTRUCT pLineStruct ;
  188.    PFLOAT      pDataPoints ;
  189.    INT         nLegendItems,
  190.                cx,
  191.                cxBar,
  192.                xDataPoint,
  193.                y ;
  194.    RECT        rectBar ;
  195.    RECT        rectBkgrnd ;
  196.    HBRUSH      hOldBrush ;
  197.    FLOAT       eValue ;
  198.    PLINESTRUCT pCurrentLine ;
  199.  
  200.    // Determine how many items are in the legend.
  201.  
  202.    nLegendItems = 0 ;
  203.  
  204.    for (pLineStruct = pGraph->pLineFirst ;
  205.         pLineStruct ;
  206.         pLineStruct = pLineStruct->pLineNext)
  207.       {  // for
  208.       nLegendItems++ ;
  209.       }  // for
  210.  
  211.    if (nLegendItems == 0)
  212.       return(FALSE) ;
  213.  
  214.    // get current select line for highlighting
  215.    if (pGraph->HighLightOnOff)
  216.       {
  217.       pCurrentLine = CurrentGraphLine (hWndGraph) ;
  218.       }
  219.    else
  220.       {
  221.       pCurrentLine = NULL ;
  222.       }
  223.  
  224.    // Determine the width of each bar.
  225.    cx = pGraph->rectData.right - pGraph->rectData.left ;
  226.  
  227.  
  228.    if (PlayingBackLog())
  229.       {
  230.       // get the average using the start and stop data point 
  231.       // from the log file
  232.       PlaybackLines (pGraph->pSystemFirst, 
  233.                      pGraph->pLineFirst, 
  234.                      PlaybackLog.StartIndexPos.iPosition) ;
  235.       PlaybackLines (pGraph->pSystemFirst, 
  236.                      pGraph->pLineFirst, 
  237.                      PlaybackLog.StopIndexPos.iPosition) ;
  238.       }
  239.    else
  240.       {
  241.       // Loop through all the DataLines and draw a bar for
  242.       // it's last value.
  243.       xDataPoint = pGraph->gKnownValue % pGraph->gMaxValues ;
  244.       }
  245.  
  246.    rectBar.bottom = pGraph->rectData.bottom + 1 ;
  247.  
  248.    rectBkgrnd = pGraph->rectData ;
  249.  
  250.    hOldBrush = SelectBrush (hDC, hBrushFace) ;
  251.  
  252.    PatBlt (hDC, 
  253.            rectBkgrnd.left, rectBkgrnd.top,
  254.            rectBkgrnd.right - rectBkgrnd.left,
  255.            rectBkgrnd.bottom - rectBkgrnd.top + 1,
  256.            PATCOPY) ;
  257.    DrawGrid(hDC, pGraph, &(rectBkgrnd), FALSE) ;
  258.  
  259.    rectBar.right = pGraph->rectData.left ;
  260.    for (pLineStruct = pGraph->pLineFirst ;
  261.         pLineStruct ;
  262.         pLineStruct = pLineStruct->pLineNext)
  263.       {  // for
  264.       pDataPoints = pLineStruct->lnValues ;
  265.  
  266.       if (PlayingBackLog())
  267.          {
  268.          eValue = CounterEntry (pLineStruct) ;
  269.          }
  270.       else
  271.          {
  272.          eValue = pDataPoints[xDataPoint] ;
  273.          }
  274.  
  275.  
  276.       y = ScaleAndInvertY (eValue,
  277.                            pLineStruct,
  278.                            pGraph) ;
  279.  
  280.       rectBar.left   = rectBar.right ;
  281.       rectBar.top    = y ;
  282.  
  283.       // nomore line to draw
  284.       if (nLegendItems == 0 )
  285.          {
  286.          break ;
  287.          }
  288.  
  289.       cxBar = DDA_DISTRIBUTE (cx, nLegendItems) ;
  290.       rectBar.right  = rectBar.left + cxBar ;
  291.  
  292.       // setup for next DDA
  293.       nLegendItems-- ;
  294.       cx -= cxBar ;
  295.  
  296.       // NOTE: this handle creation should be moved to line
  297.       //       create time.
  298.  
  299.       if (pCurrentLine == pLineStruct)
  300.          {
  301.          SetBkColor (hDC, crWhite) ;
  302.          }
  303.       else
  304.          {
  305.          SetBkColor (hDC, pLineStruct->Visual.crColor) ;
  306.          }
  307.        ExtTextOut (hDC, rectBar.right, rectBar.top, ETO_OPAQUE,
  308.          &rectBar, NULL, 0, NULL) ;
  309.       }  // for
  310.  
  311.    return (TRUE) ;
  312.    }
  313.  
  314.  
  315. /***************************************************************************
  316.  * DrawTLGraphData - Draw Time Line Graph Data.
  317.  *
  318.  *  Some notes about drawing the DataPoint graphs.
  319.  *
  320.  *      1]  It's real expensive to make a GDI call. So, we do not
  321.  *          make a MoveToEx and LineTo call for each point.  Instead
  322.  *          we create a polyline and send it down to GDI.
  323.  *
  324.  *      2]  The X coordinates for each point in the polyline is generated
  325.  *          from our favorite xDataPoint to xWindows DDA.
  326.  *
  327.  *      3]  The Y coordinate is generated from the pLineStruct->lnValues[x]
  328.  *          data associated with each line.
  329.  ***************************************************************************/
  330. BOOL DrawTLGraphData (HDC hDC, 
  331.                       BOOL bForPaint,
  332.                       PRECT prctPaint, 
  333.                       PGRAPHSTRUCT pGraph)
  334. /*
  335.    Called By:     UpdateGraphDisplay only. 
  336. */
  337.    {
  338.    PLINESTRUCT pLineStruct ;
  339.    HPEN        hPen = 0 ;
  340.    HPEN        hOldPen ;
  341.    PFLOAT      pDataPoints ;
  342.    INT         i, j,
  343.                iValidValues,
  344.                xDispDataPoint,
  345.                xLeftLimit,
  346.                xRightLimit ;
  347.    PPOINT      pptDataPoints ;
  348.    INT         numOfData, rectWidth, xPos ;
  349.    PLINESTRUCT pCurrentLine ;
  350.    INT         DrawZeroPoint = 0 ;
  351.  
  352. //   SetBkColor (hDC, crLightGray) ;
  353.  
  354.    if (!IsPrinterDC (hDC))
  355.       {
  356.       if (bForPaint)
  357.          {
  358.          IntersectClipRect (hDC, 
  359.                             pGraph->rectData.left,
  360.                             pGraph->rectData.top,
  361.                             pGraph->rectData.right,
  362.                             pGraph->rectData.bottom + 1) ;
  363.          }
  364.       else
  365.          {
  366.          IntersectClipRect (hDC, 
  367.                             pGraph->rectData.left,
  368.                             pGraph->rectData.top,
  369.                             PlayingBackLog () ? 
  370.                               pGraph->rectData.right :
  371.                               min (pGraph->rectData.right,
  372.                                    pGraph->gTimeLine.xLastTime + 2),
  373.                             pGraph->rectData.bottom + 1) ;
  374.          }
  375.       }
  376.  
  377.    xLeftLimit  = prctPaint->left  - pGraph->gTimeLine.ppd - 1 ;
  378.    
  379.    if (bForPaint)
  380.       xRightLimit = prctPaint->right + pGraph->gTimeLine.ppd ;
  381.    else
  382.       xRightLimit = prctPaint->right ;
  383.    
  384.    pptDataPoints  = pGraph->pptDataPoints ;
  385.  
  386.    iValidValues   = pGraph->gTimeLine.iValidValues ;
  387.  
  388.    if (!PlayingBackLog() &&
  389.       pGraph->gOptions.iGraphOrHistogram == LINE_GRAPH)
  390.       {
  391.       // drawing the 0th at the end of the chart.
  392.       DrawZeroPoint = 1 ;
  393.       if (iValidValues == pGraph->gMaxValues)
  394.          {
  395.          iValidValues++ ;
  396.          }
  397.       }
  398.  
  399.    // get current select line for highlighting
  400.    if (pGraph->HighLightOnOff)
  401.       {
  402.       pCurrentLine = CurrentGraphLine (hWndGraph) ;
  403.       }
  404.    else
  405.       {
  406.       pCurrentLine = NULL ;
  407.       }
  408.  
  409.    // loop through lines to plot
  410.    for (pLineStruct = pGraph->pLineFirst ;
  411.         pLineStruct || pCurrentLine;
  412.         pLineStruct = pLineStruct->pLineNext)
  413.       {  // for
  414.  
  415.       if (pLineStruct == NULL)
  416.          {
  417.          // now draw the current line
  418.          pLineStruct = pCurrentLine ;
  419.          }
  420.       else if (pLineStruct == pCurrentLine)
  421.          {
  422.          // skip this line and draw it later
  423.          continue ;
  424.          }
  425.  
  426.       // "Localize" some variables from the line data structure.
  427.       pDataPoints    = pLineStruct->lnValues ;
  428.  
  429.  
  430.       rectWidth      = pGraph->rectData.right - pGraph->rectData.left ;
  431.       numOfData      = pGraph->gMaxValues - 1 + DrawZeroPoint ;
  432.  
  433.       // Generate the polyline data.
  434.       xDispDataPoint = pGraph->rectData.left ;
  435.  
  436.       // Only process points that lie within the update region.
  437.       // Also only process points that have valid data.
  438.       j = 0 ;
  439.  
  440.       for (i = 0 ; i < iValidValues ; i++)
  441.          {  // for
  442.          if (xDispDataPoint > xRightLimit)
  443.             {
  444.             // we are done!
  445.             break ;
  446.             }
  447.          if (xDispDataPoint >= xLeftLimit)
  448.             {
  449.             // It is within the limits, plot the point
  450.             pptDataPoints[j].x = xDispDataPoint ;
  451.             pptDataPoints[j].y = ScaleAndInvertY (
  452.                (i == pGraph->gMaxValues) ? pDataPoints[0] : pDataPoints[i],
  453.                pLineStruct,
  454.                pGraph) ;
  455.             j++ ;
  456.             }  // if
  457.  
  458.          // setup for the next point
  459.          if (!numOfData)
  460.             {
  461.             // no more points to go
  462.             break ;
  463.             }
  464.  
  465.          xPos = DDA_DISTRIBUTE (rectWidth, numOfData) ;
  466.          xDispDataPoint += xPos ;
  467.          numOfData-- ;
  468.          rectWidth -= xPos ;
  469.          }  // for i
  470.  
  471.       // only need to draw the line if there is point to draw.
  472.       if (j > 0)
  473.          {
  474.          // Set the pen color and draw the polyline.
  475.          if (IsPrinterDC (hDC))
  476.             {
  477.             hPen = LineCreatePen (hDC, &(pLineStruct->Visual), TRUE) ;
  478.             hOldPen = SelectObject (hDC, hPen) ;
  479.             }
  480.          else
  481.             {
  482.             if (pCurrentLine == pLineStruct)
  483.                {
  484.                // highlight this line by turning it into White color
  485.                hOldPen = SelectObject (hDC, hWhitePen) ;
  486.                }
  487.             else
  488.                {
  489.                SelectObject (hDC, pLineStruct->hPen) ;
  490.                }
  491.             }
  492.  
  493.          Polyline(hDC, pptDataPoints, j) ;
  494.  
  495.          if (hPen)
  496.             {
  497.             SelectObject (hDC, hOldPen) ;
  498.  
  499.             if (hPen != hWhitePen)
  500.                {
  501.                DeletePen (hPen) ;
  502.                }
  503.             hPen = 0 ;
  504.             }
  505.          }
  506.  
  507.       if (pCurrentLine == pLineStruct)
  508.          {
  509.          // We are done...
  510.          break ;
  511.          }
  512.       }  // for pLine
  513.  
  514.    if (IsTLineWindowUp())
  515.       {
  516.       // re-draw the timelines if need
  517.       TLineRedraw (hDC, pGraph) ;
  518.       }
  519.  
  520.    // reset the clipping region
  521.    SelectClipRgn (hDC, pGraph->hGraphRgn) ;
  522.  
  523.    return (TRUE) ;
  524.    }  // DrawTLGraphData
  525.  
  526.  
  527.  
  528. /***************************************************************************
  529.  * bInitTimeLine - Initialize the fields of the time line structure.
  530.  ***************************************************************************/
  531. BOOL bInitTimeLine(PGRAPHSTRUCT pGraph)
  532. {
  533.  
  534.         pGraph->gTimeLine.xLastTime      = 0 ;
  535.         pGraph->gTimeLine.ppd            = 0 ;
  536.         pGraph->gTimeLine.rppd           = 0 ;
  537.         pGraph->gTimeLine.iValidValues   = 1 ;
  538.  
  539.         return (TRUE) ;
  540.  
  541. }
  542.  
  543.  
  544. /***************************************************************************
  545.  * Scale Time Line
  546.  *
  547.  *  This routine should be called from the WM_SIZE message.
  548.  *  It does the scaling from the number of data points to the
  549.  *  size of the window.
  550.  ***************************************************************************/
  551. void ScaleTimeLine (PGRAPHSTRUCT pGraph)
  552.    {
  553.    INT     nDataPoints,
  554.            cxClient ;
  555.  
  556.    // Calculate the pels per data point.
  557.    nDataPoints = pGraph->gMaxValues - 1 ;
  558.    cxClient    = pGraph->rectData.right - pGraph->rectData.left ;
  559.  
  560.    // ppd  = Pixels per DataPoint.
  561.    // rppd = Remaining Pixels per DataPoint.
  562.    pGraph->gTimeLine.ppd  = cxClient / nDataPoints ;
  563.    pGraph->gTimeLine.rppd = cxClient % nDataPoints ;
  564.    }
  565.  
  566.  
  567. void DisplayTimeLine(HDC hDC, PGRAPHSTRUCT pGraph)
  568. /*
  569.    Called By:     OnPaint only.
  570.  
  571.    Assert:        xDisplayPoint has been set by UpdateTimeLine on this
  572.                   same timer tick.
  573. */
  574.    {  // DisplayTimeLine
  575.    INT     xDisplayPoint ;
  576.    RECT    rect ;
  577.  
  578.    if (pGraph->gTimeLine.xLastTime == -1)
  579.       {
  580.       UpdateTimeLine (hGraphDisplayDC, pGraph, TRUE) ;
  581.       }
  582.  
  583.    // xDisplayPoint is X coordinate to display the time line at.
  584.    if ((xDisplayPoint = pGraph->gTimeLine.xLastTime) == 0)
  585.       return ;
  586.  
  587.    SelectBrush (hDC, pGraph->hbRed) ;
  588.  
  589.    if (xDisplayPoint >= pGraph->rectData.right)
  590.       {
  591.       rect.left   = pGraph->rectData.left ;
  592.       }
  593.    else
  594.       {
  595. //      rect.left   = xDisplayPoint++ ;
  596.       rect.left   = xDisplayPoint ;
  597.       }
  598.    rect.top    = pGraph->rectData.top ;
  599.    rect.right  = rect.left + 2 ;
  600.    rect.bottom = pGraph->rectData.bottom ;
  601.  
  602. //   IntersectRect (&rect, &rect, &pGraph->rectData) ;
  603.    if (rect.right > pGraph->rectData.right)
  604.       {
  605.       rect.right = pGraph->rectData.right ;
  606.       }
  607.    PatBlt (hDC, 
  608.            rect.left, rect.top,
  609.            rect.right - rect.left,
  610.            rect.bottom - rect.top + 1 ,
  611.            PATCOPY) ;
  612.          
  613.    }  // DisplayTimeLine
  614.  
  615.  
  616.  
  617. int SuggestedNumTics (int iRange)
  618. /*
  619.    Effect:        Return an appropriate number of tic marks to display
  620.                   within iRange pixels.
  621.  
  622.                   These numbers are empirically chosen for pleasing 
  623.                   results.
  624. */
  625.    {  // SuggestedNumTics
  626.    if (iRange < 20)
  627.       return (0) ;
  628.  
  629.    if (iRange < 50)
  630.       return (2) ;
  631.  
  632.    if (iRange < 100)
  633.       return (4) ;
  634.  
  635.    if (iRange < 150)
  636.       return (5) ;
  637.  
  638.    if (iRange < 300)
  639.       return (10) ;
  640.  
  641.    if (iRange < 500)
  642.       return (20) ;
  643.  
  644.    return (25) ;
  645.    }  // SuggestedNumTics
  646.  
  647.  
  648.  
  649. void SetGridPositions (PGRAPHSTRUCT pGraph)
  650.    {  // SetGridPositions
  651.    int            xDataWidth ;
  652.    int            yDataHeight ;
  653.  
  654.    int            iCurrentTicPixels ;
  655.    int            iNumTics ;
  656.  
  657.    int            i ;
  658.  
  659.  
  660.    //=============================//
  661.    // Set number of Tics          //
  662.    //=============================//
  663.  
  664.    xDataWidth = pGraph->rectData.right - pGraph->rectData.left ;
  665.    yDataHeight = pGraph->rectData.bottom - pGraph->rectData.top ;
  666.  
  667.    pGraph->xNumTics = PinInclusive (SuggestedNumTics (xDataWidth),
  668.                                     0, iGraphMaxTics) ;
  669.    pGraph->yNumTics = PinInclusive (SuggestedNumTics (yDataHeight),
  670.                                     0, iGraphMaxTics) ;
  671.  
  672.    // if we have more tics than possible integral values, reduce the number
  673.    // of tics.
  674.    if (pGraph->gOptions.iVertMax < pGraph->yNumTics)
  675.       pGraph->yNumTics = pGraph->gOptions.iVertMax ;
  676.  
  677.  
  678.    //=============================//
  679.    // Set X Tic Positions         //
  680.    //=============================//
  681.  
  682.    if (pGraph->xNumTics)
  683.       {
  684.       iNumTics = pGraph->xNumTics ;
  685.  
  686.       pGraph->axTics[0] = 0 ;
  687.       for (i = 1 ;
  688.            i < pGraph->xNumTics ;
  689.            i++)
  690.          {  // for
  691.          if (iNumTics == 0)
  692.             {
  693.             break ;
  694.             }
  695.          iCurrentTicPixels = DDA_DISTRIBUTE (xDataWidth, iNumTics) ;
  696.          pGraph->axTics [i] = pGraph->axTics [i - 1] + iCurrentTicPixels ;
  697.  
  698.          xDataWidth -= iCurrentTicPixels ;
  699.          iNumTics-- ;
  700.          }  // for
  701.       }  // if
  702.  
  703.  
  704.    //=============================//
  705.    // Set Y Tic Positions         //
  706.    //=============================//
  707.  
  708.    if (pGraph->yNumTics)
  709.       {
  710.       iNumTics = pGraph->yNumTics ;
  711.  
  712.       pGraph->ayTics[0] = 0 ;
  713.       for (i = 1 ;
  714.            i < pGraph->yNumTics ;
  715.            i++)
  716.          {  // for
  717.          if (iNumTics == 0)
  718.             {
  719.             break ;
  720.             }
  721.          iCurrentTicPixels = DDA_DISTRIBUTE (yDataHeight, iNumTics) ;
  722.          pGraph->ayTics [i] = pGraph->ayTics [i- 1] + iCurrentTicPixels ;
  723.  
  724.          yDataHeight -= iCurrentTicPixels ;
  725.          iNumTics-- ;
  726.          }  // for
  727.       }  // if
  728.    }  // SetGridPositions
  729.  
  730.  
  731.  
  732. int GraphVerticalScaleWidth (HDC hDC,
  733.                              PGRAPHSTRUCT pGraph)
  734.    {
  735.    TCHAR          szMaxValue [20] ;
  736.    int            xWidth ;
  737.  
  738.    if (!pGraph->gOptions.bLabelsChecked)
  739.       return (0) ;
  740.  
  741.  
  742. //   SelectFont (hDC, IsPrinterDC (hDC) ? hFontPrinterScales : hFontScales) ;
  743.  
  744.  
  745.    TSPRINTF (szMaxValue, TEXT(" %1d "),
  746.             pGraph->gOptions.iVertMax * 10 ) ;
  747.  
  748.    xWidth = TextWidth (hDC, szMaxValue) ;
  749.  
  750.    return (xWidth) ;
  751.    }
  752.  
  753.  
  754.  
  755. void DrawGraphScale (HDC hDC, 
  756.                      PGRAPHSTRUCT pGraph)
  757.    {
  758.    TCHAR   szScale [20] ;
  759.  
  760.    INT     len,
  761.            i,
  762.            nLines,
  763.            iUnitsPerLine ;
  764.    FLOAT   ePercentOfTotal  ;
  765.    FLOAT   eDiff ;
  766.    BOOL    bUseFloatingPt = FALSE ;
  767.  
  768.    //=============================//
  769.    // Draw Vertical Scale?        //
  770.    //=============================//
  771.  
  772.    if (!pGraph->gOptions.bLabelsChecked)
  773.        return ;
  774.  
  775.    // Get the number of lines.
  776.    nLines = pGraph->yNumTics ;
  777.  
  778.    // nLines may be zero if the screen size if getting too small
  779.    if (nLines > 0)
  780.       {
  781.       // Calculate what percentage of the total each line represents.
  782.       ePercentOfTotal = ((FLOAT) 1.0) / ((FLOAT) nLines)  ;
  783.  
  784.       // Calculate the amount (number of units) of the Vertical max each
  785.       // each line in the graph represents.
  786.       iUnitsPerLine = (INT) ((FLOAT) pGraph->gOptions.iVertMax * ePercentOfTotal) ;
  787.       ePercentOfTotal *= (FLOAT) pGraph->gOptions.iVertMax ;
  788.       eDiff = (FLOAT)iUnitsPerLine - ePercentOfTotal ;
  789.       if (eDiff < (FLOAT) 0.0)
  790.          eDiff = -eDiff ;
  791.  
  792.       if (eDiff > (FLOAT) 0.1)
  793.          bUseFloatingPt = TRUE ;
  794.       }
  795.  
  796.    //=============================//
  797.    // Set Drawing Attributes      //
  798.    //=============================//
  799.  
  800. //   SelectFont (hDC, IsPrinterDC (hDC) ? hFontPrinterScales : hFontScales) ;
  801.    SetBkMode(hDC, TRANSPARENT) ;
  802.    SetTextAlign (hDC, TA_TOP | TA_RIGHT) ;
  803.    SelectObject(hDC, GetStockObject (BLACK_PEN)) ;
  804.  
  805.    // Set the background color to gray
  806.    if (!IsPrinterDC (hDC))
  807.       FillRect (hDC, &(pGraph->rectVertScale), hbLightGray) ;
  808.  
  809. // TESTING TESTING
  810. #ifdef   PERFMON_DEBUG
  811. GdiSetBatchLimit(1) ;   // disable Batching
  812. #endif
  813.  
  814.    // Now Output each string.
  815.    for (i = 0 ; 
  816.         i < nLines ; 
  817.         i++)
  818.       {  // for
  819.       if (bUseFloatingPt)
  820.          {
  821.          len = TSPRINTF (szScale, TEXT("%1.1f"),
  822.             (FLOAT)pGraph->gOptions.iVertMax - ((FLOAT)i *
  823.              ePercentOfTotal)) ;
  824.          ConvertDecimalPoint (szScale) ;
  825.          }
  826.       else
  827.          {
  828.          len = TSPRINTF (szScale, TEXT("%d"),
  829.                          pGraph->gOptions.iVertMax - (i * iUnitsPerLine)) ;
  830.          }
  831.       TextOut (hDC,
  832.                pGraph->rectVertScale.right,
  833.                pGraph->ayTics[i] +
  834.                pGraph->rectData.top - HalfTextHeight,
  835.                szScale,
  836.                len) ;
  837.       }  // for
  838.  
  839.    // Output the "min value" separately.
  840.    TextOut (hDC, 
  841.             pGraph->rectVertScale.right,
  842.             pGraph->rectData.bottom - HalfTextHeight,
  843.             TEXT("0"),
  844.             1) ;
  845.  
  846. #ifdef   PERFMON_DEBUG
  847. // TESTING TESTING
  848. GdiSetBatchLimit(0) ;   // enable default Batching
  849. #endif
  850.  
  851.  
  852.    }  // DrawGraphScale
  853.  
  854.  
  855.  
  856. void SizeGraphDisplayComponentsRect (HDC hDC,
  857.                                      PGRAPHSTRUCT pGraph,
  858.                                      RECT rectDisplay)
  859.    {  // SizeGraphDisplayComponentsRect
  860.    int            xScaleWidth ;
  861.  
  862.    if (!rectDisplay.right || !rectDisplay.bottom)
  863.       return ;
  864.  
  865.    //=============================//
  866.    // Size the Vertical Scale     //
  867.    //=============================//
  868.  
  869.    xScaleWidth = GraphVerticalScaleWidth (hDC, pGraph) ;
  870.    pGraph->rectVertScale.left = rectDisplay.left ;
  871.    pGraph->rectVertScale.top = rectDisplay.top ;
  872.    pGraph->rectVertScale.right = rectDisplay.left + xScaleWidth ;
  873.    pGraph->rectVertScale.bottom = rectDisplay.bottom ;
  874.  
  875.    //=============================//
  876.    // Size the Horizontal Scale   //
  877.    //=============================//
  878.  
  879.    pGraph->rectHorzScale.left = 0 ;
  880.    pGraph->rectHorzScale.top = 0 ;
  881.    pGraph->rectHorzScale.right = 0 ;
  882.    pGraph->rectHorzScale.bottom = 0 ;
  883.  
  884.    //=============================//
  885.    // Size the Data Area          //
  886.    //=============================//
  887.  
  888.    pGraph->rectData.left = pGraph->rectVertScale.right + 3 + ThreeDPad ;
  889.    pGraph->rectData.right = rectDisplay.right - 5 - ThreeDPad ;
  890.    pGraph->rectData.top = rectDisplay.top + 5 + ThreeDPad ;
  891.    pGraph->rectData.bottom = rectDisplay.bottom - 5 - ThreeDPad ;
  892.  
  893.    SetGridPositions (pGraph) ;
  894.    ScaleTimeLine (pGraph) ;
  895.  
  896.    //==========================================//
  897.    // Invalidate the last time line poisition  //
  898.    //==========================================//
  899.    pGraph->gTimeLine.xLastTime = -1 ;
  900.  
  901.    if (pGraph->hGraphRgn)
  902.       {
  903.       DeleteObject (pGraph->hGraphRgn) ;
  904.       }
  905.  
  906.    pGraph->hGraphRgn = CreateRectRgn (rectDisplay.left,
  907.       rectDisplay.top,
  908.       rectDisplay.right,
  909.       rectDisplay.bottom) ;
  910.  
  911.    SelectClipRgn (hDC, pGraph->hGraphRgn) ;
  912.  
  913.    }  // SizeGraphDisplayComponentsRect
  914.  
  915.  
  916. void SizeGraphDisplayComponents (HWND hWnd)
  917. /*
  918.    Effect:        Given the graph display window hWnd, of size 
  919.                   (xWidth x yHeight), determine the size and position
  920.                   of the various graph display components: the vertical
  921.                   scale, the horizontal scale, and the data area.
  922.  
  923.    Called By:     OnSize, any other routine that changes the visibility
  924.                   of a vertical or horizontal scale.
  925.  
  926.    Note:          This function has multiple return points.
  927. */
  928.    {  // SizeGraphDisplayComponents
  929.    PGRAPHSTRUCT   pGraph ;
  930.    RECT           rectClient ;
  931.  
  932.    pGraph = GraphData (hWnd) ;
  933.    GetClientRect (hWnd, &rectClient) ;
  934.  
  935.    SizeGraphDisplayComponentsRect (hGraphDisplayDC, pGraph, rectClient) ;
  936.    }
  937.  
  938.  
  939. void UpdateGraphDisplay (HDC hDC,
  940.                          BOOL bForPaint,
  941.                          LPRECT lpRect,
  942.                          PGRAPHSTRUCT pGraph)
  943. /*
  944.    Effect:        Draw the portions of the graph that change as the
  945.                   graph's values change. This includes the background,
  946.                   the grid, the lines, and the timeline.
  947. */
  948.    {  // UpdateGraphDisplay
  949.    RECT           rectUpdate ;
  950.  
  951.    if (!bForPaint && !IsPrinterDC (hDC) && 
  952.        pGraph->gOptions.iGraphOrHistogram == LINE_GRAPH)
  953.       {
  954.       HBRUSH         hOldBrush ;
  955.  
  956.       rectUpdate = pGraph->rectData ;
  957.       rectUpdate.bottom += 1 ;
  958.  
  959.       IntersectRect (&rectUpdate, lpRect, &rectUpdate) ;
  960.       hOldBrush = SelectBrush (hDC, hBrushFace) ;
  961.  
  962.       PatBlt (hDC, 
  963.               rectUpdate.left, rectUpdate.top,
  964.               rectUpdate.right - rectUpdate.left,
  965.               rectUpdate.bottom - rectUpdate.top,
  966.               PATCOPY) ;
  967.       }
  968.    else
  969.       {
  970.       IntersectRect (&rectUpdate, lpRect, &pGraph->rectData) ;
  971.       }
  972.    
  973.    if (pGraph->gOptions.iGraphOrHistogram == LINE_GRAPH)
  974.       {
  975.       DrawGrid(hDC, pGraph, &rectUpdate, bForPaint) ;
  976.       if (pGraph->pLineFirst != NULL)
  977.           {
  978.           DrawTLGraphData(hDC, bForPaint, &rectUpdate, pGraph) ;
  979.           if (!PlayingBackLog ())
  980.              DisplayTimeLine(hDC, pGraph) ;
  981.           }
  982.       }
  983.    else
  984.       {
  985.       DrawBarChartData (hDC, pGraph) ;
  986.       }
  987.    }  // UpdateGraphDisplay
  988.  
  989.                     
  990. BOOL UpdateTimeLine (HDC hDC, 
  991.                      PGRAPHSTRUCT pGraph,
  992.                      BOOL getLastTimeLocation)
  993. /*
  994.    Called By:     GraphTimer only.
  995.  
  996.    See Also:      UpdateGraphDisplay.
  997. */
  998.    {
  999.    INT     i,
  1000.            xDisplayPoint,
  1001.            xDataPoint ;
  1002.    RECT    rctUpdate ;
  1003.    INT     xLastTime ;
  1004.    INT     rectWidth,
  1005.            xPos,
  1006.            numOfPoints ;
  1007.  
  1008.  
  1009.    if ((xLastTime = pGraph->gTimeLine.xLastTime) != 0)
  1010.       {
  1011.       if ((pGraph->gKnownValue % pGraph->gMaxValues) == 1)
  1012.          {
  1013.          // Data wrap around case
  1014.          rctUpdate.left   = pGraph->rectData.left ;
  1015.          rctUpdate.right  = pGraph->rectData.left +
  1016.              pGraph->gTimeLine.ppd + 1 ;
  1017.          }
  1018.       else
  1019.          {
  1020.          rctUpdate.left   = xLastTime - pGraph->gTimeLine.ppd ;
  1021.          rctUpdate.right  = xLastTime +
  1022.              pGraph->gTimeLine.ppd + 1 ;
  1023.          }
  1024.       rctUpdate.top    = pGraph->rectData.top ;
  1025.       rctUpdate.bottom = pGraph->rectData.bottom + 1 ;
  1026.       }
  1027.  
  1028.    // Calculate where to draw the time line.
  1029.    // This is done by running a simple DDA (Digital Differential Analyzer)
  1030.    // We have to position the time depending upon the size of the
  1031.    // graph window.  In essence we need to calculate the x display
  1032.    // coordinate.
  1033.  
  1034.    // Note we should wrap Known Value in UpdateGLData.
  1035.    // We should also use a data buffer of 256 bytes so we can
  1036.    // wrap with and AND.
  1037.  
  1038.    // xDataPoint =  pGraph->gKnownValue ;
  1039.    xDataPoint =  pGraph->gKnownValue % pGraph->gMaxValues ;
  1040.  
  1041.    xDisplayPoint = pGraph->rectData.left ;
  1042.  
  1043.    numOfPoints = pGraph->gMaxValues - 1 ;
  1044.  
  1045.    if (!PlayingBackLog() &&
  1046.       pGraph->gOptions.iGraphOrHistogram == LINE_GRAPH)
  1047.       {
  1048.       // drawing the 0th at the end of the chart.
  1049.       // So, we do have gMaxValues points
  1050.       numOfPoints++ ;
  1051.       if ((pGraph->gKnownValue % pGraph->gMaxValues) == 0)
  1052.          {
  1053.          xDataPoint = pGraph->gMaxValues ;
  1054.          }
  1055.       }
  1056.  
  1057.    rectWidth = pGraph->rectData.right - pGraph->rectData.left ;
  1058.  
  1059.    for (i = 0 ; i < xDataPoint ; i++)
  1060.       {
  1061.       if (numOfPoints == 0)
  1062.          {
  1063.          break ;
  1064.          }
  1065.       xPos = DDA_DISTRIBUTE (rectWidth, numOfPoints) ;
  1066.       xDisplayPoint += xPos ;
  1067.       rectWidth -= xPos ;
  1068.       numOfPoints-- ;
  1069.       }  // for
  1070.  
  1071.    pGraph->gTimeLine.xLastTime = xDisplayPoint ;
  1072.  
  1073.    if (!getLastTimeLocation && iPerfmonView == IDM_VIEWCHART && !bPerfmonIconic)
  1074.       {
  1075.       UpdateGraphDisplay (hDC, FALSE, &rctUpdate, pGraph) ;
  1076.       }
  1077.  
  1078.    return(TRUE) ;
  1079.    }
  1080.  
  1081.  
  1082. //==========================================================================//
  1083. //                              Message Handlers                            //
  1084. //==========================================================================//
  1085.  
  1086.  
  1087. void /*static*/ OnCreate (HWND hWnd)
  1088. /*
  1089.    Effect:        Perform all actions needed when a GraphDisplay window is 
  1090.                   created.
  1091.                   In particular, initialize the graph instance data and
  1092.                   create the child windows.
  1093.  
  1094.    Called By:     GraphDisplayWndProc, in response to a WM_CREATE message.
  1095. */
  1096.    {  // OnCreate
  1097.    LOGBRUSH       LogBrush ;
  1098.    TEXTMETRIC     tmScales ;
  1099.  
  1100.    hGraphDisplayDC = GetDC(hWnd) ;
  1101.  
  1102.    SelectFont(hGraphDisplayDC, hFontScales) ;
  1103.    GetTextMetrics(hGraphDisplayDC, &tmScales) ;
  1104.    HalfTextHeight = tmScales.tmHeight / 2 ;
  1105.  
  1106.    SetBkColor (hGraphDisplayDC, crLightGray) ;
  1107.  
  1108.  
  1109.    InsertGraph(hWnd) ;
  1110.    bInitTimeLine(pGraphs) ;
  1111.  
  1112.    pGraphs->hWnd = hWnd ;
  1113.  
  1114.    // Create the brush and pen used by the time line.
  1115.    // We don't want to create these on every timer tick.
  1116.  
  1117.    LogBrush.lbStyle = BS_SOLID ;
  1118.    LogBrush.lbColor = RGB(0xff, 0, 0) ;
  1119.    LogBrush.lbHatch = 0 ;
  1120.  
  1121.    // Now get the system resources we use "all the time"
  1122.    pGraphs->hbRed = CreateBrushIndirect(&LogBrush) ;
  1123.    pGraphs->hGridPen = CreatePen (PS_SOLID, 1, crGray) ;
  1124.  
  1125.    pGraphs->xNumTics = 0 ;
  1126.    pGraphs->yNumTics = 0 ;
  1127.    }  // OnCreate
  1128.  
  1129.  
  1130. void /*static*/ OnSize (HWND hWnd,
  1131.                     WORD xWidth,
  1132.                     WORD yHeight)
  1133.    {  // OnSize
  1134.    PGRAPHSTRUCT   pGraph ;
  1135.  
  1136.    pGraph = GraphData (hWnd) ;
  1137.  
  1138.    SizeGraphDisplayComponents (hWnd) ;
  1139.    }  // OnSize
  1140.  
  1141.  
  1142. void /*static*/ OnPaint (HWND hWnd)
  1143.    {
  1144.    HDC            hDC ;
  1145.    PAINTSTRUCT    ps ;
  1146.    PGRAPHSTRUCT   pGraph ;
  1147.  
  1148.    pGraph = GraphData (hWnd) ;
  1149.    hDC = BeginPaint(hWnd, &ps) ;
  1150.  
  1151.    DrawGraphDisplay (hDC, ps.rcPaint, pGraph) ;
  1152.  
  1153.    EndPaint(hWnd, &ps) ;
  1154.    }  // OnPaint
  1155.  
  1156.  
  1157. //==========================================================================//
  1158. //                             Exported Functions                           //
  1159. //==========================================================================//
  1160.  
  1161. #ifdef KEEP_PRINT
  1162. void PrintGraphDisplay (HDC hDC,
  1163.                         PGRAPHSTRUCT pGraph)
  1164.    {
  1165.    DrawGraphScale (hDC, pGraph) ;
  1166.  
  1167.  
  1168. //!!   UpdateGraphDisplay (hDC, TRUE, &(pGraph->rectData), pGraph) ;
  1169.  
  1170.    IntersectClipRect (hDC, 0, 0, 10000, 10000) ;
  1171.    SelectBrush (hDC, GetStockObject (HOLLOW_BRUSH)) ;
  1172.    SelectPen (hDC, GetStockObject (BLACK_PEN)) ;
  1173.    Rectangle (hDC, 
  1174.               pGraph->rectData.left,
  1175.               pGraph->rectData.top,
  1176.               pGraph->rectData.right,
  1177.               pGraph->rectData.bottom) ;
  1178.    }  // PrintGraphDisplay
  1179. #endif
  1180.  
  1181.  
  1182.  
  1183. void DrawGraphDisplay (HDC hDC,
  1184.                        RECT rectDraw,
  1185.                        PGRAPHSTRUCT pGraph)
  1186.    {
  1187.    BOOL        bPaintScale ;
  1188.    INT         LocalThreeDPad = ThreeDPad - 1 ;
  1189.  
  1190.    // Only draw the vertical labels if the paint rectangle
  1191.    // any portion of the window to the left of the graph area.
  1192.  
  1193.    bPaintScale = (rectDraw.left <= pGraph->rectVertScale.right) ;
  1194.    if (bPaintScale)
  1195.       DrawGraphScale (hDC, pGraph) ;
  1196.    if (IsPrinterDC (hDC))
  1197.       Rectangle (hDC, 
  1198.                  pGraph->rectData.left,
  1199.                  pGraph->rectData.top,
  1200.                  pGraph->rectData.right,
  1201.                  pGraph->rectData.bottom) ;
  1202.    else
  1203.       ThreeDConcave1 (hDC, 
  1204.                      pGraph->rectData.left - LocalThreeDPad,
  1205.                      pGraph->rectData.top - LocalThreeDPad,
  1206.                      pGraph->rectData.right + LocalThreeDPad,
  1207.                      pGraph->rectData.bottom + LocalThreeDPad + 1) ;
  1208.  
  1209.    UpdateGraphDisplay (hDC, TRUE, &(rectDraw), pGraph) ;
  1210.    }  // DrawGraphDisplay
  1211.  
  1212.  
  1213. LRESULT APIENTRY GraphDisplayWndProc (HWND hWnd, 
  1214.                                       UINT uMsg, 
  1215.                                       WPARAM wParam,
  1216.                                       LPARAM lParam)
  1217.    {  // GraphDisplayWndProc
  1218.    LONG        lret = 0L ;
  1219.    BOOL        bCallDefProc = FALSE ;
  1220.  
  1221.    switch (LOWORD (uMsg))
  1222.       {
  1223.       case WM_LBUTTONDOWN:
  1224.          DoWindowDrag (hWnd, lParam) ;
  1225.          break ;
  1226.  
  1227.       case WM_LBUTTONDBLCLK:
  1228.          SendMessage (hWndMain, uMsg, wParam, lParam) ;
  1229.          break ;
  1230.  
  1231.       case WM_CREATE:
  1232.          OnCreate (hWnd) ;
  1233.          break ;
  1234.  
  1235.       case WM_SIZE:
  1236.          OnSize (hWnd, LOWORD (lParam), HIWORD (lParam)) ;
  1237.          break ;
  1238.  
  1239.       case WM_DESTROY:
  1240.          KillTimer(hWndMain, GRAPH_TIMER_ID) ;
  1241.          break ;
  1242.  
  1243.       case WM_PAINT:
  1244.          OnPaint (hWnd) ;
  1245.          break ;
  1246.  
  1247.       case WM_TIMER:
  1248.          GraphTimer (hWnd, FALSE) ;
  1249.          break ;
  1250.  
  1251.       default:
  1252.          bCallDefProc = TRUE ;
  1253.          break ;
  1254.       }  // switch
  1255.  
  1256.  
  1257.    if (bCallDefProc)
  1258.       {
  1259.       lret = DefWindowProc(hWnd, uMsg, wParam, lParam) ;
  1260.       }
  1261.    return (lret) ;
  1262.    }
  1263.  
  1264.  
  1265. BOOL GraphDisplayInitializeApplication (void)
  1266.    {  // GraphDisplayInitializeApplication
  1267.    WNDCLASS       wc ;
  1268.  
  1269.    wc.style         = dwGraphDisplayClassStyle ;
  1270.    wc.lpfnWndProc   = (WNDPROC) GraphDisplayWndProc ;
  1271.    wc.hInstance     = hInstance ;
  1272.    wc.cbClsExtra    = iGraphDisplayWindowExtra ;
  1273.    wc.cbWndExtra    = iGraphDisplayClassExtra ;
  1274.    wc.hIcon         = NULL ;
  1275.    wc.hCursor       = LoadCursor(NULL, IDC_ARROW) ;
  1276.    wc.hbrBackground = hbLightGray ;
  1277.    wc.lpszMenuName  = NULL ;
  1278.    wc.lpszClassName = (LPTSTR) szGraphDisplayWindowClass ;
  1279.  
  1280.    return (RegisterClass (&wc)) ;
  1281.    }  // GraphDisplayInitializeApplication
  1282.  
  1283.  
  1284.  
  1285. HWND CreateGraphDisplayWindow (HWND hWndGraph)
  1286.    {
  1287.    return (CreateWindow (szGraphDisplayWindowClass,   // class
  1288.                          NULL,                        // caption
  1289.                          dwGraphDisplayWindowStyle,   // window style
  1290.                          0, 0,                        // position
  1291.                          0, 0,                        // size
  1292.                          hWndGraph,                   // parent window
  1293.                          NULL,                        // menu
  1294.                          hInstance,                  // program instance
  1295.                          NULL)) ;                     // user-supplied data
  1296.    }
  1297.  
  1298.  
  1299.  
  1300. BOOL ToggleGraphRefresh (HWND hWnd)
  1301.    {  // ToggleGraphRefresh
  1302.    PGRAPHSTRUCT   pGraph ;
  1303.  
  1304.    pGraph = GraphData (hWnd) ;
  1305.  
  1306.    if (pGraph->bManualRefresh)
  1307.       SetGraphTimer (pGraph) ;
  1308.    else
  1309.       ClearGraphTimer (pGraph) ;
  1310.  
  1311.    pGraph->bManualRefresh = !pGraph->bManualRefresh ;
  1312.    return (pGraph->bManualRefresh) ;
  1313.    }  // ToggleGraphRefresh
  1314.  
  1315. BOOL GraphRefresh (HWND hWnd)
  1316.    {  // GraphRefresh
  1317.    PGRAPHSTRUCT   pGraph ;
  1318.  
  1319.    pGraph = GraphData (hWnd) ;
  1320.  
  1321.    return (pGraph->bManualRefresh) ;
  1322.    }  // GraphRefresh
  1323.  
  1324.  
  1325. void GraphTimer (HWND hWnd, 
  1326.                  BOOL bForce)
  1327. /*
  1328.    Effect:        Handle any actions necessary when the graph display
  1329.                   window hWnd receives a timer tick. In particular,
  1330.                   update the graph display and update the status bar
  1331.                   if it is showing.
  1332.  
  1333.    Called By:     GraphDisplayWndProc, in response to a WM_TIMER message.
  1334. */
  1335.    {  // GraphTimer
  1336.    PGRAPHSTRUCT         pGraph ;
  1337.  
  1338.    pGraph = GraphData (hWnd) ;
  1339.  
  1340.    if (!pGraph->pLineFirst)
  1341.       return ;
  1342.  
  1343.  
  1344.    if (bForce || !pGraph->bManualRefresh)
  1345.       {
  1346.  
  1347.    
  1348.       HandleGraphTimer () ;
  1349.  
  1350.       // If we are displaying a time-line graph then do the
  1351.       // calculations for the minimal update region.
  1352.       // If were doing a bar graph, then draw the
  1353.       // whole graph area.
  1354.  
  1355.       if (pGraph->gOptions.iGraphOrHistogram == LINE_GRAPH)
  1356.          UpdateTimeLine (hGraphDisplayDC, pGraph, FALSE) ;
  1357.       else
  1358.          DrawBarChartData (hGraphDisplayDC, pGraph) ;
  1359.  
  1360.       // Take care of the status bar window
  1361.       StatusTimer (hWndGraphStatus, FALSE) ;
  1362.  
  1363.       }  // if
  1364.    }  // GraphTimer
  1365.  
  1366. // this routine set/reset the line highlight mode
  1367. void ChartHighlight (void)
  1368.    {
  1369.    PGRAPHSTRUCT         pGraph ;
  1370.  
  1371.  
  1372.    if (pGraph = GraphData (hWndGraph))
  1373.       {
  1374.       if (pGraph->pLineFirst)
  1375.          {
  1376.          // toggle the HightlightOnOff mode
  1377.          pGraph->HighLightOnOff = !pGraph->HighLightOnOff ;
  1378.          WindowInvalidate (hWndGraphDisplay) ;
  1379.          }
  1380.       else
  1381.          {
  1382.          // no line availble, just turn it off
  1383.          pGraph->HighLightOnOff = FALSE ;
  1384.          }
  1385.       }
  1386.    }  // ChartHighlight
  1387.  
  1388. 
  1389.