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 / rptfct.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-10-05  |  50.5 KB  |  1,616 lines

  1. /*****************************************************************************
  2.  *
  3.  *  RptFct.c - This file contains support routines for the Report view.
  4.  *       They are moved here because Report.c is getting too big.
  5.  *
  6.  *  Microsoft Confidential
  7.  *  Copyright (c) 1992-1997 Microsoft Corporation
  8.  *
  9.  *  Author -
  10.  *
  11.  *       Hon-Wah Chan
  12.  *
  13.  ****************************************************************************/
  14.  
  15. #include "perfmon.h"
  16. #include <stdio.h>      // for sprintf
  17. #include "report.h"     // Exported declarations for this file
  18.  
  19. #include "line.h"       // for LineAppend
  20. #include "pmemory.h"    // for MemoryXXX (mallloc-type) routines
  21. #include "system.h"     // for SystemGet
  22. #include "utils.h"
  23. #include "perfmops.h"   // for BuildValueListForSystems
  24.  
  25. // extern defined in Report.c
  26.  
  27. extern TCHAR          szSystemFormat [] ;
  28. extern TCHAR          szObjectFormat [] ;
  29.  
  30.  
  31. #define szValuePlaceholder          TEXT("-999999999.999")
  32. #define szLargeValueFormat          TEXT("%12.0f")
  33. #define eStatusLargeValueMax        ((FLOAT) 999999999.0)
  34. #define szValueFormat               TEXT("%12.3f")
  35.  
  36. //========================
  37. // Local routines prototypes
  38. //========================
  39.  
  40. void ColumnRemoveOne (PREPORT pReport,
  41.                       POBJECTGROUP pObjectGroup,
  42.                       int iColumnNumber) ;
  43.  
  44. BOOL CounterGroupRemove (PCOUNTERGROUP *ppCounterGroupFirst,
  45.                         PCOUNTERGROUP pCounterGroupRemove) ;
  46.  
  47. BOOL ObjectGroupRemove (POBJECTGROUP *ppObjectGroupFirst,
  48.                         POBJECTGROUP pObjectGroupRemove) ;
  49.  
  50. BOOL SystemGroupRemove (PSYSTEMGROUP *ppSystemGroupFirst,
  51.                         PSYSTEMGROUP pSystemGroupRemove) ;
  52.  
  53. PCOUNTERGROUP GetNextCounter (PSYSTEMGROUP   pSystemGroup,
  54.                               POBJECTGROUP   pObjectGroup,
  55.                               PCOUNTERGROUP  pCounterGroup) ;
  56.  
  57. // CheckColumnGroupRemove is used to check if the given
  58. // column is empty.  If it is empty, it is removed from 
  59. // the column link list
  60. void CheckColumnGroupRemove (PREPORT      pReport,
  61.                              POBJECTGROUP pObjectGroup,
  62.                              int          iReportColumn)
  63.    {
  64.    // check if we need to remove the this column
  65.    PLINE          pCounterLine ;
  66.    PCOUNTERGROUP  pCounterGrp ;
  67.    BOOL           bColumnFound = FALSE ;
  68.  
  69.    if (iReportColumn < 0 || pObjectGroup->pCounterGroupFirst == NULL)
  70.       {
  71.       // no column for this Counter group, forget it
  72.       return ;
  73.       }
  74.  
  75.  
  76.    // go thru each Counter group and check if any line in the
  77.    // group matches the given column number
  78.    for (pCounterGrp = pObjectGroup->pCounterGroupFirst ;
  79.         pCounterGrp ;
  80.         pCounterGrp = pCounterGrp->pCounterGroupNext )
  81.       {
  82.       for (pCounterLine = pCounterGrp->pLineFirst ;
  83.            pCounterLine ;
  84.            pCounterLine = pCounterLine->pLineCounterNext)
  85.          {
  86.          if (pCounterLine->iReportColumn == iReportColumn)
  87.             {
  88.             // found one, this column is not empty
  89.             bColumnFound = TRUE ;
  90.             break ;
  91.             }
  92.          }  // for pCounterLine
  93.       if (bColumnFound)
  94.          {
  95.          break ;
  96.          }
  97.       }  // for pCounterGrp
  98.  
  99.    if (bColumnFound == FALSE)
  100.       {
  101.       // we have deleted the last column item, remove this column
  102.       ColumnRemoveOne (pReport,
  103.          pObjectGroup,
  104.          iReportColumn) ;
  105.       }
  106.    }  // CheckColumnGroupRemove
  107.  
  108.  
  109. //================================
  110. // Line routine
  111. //================================
  112.  
  113. void ReportLineValueRect (PREPORT pReport,
  114.                           PLINE pLine,
  115.                           LPRECT lpRect)
  116.    {  // ReportLineValueRect
  117.    lpRect->left = ValueMargin (pReport) + pLine->xReportPos ;
  118.    lpRect->top = pLine->yReportPos ;
  119.    lpRect->right = lpRect->left + pReport->xValueWidth ;
  120.    lpRect->bottom = lpRect->top + pReport->yLineHeight ;
  121.    }  // ReportLineValueRect
  122.  
  123. PLINE LineRemoveItem (PREPORT pReport,
  124.                       enum REPORT_ITEM_TYPE  *pNewItemType)
  125.    {
  126.    PLINE          pLine ;
  127.    PLINE          pNextLine = NULL ;
  128.    PLINE          pReturnLine = NULL ;
  129.    PLINE          pLeftLine = NULL ;
  130.    PSYSTEMGROUP   pSystemGroup ;
  131.    POBJECTGROUP   pObjectGroup ;
  132.    PCOUNTERGROUP  pCounterGroup ;
  133.    PCOUNTERGROUP  pNextCounterGroup ;
  134.    BOOL           bCreatNewCounterGroup ;
  135.  
  136.  
  137.    //=============================//
  138.    // Remove line, line's system  //
  139.    //=============================//
  140.  
  141.    pLine = pReport->CurrentItem.pLine ;
  142.    LineRemove (&pReport->pLineFirst, pLine) ;
  143.  
  144.    // no more line, no more timer...
  145.    if (!pReport->pLineFirst)
  146.       {
  147.       pReport->xWidth = 0 ;
  148.       pReport->yHeight = 0 ;
  149.       pReport->xMaxCounterWidth = 0 ;
  150.       ClearReportTimer (pReport) ;
  151.       }
  152.  
  153.    //=============================//
  154.    // Get correct spot; remove line //
  155.    //=============================//
  156.  
  157.    pSystemGroup = GetSystemGroup (pReport, pLine->lnSystemName) ;
  158.    pObjectGroup = GetObjectGroup (pSystemGroup, pLine->lnObjectName) ;
  159.    pCounterGroup = GetCounterGroup (pObjectGroup,
  160.       pLine->lnCounterDef.CounterNameTitleIndex,
  161.       &bCreatNewCounterGroup,
  162.       pLine->lnCounterName) ;
  163.  
  164.    if (!pCounterGroup)
  165.       return (NULL) ;
  166.  
  167.    LineCounterRemove (&pCounterGroup->pLineFirst, pLine) ;
  168.  
  169.    // check which line to get the focus
  170.    if (pCounterGroup->pLineFirst)
  171.       {
  172.       // simple case, we still have line in the same counter group
  173.       // get the one right after this delete line.
  174.       for (pNextLine = pCounterGroup->pLineFirst ;
  175.            pNextLine ;
  176.            pNextLine = pNextLine->pLineCounterNext)
  177.          {
  178.          if (pNextLine->xReportPos > pLine->xReportPos)
  179.             {
  180.             if (pReturnLine == NULL || 
  181.                 pReturnLine->xReportPos > pNextLine->xReportPos)
  182.                {
  183.                pReturnLine = pNextLine ;
  184.                }
  185.             }
  186.          else
  187.             {
  188.             if (pLeftLine == NULL ||
  189.                 pLeftLine->xReportPos < pNextLine->xReportPos)
  190.                {
  191.                pLeftLine = pNextLine ;
  192.                }
  193.             }
  194.          }
  195.  
  196.       if (!pReturnLine && pLeftLine)
  197.          {
  198.          // the delete line is the last column, then use the line 
  199.          // to its left
  200.          pReturnLine = pLeftLine ;
  201.          }
  202.       }
  203.    else
  204.       {
  205.       pNextCounterGroup = GetNextCounter (
  206.             pSystemGroup,
  207.             pObjectGroup,
  208.             pCounterGroup) ;
  209.  
  210.       if (pNextCounterGroup)
  211.          {
  212.          pLeftLine = NULL ;
  213.          for (pNextLine = pNextCounterGroup->pLineFirst ;
  214.               pNextLine ;
  215.               pNextLine = pNextLine->pLineCounterNext)
  216.             {
  217.             // get the line in the first column
  218.             if (pLeftLine == NULL ||
  219.                 pNextLine->xReportPos < pLeftLine->xReportPos)
  220.                {
  221.                pLeftLine = pNextLine ;
  222.                }
  223.             }
  224.          pReturnLine = pLeftLine ;
  225.          }
  226.  
  227.       // remove this counter group if there is no line
  228.       CounterGroupRemove (&pObjectGroup->pCounterGroupFirst, pCounterGroup) ;
  229.       }
  230.       
  231.    // check if we need to remove any empty column
  232.    CheckColumnGroupRemove (pReport, pObjectGroup, pLine->iReportColumn) ;
  233.  
  234.    if (!(pObjectGroup->pCounterGroupFirst))
  235.       ObjectGroupRemove (&pSystemGroup->pObjectGroupFirst, pObjectGroup) ;
  236.  
  237.    if (!(pSystemGroup->pObjectGroupFirst))
  238.       SystemGroupRemove (&pReport->pSystemGroupFirst, pSystemGroup) ;
  239.  
  240.  
  241.    LineFree (pLine) ;
  242.  
  243.    if (pReturnLine && pNewItemType)
  244.       {
  245.       *pNewItemType = REPORT_TYPE_LINE ;
  246.       }
  247.  
  248.    return (pReturnLine) ;
  249.    }  // LineRemoveItem
  250.  
  251. //======================================//
  252. // Column Group routines                //
  253. //======================================//
  254.  
  255. void ReportColumnRect (PREPORT pReport,
  256.                        PCOLUMNGROUP pColumnGroup,
  257.                        LPRECT  lpRect)
  258.    {  // ReportColumnRect
  259.    lpRect->left = ValueMargin (pReport) + pColumnGroup->xPos ;
  260.    lpRect->top = pColumnGroup->yFirstLine ;
  261.    lpRect->right = lpRect->left + pColumnGroup->xWidth ;
  262.    lpRect->bottom = lpRect->top + pReport->yLineHeight ;
  263.    if (pColumnGroup->lpszParentName)
  264.       {
  265.       lpRect->top -= pReport->yLineHeight ;
  266.       }
  267.    }  // ReportColumnRect
  268.  
  269.  
  270. BOOL ColumnSame (PCOLUMNGROUP pColumnGroup,
  271.                  LPTSTR lpszParentName,
  272.                  LPTSTR lpszInstanceName)
  273.    {  // ColumnSame
  274.    BOOL           bParentSame ;
  275.    BOOL           bInstanceSame ;
  276.  
  277.    bParentSame = (!lpszParentName && !pColumnGroup->lpszParentName) ||
  278.                  strsame (lpszParentName, pColumnGroup->lpszParentName) ;
  279.    bInstanceSame = (!lpszInstanceName && !pColumnGroup->lpszInstanceName) ||
  280.                  strsame (lpszInstanceName, pColumnGroup->lpszInstanceName) ;
  281.  
  282.    return (bParentSame && bInstanceSame) ;                 
  283.    }  // ColumnSame
  284.  
  285.  
  286. PCOLUMNGROUP ColumnGroupCreate (PREPORT pReport,
  287.                                 int xPos,
  288.                                 LPTSTR lpszParentName,
  289.                                 LPTSTR lpszInstanceName,
  290.                                 int PreviousColumnNumber,
  291.                                 int yFirstLine)
  292.    {  // ColumnGroupCreate
  293.    PCOLUMNGROUP   pColumnGroup ;
  294.    HDC            hDC ;
  295.  
  296.    hDC = GetDC (pReport->hWnd) ;
  297.    pColumnGroup = MemoryAllocate (sizeof (COLUMNGROUP)) ;
  298.  
  299.    if (pColumnGroup)
  300.       {
  301.       pColumnGroup->pColumnGroupNext = NULL ;
  302.       pColumnGroup->lpszParentName = StringAllocate (lpszParentName) ;
  303.       pColumnGroup->lpszInstanceName = StringAllocate (lpszInstanceName) ;
  304.       pColumnGroup->ParentNameTextWidth = TextWidth (hDC, lpszParentName) ;
  305.       pColumnGroup->InstanceNameTextWidth = TextWidth (hDC, lpszInstanceName) ;
  306.       pColumnGroup->xPos = xPos ;
  307.       pColumnGroup->yFirstLine = yFirstLine ;
  308.       pColumnGroup->ColumnNumber = PreviousColumnNumber + 1 ;
  309.       pColumnGroup->xWidth = max (max (pColumnGroup->ParentNameTextWidth,
  310.                                        pColumnGroup->InstanceNameTextWidth),
  311.                                   pReport->xValueWidth) ;
  312.  
  313.       pReport->xWidth = max (pReport->xWidth,
  314.                              RightHandMargin +
  315.                              ValueMargin (pReport) + 
  316.                              pColumnGroup->xPos + pColumnGroup->xWidth + 
  317.                              xColumnMargin) ;
  318.       }  // if
  319.  
  320.    ReleaseDC (pReport->hWnd, hDC) ;
  321.    return (pColumnGroup) ;
  322.    }  // ColumnGroupCreate
  323.  
  324. PCOLUMNGROUP GetColumnGroup (PREPORT pReport,
  325.                           POBJECTGROUP pObjectGroup,
  326.                           PLINE pLine)
  327. /*
  328.    Effect:        Return a pointer to the appropriate column group from
  329.                   within the groups of pObject. If the line is a counter
  330.                   without instances, return NULL. Otherwise, determine
  331.                   if the counter's parent/instance pair is already found
  332.                   in an existing column and return a pointer to that column.
  333.  
  334.                   If a column with the appropriate parent/instance isn't
  335.                   found, add a new column *at the end*, and return that
  336.                   column.
  337.  
  338.    Note:          This function has multiple return points.
  339. */
  340.    {  // GetColumnGroup
  341.    PCOLUMNGROUP   pColumnGroup ;
  342.    LPTSTR         lpszParentName ;
  343.    LPTSTR         lpszInstanceName ;
  344.  
  345.  
  346.    if (!LineInstanceName (pLine))
  347.       return (NULL) ;
  348.  
  349.  
  350.    lpszParentName = LineParentName (pLine) ;
  351.    lpszInstanceName = LineInstanceName (pLine) ;
  352.       
  353.    if (!pObjectGroup->pColumnGroupFirst)
  354.       {
  355.       pObjectGroup->pColumnGroupFirst = 
  356.          ColumnGroupCreate (pReport,
  357.             0,
  358.             lpszParentName,
  359.             lpszInstanceName,
  360.             -1,
  361.             pObjectGroup->yFirstLine) ;
  362.  
  363.       if (pObjectGroup->pColumnGroupFirst)
  364.          {
  365.          pObjectGroup->pColumnGroupFirst->pParentObject =
  366.             pObjectGroup ;
  367.          }
  368.  
  369.       return (pObjectGroup->pColumnGroupFirst) ;
  370.       }
  371.  
  372.    for (pColumnGroup = pObjectGroup->pColumnGroupFirst ;
  373.         pColumnGroup ;
  374.         pColumnGroup = pColumnGroup->pColumnGroupNext)
  375.       {  // for
  376.       if (ColumnSame (pColumnGroup, lpszParentName, lpszInstanceName))
  377.          return (pColumnGroup) ;
  378.  
  379.       else if (!pColumnGroup->pColumnGroupNext)
  380.          {  // if
  381.          pColumnGroup->pColumnGroupNext = 
  382.             ColumnGroupCreate (pReport,
  383.                                pColumnGroup->xPos + pColumnGroup->xWidth +
  384.                                xColumnMargin,
  385.                                lpszParentName,
  386.                                lpszInstanceName,
  387.                                pColumnGroup->ColumnNumber,
  388.                                pObjectGroup->yFirstLine) ;
  389.  
  390.          if (pColumnGroup->pColumnGroupNext)
  391.             {
  392.             (pColumnGroup->pColumnGroupNext)->pParentObject =
  393.                pObjectGroup ;
  394.  
  395.             // build the double link-list
  396.             (pColumnGroup->pColumnGroupNext)->pColumnGroupPrevious =
  397.                pColumnGroup ;
  398.             }
  399.  
  400.          return (pColumnGroup->pColumnGroupNext) ;
  401.          }  // if
  402.       }  // for
  403.  
  404.    return (NULL) ;
  405.    }  // GetColumnGroup
  406.  
  407. // ColumnRemoveOne removes the column with the specified column number
  408. void ColumnRemoveOne (PREPORT pReport,
  409.                       POBJECTGROUP pObjectGroup,
  410.                       int iColumnNumber)
  411.    {
  412.    PCOLUMNGROUP   pColumnGroup ;
  413.    PCOLUMNGROUP   pNextColumnGroup ;
  414.  
  415.    if (pObjectGroup->pColumnGroupFirst == NULL)
  416.       {
  417.       // no column group, forget it
  418.       return ;
  419.       }
  420.  
  421.    // Find the head list
  422.    if (pObjectGroup->pColumnGroupFirst->ColumnNumber == iColumnNumber)
  423.       {
  424.       pColumnGroup = pObjectGroup->pColumnGroupFirst ;
  425.       pObjectGroup->pColumnGroupFirst = pColumnGroup->pColumnGroupNext ;
  426.       if (pColumnGroup->pColumnGroupNext)
  427.          {
  428.          // set up head of backward link list
  429.          (pColumnGroup->pColumnGroupNext)->pColumnGroupPrevious = NULL ;
  430.          }
  431.  
  432.       // free memory for this column group
  433.       MemoryFree (pColumnGroup->lpszParentName) ;
  434.       MemoryFree (pColumnGroup->lpszInstanceName) ;
  435.       MemoryFree (pColumnGroup) ;
  436.  
  437.       return ;
  438.       }
  439.    
  440.    // go thru the double link list to look for the right column
  441.    for (pColumnGroup = pObjectGroup->pColumnGroupFirst ;
  442.         pColumnGroup ;
  443.         pColumnGroup = pNextColumnGroup)
  444.       {
  445.       pNextColumnGroup = pColumnGroup->pColumnGroupNext ;
  446.  
  447.       if (pNextColumnGroup == NULL)
  448.          {
  449.          // forget it if we can't find this column for some reson.
  450.          break ;
  451.          }
  452.  
  453.       if (pNextColumnGroup->ColumnNumber == iColumnNumber)
  454.          {
  455.          pColumnGroup->pColumnGroupNext = pNextColumnGroup->pColumnGroupNext ;
  456.  
  457.          // build backward link iff it is not the end of list
  458.          if (pColumnGroup->pColumnGroupNext)
  459.             {
  460.             (pColumnGroup->pColumnGroupNext)->pColumnGroupPrevious =
  461.                pColumnGroup ;
  462.             }
  463.  
  464.          // free memory for this column group
  465.          MemoryFree (pNextColumnGroup->lpszParentName) ;
  466.          MemoryFree (pNextColumnGroup->lpszInstanceName) ;
  467.          MemoryFree (pNextColumnGroup) ;
  468.  
  469.          // Done
  470.          break ;
  471.          }
  472.       }
  473.    }  // ColumnRemoveOne
  474.  
  475. // ColumnGroupRemove removes all the columns for a given column list
  476. void ColumnGroupRemove (PCOLUMNGROUP pColumnGroupFirst)
  477.    {
  478.    PCOLUMNGROUP   pColumnGroup ;
  479.    PCOLUMNGROUP   pNextColumnGroup ;
  480.  
  481.    for (pColumnGroup = pColumnGroupFirst ;
  482.         pColumnGroup ;
  483.         pColumnGroup = pNextColumnGroup)
  484.       {
  485.       pNextColumnGroup = pColumnGroup->pColumnGroupNext ;
  486.  
  487.       // free memory for this column group
  488.       MemoryFree (pColumnGroup->lpszParentName) ;
  489.       MemoryFree (pColumnGroup->lpszInstanceName) ;
  490.       MemoryFree (pColumnGroup) ;
  491.       }
  492.    }  // ColumnGroupRemove
  493.  
  494. // ColumnRemoveItem  is called when user wants to delete a
  495. // selected column.
  496. PCOLUMNGROUP ColumnRemoveItem (PREPORT      pReport,
  497.                                PCOLUMNGROUP pColumnGroup,
  498.                                BOOL         bCleanUpLink,
  499.                                enum REPORT_ITEM_TYPE  *pNewItemType)
  500.    {
  501.    PLINE          pLine, pNextLine ;
  502.    PSYSTEMGROUP   pSystemGroup ;
  503.    POBJECTGROUP   pObjectGroup ;
  504.    PCOUNTERGROUP  pCounterGroup, pNextCounterGroup ;
  505.    BOOL           bColumnFound ;
  506.    PCOLUMNGROUP   pRetColumnGroup = NULL ;
  507.  
  508.    pObjectGroup = pColumnGroup->pParentObject ;
  509.    pSystemGroup = pObjectGroup->pParentSystem ;
  510.  
  511.    // first, get rid of all the counter lines with this column number
  512.    // Note - each Counter group has only 1 line (or 0) with this
  513.    // column number
  514.    for (pCounterGroup = pObjectGroup->pCounterGroupFirst ;
  515.         pCounterGroup ;
  516.         pCounterGroup = pNextCounterGroup )
  517.       {
  518.       pNextCounterGroup = pCounterGroup->pCounterGroupNext ;
  519.       bColumnFound = FALSE ;
  520.  
  521.       for (pLine = pCounterGroup->pLineFirst ;
  522.            pLine ;
  523.            pLine = pNextLine)
  524.          {
  525.          pNextLine = pLine->pLineCounterNext ;
  526.          if (pLine->iReportColumn == pColumnGroup->ColumnNumber)
  527.             {
  528.             bColumnFound = TRUE ;
  529.             // delete this line
  530.             LineRemove (&pReport->pLineFirst, pLine) ;
  531.             LineCounterRemove (&pCounterGroup->pLineFirst, pLine) ;
  532.             LineFree (pLine) ;
  533.             break ;
  534.             }
  535.          }
  536.  
  537.       if (bColumnFound)
  538.          {
  539.          // check if we need delete this counter group
  540.          if (!(pCounterGroup->pLineFirst))
  541.             {
  542.             CounterGroupRemove (&pObjectGroup->pCounterGroupFirst, pCounterGroup) ;
  543.             }
  544.          }
  545.       }
  546.  
  547.       
  548.    // determine which column group to go after deleting this
  549.    if (pColumnGroup->pColumnGroupNext)
  550.       {
  551.       // get the Column group after this delete one
  552.       pRetColumnGroup = pColumnGroup->pColumnGroupNext ;
  553.       }
  554.    else
  555.       {
  556.       // get the Counter group before this delete one
  557.       pRetColumnGroup = pColumnGroup->pColumnGroupPrevious ;
  558.       }
  559.  
  560.    if (pNewItemType)
  561.       {
  562.       if (pRetColumnGroup)
  563.          {
  564.          *pNewItemType = REPORT_TYPE_COLUMN ;
  565.          }
  566.       else
  567.          {
  568.          // get next counter group
  569.          pNextCounterGroup = GetNextCounter (
  570.             pSystemGroup,
  571.             pObjectGroup,
  572.             NULL) ;
  573.  
  574.          if (pNextCounterGroup)
  575.             {
  576.             // we have to return Counter group, so we have to do the
  577.             // dirty casting..
  578.             *pNewItemType = REPORT_TYPE_COUNTER ;
  579.             pRetColumnGroup = (PCOLUMNGROUP) pNextCounterGroup ;
  580.             }
  581.          }
  582.       }
  583.  
  584.  
  585.    // remove this column group
  586.    ColumnRemoveOne (pReport, pObjectGroup, pColumnGroup->ColumnNumber) ;
  587.  
  588.    // check for further cleanup
  589.    if (bCleanUpLink)
  590.       {
  591.       if (!(pObjectGroup->pCounterGroupFirst))
  592.          ObjectGroupRemove (&pSystemGroup->pObjectGroupFirst, pObjectGroup) ;
  593.  
  594.       if (!(pSystemGroup->pObjectGroupFirst))
  595.          SystemGroupRemove (&pReport->pSystemGroupFirst, pSystemGroup) ;
  596.       }
  597.    return (pRetColumnGroup) ;
  598.    }  // ColumnRemoveItem
  599.  
  600.  
  601. //======================================//
  602. // Counter Group routines               //
  603. //======================================//
  604.  
  605. void ReportCounterRect (PREPORT        pReport,
  606.                         PCOUNTERGROUP  pCounterGroup,
  607.                         LPRECT         lpRect)
  608.    {  // ReportCounterRect
  609.    lpRect->left = xCounterMargin ;
  610.    lpRect->top = pCounterGroup->yLine ;
  611.    lpRect->right = lpRect->left + pCounterGroup->xWidth + yScrollHeight / 2 ;
  612.    lpRect->bottom = lpRect->top + pReport->yLineHeight ;
  613.    }  // ReportCounterRect
  614.  
  615.  
  616. PCOUNTERGROUP CounterGroupCreate (DWORD  dwCounterIndex,
  617.                                   LPTSTR pCounterName) 
  618.    {  // CounterGroupCreate
  619.    PCOUNTERGROUP  pCounterGroup ;
  620.    HDC            hDC ;
  621.    PREPORT        pReport ;
  622.  
  623.    pCounterGroup = MemoryAllocate (sizeof (COUNTERGROUP)) ;
  624.  
  625.    if (pCounterGroup)
  626.       {
  627.       pCounterGroup->pCounterGroupNext = NULL ;
  628.       pCounterGroup->pLineFirst = NULL ;
  629.       pCounterGroup->dwCounterIndex = dwCounterIndex ;
  630.  
  631.       if (pCounterName)
  632.          {
  633.          hDC = GetDC (hWndReport) ;
  634.          pReport = ReportData (hWndReport) ;
  635.          SelectFont (hDC, pReport->hFont) ;
  636.          pCounterGroup->xWidth = TextWidth (hDC, pCounterName) ;
  637.          ReleaseDC (hWndReport, hDC) ;
  638.          }
  639.       }  // if
  640.  
  641.    return (pCounterGroup) ;
  642.    }  // CounterGroupCreate
  643.  
  644.  
  645. PCOUNTERGROUP GetCounterGroup (POBJECTGROUP pObjectGroup,
  646.                             DWORD dwCounterIndex,
  647.                             BOOL *pbCounterGroupCreated,
  648.                             LPTSTR pCounterName) 
  649.    {  // GetCounterGroup
  650.    PCOUNTERGROUP   pCounterGroup ;
  651.  
  652.    *pbCounterGroupCreated = FALSE ;
  653.    if (!pObjectGroup)
  654.       return (FALSE) ;
  655.  
  656.    if (!pObjectGroup->pCounterGroupFirst)
  657.       {
  658.       pObjectGroup->pCounterGroupFirst =
  659.          CounterGroupCreate (dwCounterIndex, pCounterName) ;
  660.  
  661.       if (pObjectGroup->pCounterGroupFirst)
  662.          {
  663.          *pbCounterGroupCreated = TRUE ;
  664.          pObjectGroup->pCounterGroupFirst->pParentObject =
  665.             pObjectGroup ;
  666.          }
  667.  
  668.       return (pObjectGroup->pCounterGroupFirst) ;
  669.       }
  670.  
  671.    for (pCounterGroup = pObjectGroup->pCounterGroupFirst ;
  672.         pCounterGroup ;
  673.         pCounterGroup = pCounterGroup->pCounterGroupNext)
  674.       {  // for
  675.       if (dwCounterIndex && pCounterGroup->dwCounterIndex == dwCounterIndex)
  676.          {
  677.          return (pCounterGroup) ;
  678.          }
  679.       else if (!dwCounterIndex &&
  680.          pCounterGroup->pLineFirst &&
  681.          pstrsame (pCounterGroup->pLineFirst->lnCounterName, pCounterName))
  682.          {
  683.          return (pCounterGroup) ;
  684.          }
  685.       else if (!pCounterGroup->pCounterGroupNext)
  686.          {  // if
  687.          pCounterGroup->pCounterGroupNext = 
  688.             CounterGroupCreate (dwCounterIndex, pCounterName) ;
  689.          if (pCounterGroup->pCounterGroupNext)
  690.             {
  691.             *pbCounterGroupCreated = TRUE ;
  692.             (pCounterGroup->pCounterGroupNext)->pParentObject =
  693.                pObjectGroup ;
  694.  
  695.             // build backward link
  696.             (pCounterGroup->pCounterGroupNext)->pCounterGroupPrevious =
  697.                pCounterGroup ;
  698.             }
  699.          return (pCounterGroup->pCounterGroupNext) ;
  700.          }  // if
  701.       }  // for
  702.  
  703.    return (NULL) ;
  704.    }  // GetCounterGroup
  705.  
  706. BOOL CounterGroupRemove (PCOUNTERGROUP *ppCounterGroupFirst,
  707.                         PCOUNTERGROUP pCounterGroupRemove)
  708.    {
  709.    PCOUNTERGROUP  pCounterGroup ;
  710.  
  711.    if (*ppCounterGroupFirst == pCounterGroupRemove)
  712.       {
  713.       *ppCounterGroupFirst = (*ppCounterGroupFirst)->pCounterGroupNext ;
  714.  
  715.       if (*ppCounterGroupFirst)
  716.          {
  717.          // set up head of backward link list
  718.          (*ppCounterGroupFirst)->pCounterGroupPrevious = NULL ;
  719.          }
  720.  
  721.       MemoryFree (pCounterGroupRemove) ;
  722.       return (TRUE) ;
  723.       }
  724.  
  725.    for (pCounterGroup = *ppCounterGroupFirst ;
  726.         pCounterGroup->pCounterGroupNext ;
  727.         pCounterGroup = pCounterGroup->pCounterGroupNext)
  728.       {   // for
  729.       if (pCounterGroup->pCounterGroupNext == pCounterGroupRemove)
  730.          {
  731.          pCounterGroup->pCounterGroupNext = pCounterGroupRemove->pCounterGroupNext ;
  732.          if (pCounterGroup->pCounterGroupNext)
  733.             {
  734.             (pCounterGroup->pCounterGroupNext)->pCounterGroupPrevious
  735.                = pCounterGroup ;
  736.             }
  737.          MemoryFree (pCounterGroupRemove) ;
  738.          return (TRUE) ;
  739.          }  // if
  740.       }  // for
  741.  
  742.    return (FALSE) ;
  743.    }  // CounterGroupRemove
  744.  
  745.  
  746. // CounterRemoveItem is called when user wants to delete a
  747. // selected counter (row)                        
  748. PCOUNTERGROUP CounterRemoveItem (PREPORT        pReport,
  749.                                  PCOUNTERGROUP  pCounterGroup,
  750.                                  BOOL           bCleanUpLink,
  751.                                  enum REPORT_ITEM_TYPE  *pNewItemType)
  752.    {
  753.    PLINE          pLine, pNextLine ;
  754.    POBJECTGROUP   pObjectGroup ;
  755.    PSYSTEMGROUP   pSystemGroup ;
  756.    PCOLUMNGROUP   pColumnGroup ;
  757.    PCOLUMNGROUP   pNextColumnGroup ;
  758.    PCOUNTERGROUP  pRetCounterGroup = NULL ;
  759.  
  760.    pObjectGroup = pCounterGroup->pParentObject ;
  761.    pSystemGroup = pObjectGroup->pParentSystem ;
  762.  
  763.    // first, remove all the counter lines from this counter group
  764.    // and from the Report line link-list
  765.    for (pLine = pCounterGroup->pLineFirst ;
  766.         pLine ;
  767.         pLine = pNextLine)
  768.       {
  769.       pNextLine = pLine->pLineCounterNext ;
  770.       LineRemove (&pReport->pLineFirst, pLine) ;
  771.       LineFree (pLine) ;
  772.       }
  773.  
  774.    // we only need to delete the counter group iff we are deleting
  775.    // this selected Counter.
  776.    if (bCleanUpLink)
  777.       {
  778.       // determine which counter group to go after deleting this
  779.       pRetCounterGroup = GetNextCounter (
  780.          pSystemGroup ,
  781.          pObjectGroup,
  782.          pCounterGroup) ;
  783.  
  784.       // remove this counter group from its parent object group
  785.       CounterGroupRemove (&pObjectGroup->pCounterGroupFirst, pCounterGroup) ;
  786.  
  787.       if (!(pObjectGroup->pCounterGroupFirst))
  788.          ObjectGroupRemove (&pSystemGroup->pObjectGroupFirst, pObjectGroup) ;
  789.       else
  790.          {
  791.          // Object group not empty, check for any empty column
  792.          for (pColumnGroup = pObjectGroup->pColumnGroupFirst ;
  793.             pColumnGroup ;
  794.             pColumnGroup = pNextColumnGroup)
  795.             {
  796.             pNextColumnGroup = pColumnGroup->pColumnGroupNext ;
  797.             CheckColumnGroupRemove (pReport, pObjectGroup, pColumnGroup->ColumnNumber) ;
  798.             }
  799.          }
  800.  
  801.       if (!(pSystemGroup->pObjectGroupFirst))
  802.          {
  803.          SystemGroupRemove (&pReport->pSystemGroupFirst, pSystemGroup) ;
  804.          }
  805.       }
  806.    else
  807.       {
  808.       // get rid of this counter's memory
  809.       MemoryFree (pCounterGroup) ;
  810.       }
  811.  
  812.    if (pRetCounterGroup && pNewItemType)
  813.       {
  814.       *pNewItemType = REPORT_TYPE_COUNTER ;
  815.       }
  816.    return (pRetCounterGroup) ;
  817.    }  // CounterRemoveItem
  818.  
  819.  
  820. // GetNextCounter is used to get:
  821. // If the current system is not empty, then get the 
  822. //    (next object first counter) or
  823. //    (previous object last counter. )
  824. // If the current system is empty, then get the 
  825. //    (next system first object first counter) or 
  826. //    (previous system last object last counter)
  827. // Note - Any of the input pointers could be NULL pointer.
  828. PCOUNTERGROUP GetNextCounter (PSYSTEMGROUP   pSystemGroup,
  829.                               POBJECTGROUP   pObjectGroup,
  830.                               PCOUNTERGROUP  pCounterGroup)
  831.    {
  832.    PCOUNTERGROUP  pRetCounter = NULL ;
  833.    PCOUNTERGROUP  pCounterGrp ;
  834.    POBJECTGROUP   pObjectGrp ;
  835.  
  836.    if (pCounterGroup && pCounterGroup->pCounterGroupNext)
  837.       {
  838.       pRetCounter = pCounterGroup->pCounterGroupNext ;
  839.       }
  840.    else if (pCounterGroup && pCounterGroup->pCounterGroupPrevious)
  841.       {
  842.       pRetCounter = pCounterGroup->pCounterGroupPrevious ;
  843.       }
  844.    else if (pObjectGroup && pObjectGroup->pObjectGroupNext)
  845.       {
  846.       // get the next Object first Counter
  847.       pRetCounter = pObjectGroup->pObjectGroupNext->pCounterGroupFirst ;
  848.       }
  849.    else if (pObjectGroup && pObjectGroup->pObjectGroupPrevious)
  850.       {
  851.       // get the previous object last counter
  852.       pCounterGrp = (pObjectGroup->pObjectGroupPrevious)->pCounterGroupFirst ;
  853.       if (pCounterGrp)
  854.          {
  855.          // get the last counter group of this object
  856.          for (;
  857.               pCounterGrp->pCounterGroupNext ;
  858.               pCounterGrp = pCounterGrp->pCounterGroupNext )
  859.             {
  860.             ;
  861.             }
  862.          }
  863.       pRetCounter = pCounterGrp ;
  864.  
  865.       }
  866.    else if (pSystemGroup && pSystemGroup->pSystemGroupNext)
  867.       {
  868.       // get next system first object first counter
  869.       pObjectGrp = pSystemGroup->pSystemGroupNext->pObjectGroupFirst ;
  870.       pRetCounter = pObjectGrp->pCounterGroupFirst ;
  871.       }
  872.    else if (pSystemGroup && pSystemGroup->pSystemGroupPrevious)
  873.       {
  874.       // get previous system last object last counter
  875.       pObjectGrp = pSystemGroup->pSystemGroupPrevious->pObjectGroupFirst ;
  876.       if (pObjectGrp)
  877.          {
  878.          // get the last object group of this system
  879.          for (;
  880.               pObjectGrp->pObjectGroupNext ;
  881.               pObjectGrp = pObjectGrp->pObjectGroupNext )
  882.             {
  883.             ;
  884.             }
  885.          }
  886.       
  887.       if (pObjectGrp)
  888.          {
  889.          pCounterGrp = pObjectGrp->pCounterGroupFirst ;
  890.          if (pCounterGrp)
  891.             {
  892.             // get the last counter group of this object
  893.             for (;
  894.                  pCounterGrp->pCounterGroupNext ;
  895.                  pCounterGrp = pCounterGrp->pCounterGroupNext )
  896.                {
  897.                ;
  898.                }
  899.             }
  900.          pRetCounter = pCounterGrp ;
  901.          }
  902.       }
  903.  
  904.    return (pRetCounter) ;
  905.  
  906.    }  // GetNextCounter
  907.  
  908.  
  909. //======================================//
  910. // Object Group routines                //
  911. //======================================//
  912.  
  913. void ReportObjectRect (PREPORT        pReport,
  914.                        POBJECTGROUP   pObjectGroup,
  915.                        LPRECT         lpRect)
  916.    {  // ReportObjectRect
  917.    lpRect->left = xObjectMargin ;
  918.    lpRect->top = pObjectGroup->yFirstLine ;
  919.    lpRect->right = lpRect->left + pObjectGroup->xWidth ;
  920.    lpRect->bottom = lpRect->top + pReport->yLineHeight ;
  921.    }  // ReportObjectRect
  922.  
  923.  
  924. POBJECTGROUP ObjectGroupCreate (LPTSTR lpszObjectName)
  925.    {  // ObjectGroupCreate
  926.    POBJECTGROUP   pObjectGroup ;
  927.    HDC            hDC ;
  928.    PREPORT        pReport ;
  929.    int            OldCounterWidth ;
  930.    TCHAR          szLine [LongTextLen] ;
  931.  
  932.    pObjectGroup = MemoryAllocate (sizeof (OBJECTGROUP)) ;
  933.  
  934.    if (pObjectGroup)
  935.       {
  936.       pObjectGroup->pObjectGroupNext = NULL ;
  937.       pObjectGroup->pCounterGroupFirst = NULL ;
  938.       pObjectGroup->pColumnGroupFirst = NULL ;
  939.       pObjectGroup->lpszObjectName = StringAllocate (lpszObjectName) ;
  940.  
  941.       hDC = GetDC (hWndReport) ;
  942.       pReport = ReportData (hWndReport) ;
  943.       SelectFont (hDC, pReport->hFontHeaders) ;
  944.  
  945.       TSPRINTF (szLine, szObjectFormat, lpszObjectName) ;
  946.       pObjectGroup->xWidth = TextWidth (hDC, szLine) ;
  947.  
  948.       // re-calc. the max. counter group width
  949.       OldCounterWidth = pReport->xMaxCounterWidth ;
  950.       pReport->xMaxCounterWidth =
  951.             max (pReport->xMaxCounterWidth,
  952.                  pObjectGroup->xWidth + xObjectMargin) ;
  953.  
  954.       if (OldCounterWidth < pReport->xMaxCounterWidth)
  955.           {
  956.           // adjust the report width with the new counter width
  957.           pReport->xWidth +=
  958.                (pReport->xMaxCounterWidth - OldCounterWidth);
  959.           }
  960.  
  961.       ReleaseDC (hWndReport, hDC) ;
  962.       }  // if
  963.    return (pObjectGroup) ;
  964.    }  // ObjectGroupCreate
  965.  
  966.  
  967.  
  968. POBJECTGROUP GetObjectGroup (PSYSTEMGROUP pSystemGroup,
  969.                           LPTSTR lpszObjectName)
  970.    {
  971.    POBJECTGROUP   pObjectGroup ;
  972.  
  973.    if (!pSystemGroup)
  974.       return (FALSE) ;
  975.  
  976.    if (!pSystemGroup->pObjectGroupFirst)
  977.       {
  978.       pSystemGroup->pObjectGroupFirst = ObjectGroupCreate (lpszObjectName) ;
  979.       if (pSystemGroup->pObjectGroupFirst)
  980.          {
  981.          pSystemGroup->pObjectGroupFirst->pParentSystem =
  982.             pSystemGroup ;
  983.          }
  984.       return (pSystemGroup->pObjectGroupFirst) ;
  985.       }
  986.  
  987.    for (pObjectGroup = pSystemGroup->pObjectGroupFirst ;
  988.         pObjectGroup ;
  989.         pObjectGroup = pObjectGroup->pObjectGroupNext)
  990.       {  // for
  991.       if (strsame (pObjectGroup->lpszObjectName, lpszObjectName))
  992.          {
  993.          return (pObjectGroup) ;
  994.          }
  995.       else if (!pObjectGroup->pObjectGroupNext)
  996.          {  // if
  997.          pObjectGroup->pObjectGroupNext = 
  998.             ObjectGroupCreate (lpszObjectName) ;
  999.  
  1000.          if (pObjectGroup->pObjectGroupNext)
  1001.             {
  1002.             (pObjectGroup->pObjectGroupNext)->pParentSystem =
  1003.                pSystemGroup ;
  1004.             (pObjectGroup->pObjectGroupNext)->pObjectGroupPrevious =
  1005.                pObjectGroup ;
  1006.             }
  1007.  
  1008.          return (pObjectGroup->pObjectGroupNext) ;
  1009.          }  // if
  1010.       }  // for
  1011.    }  // GetObjectGroup
  1012.  
  1013. // ObjectGroupRemove removes the specified Object group
  1014. // from the Object double link list
  1015. BOOL ObjectGroupRemove (POBJECTGROUP *ppObjectGroupFirst,
  1016.                         POBJECTGROUP pObjectGroupRemove)
  1017.    {
  1018.    POBJECTGROUP  pObjectGroup ;
  1019.  
  1020.    if (*ppObjectGroupFirst == pObjectGroupRemove)
  1021.       {
  1022.       *ppObjectGroupFirst = (*ppObjectGroupFirst)->pObjectGroupNext ;
  1023.       if (*ppObjectGroupFirst)
  1024.          {
  1025.          // set up head of backward link list
  1026.          (*ppObjectGroupFirst)->pObjectGroupPrevious = NULL ;
  1027.          }
  1028.  
  1029.       // clean up the allocated memory
  1030.       ColumnGroupRemove (pObjectGroupRemove->pColumnGroupFirst) ;
  1031.       MemoryFree (pObjectGroupRemove->lpszObjectName) ;
  1032.       MemoryFree (pObjectGroupRemove) ;
  1033.       return (TRUE) ;
  1034.       }
  1035.  
  1036.    for (pObjectGroup = *ppObjectGroupFirst ;
  1037.         pObjectGroup->pObjectGroupNext ;
  1038.         pObjectGroup = pObjectGroup->pObjectGroupNext)
  1039.       {   // for
  1040.       if (pObjectGroup->pObjectGroupNext == pObjectGroupRemove)
  1041.          {
  1042.          pObjectGroup->pObjectGroupNext = pObjectGroupRemove->pObjectGroupNext ;
  1043.          if (pObjectGroup->pObjectGroupNext)
  1044.             {
  1045.             (pObjectGroup->pObjectGroupNext)->pObjectGroupPrevious =
  1046.                pObjectGroup ;
  1047.             }
  1048.  
  1049.          // clean up this object allocated memory and its column groups
  1050.          ColumnGroupRemove (pObjectGroupRemove->pColumnGroupFirst) ;
  1051.          MemoryFree (pObjectGroupRemove->lpszObjectName) ;
  1052.          MemoryFree (pObjectGroupRemove) ;
  1053.          return (TRUE) ;
  1054.          }  // if
  1055.       }  // for
  1056.  
  1057.    return (FALSE) ;
  1058.    }  // ObjectGroupRemove
  1059.  
  1060.  
  1061. // ObjectRemoveItem is called when user delete the selected object
  1062. PCOUNTERGROUP ObjectRemoveItem (PREPORT      pReport,
  1063.                                 POBJECTGROUP pObjectGroup,
  1064.                                 BOOL         bCleanUpLink,
  1065.                                 enum REPORT_ITEM_TYPE  *pNewItemType)
  1066.    {
  1067.    PCOUNTERGROUP  pCounterGroup, pNextCounterGroup ;
  1068.    PSYSTEMGROUP   pSystemGroup ;
  1069.    PCOUNTERGROUP  pRetCounterGroup = NULL ;
  1070.  
  1071.    pSystemGroup = pObjectGroup->pParentSystem ;
  1072.  
  1073.    // remove all counter groups from this object
  1074.    for (pCounterGroup = pObjectGroup->pCounterGroupFirst ;
  1075.         pCounterGroup ;
  1076.         pCounterGroup = pNextCounterGroup )
  1077.       {
  1078.       pNextCounterGroup = pCounterGroup->pCounterGroupNext ;
  1079.       CounterRemoveItem (pReport, pCounterGroup, FALSE, NULL) ;
  1080.       }
  1081.  
  1082.    // remove all column groups from this group
  1083.    ColumnGroupRemove (pObjectGroup->pColumnGroupFirst) ;
  1084.    
  1085.    if (bCleanUpLink)
  1086.       {
  1087.       
  1088.       // get next counter group to get the focus
  1089.       if (pNewItemType)
  1090.          {
  1091.          pRetCounterGroup = GetNextCounter (
  1092.             pSystemGroup,
  1093.             pObjectGroup,
  1094.             NULL) ;
  1095.  
  1096.          if (pRetCounterGroup)
  1097.             {
  1098.             *pNewItemType = REPORT_TYPE_COUNTER ;
  1099.             }
  1100.          }
  1101.  
  1102.  
  1103.       // remove this object from its parent system group
  1104.       ObjectGroupRemove (&pSystemGroup->pObjectGroupFirst, pObjectGroup) ;
  1105.  
  1106.       if (!(pSystemGroup->pObjectGroupFirst))
  1107.          {
  1108.          SystemGroupRemove (&pReport->pSystemGroupFirst, pSystemGroup) ;
  1109.          }
  1110.       }
  1111.    else
  1112.       {
  1113.       // get rid of this object
  1114.       MemoryFree (pObjectGroup->lpszObjectName) ;
  1115.       MemoryFree (pObjectGroup) ;
  1116.       }
  1117.  
  1118.    return (pRetCounterGroup) ;
  1119.  
  1120.    }  // ObjectRemoveItem
  1121.  
  1122.  
  1123. //======================================//
  1124. // System Group routines                //
  1125. //======================================//
  1126. void ReportSystemRect (PREPORT        pReport,
  1127.                        PSYSTEMGROUP   pSystemGroup,
  1128.                        LPRECT         lpRect)
  1129.    {  // ReportSystemRect
  1130.    lpRect->left = xSystemMargin ;
  1131.    lpRect->top = pSystemGroup->yFirstLine ;
  1132.    lpRect->right = lpRect->left + pSystemGroup->xWidth ;
  1133.    lpRect->bottom = lpRect->top + pReport->yLineHeight ;
  1134.    }  // ReportSystemRect
  1135.  
  1136. PSYSTEMGROUP SystemGroupCreate (LPTSTR lpszSystemName)
  1137.    {  // SystemGroupCreate
  1138.    PSYSTEMGROUP   pSystemGroup ;
  1139.    HDC            hDC ;
  1140.    PREPORT        pReport ;
  1141.    TCHAR          szLine [LongTextLen] ;
  1142.  
  1143.    pSystemGroup = MemoryAllocate (sizeof (SYSTEMGROUP)) ;
  1144.  
  1145.    if (pSystemGroup)
  1146.       {
  1147.       pSystemGroup->pSystemGroupNext = NULL ;
  1148.       pSystemGroup->pObjectGroupFirst = NULL ;
  1149.       pSystemGroup->lpszSystemName = StringAllocate (lpszSystemName) ;
  1150.  
  1151.       // get width of system name
  1152.       hDC = GetDC (hWndReport) ;
  1153.       pReport = ReportData (hWndReport) ;
  1154.       SelectFont (hDC, pReport->hFontHeaders) ;
  1155.  
  1156.       TSPRINTF (szLine, szSystemFormat, lpszSystemName) ;
  1157.       pSystemGroup->xWidth = TextWidth (hDC, szLine) ;
  1158.       ReleaseDC (hWndReport, hDC) ;
  1159.       }  // if
  1160.  
  1161.    return (pSystemGroup) ;
  1162.    }  // SystemGroupCreate
  1163.  
  1164. PSYSTEMGROUP GetSystemGroup (PREPORT pReport,
  1165.                           LPTSTR lpszSystemName)
  1166. /*
  1167.    Effect;        Return a pointer to the system group of pReport with
  1168.                   a system name of lpszSystemName. If no system group
  1169.                   has that name, add a new system group.
  1170. */
  1171.    {  // GetSystemGroup
  1172.    PSYSTEMGROUP   pSystemGroup ;
  1173.  
  1174.    if (!pReport->pSystemGroupFirst)
  1175.       {
  1176.       pReport->pSystemGroupFirst = SystemGroupCreate (lpszSystemName) ;
  1177.       return (pReport->pSystemGroupFirst) ;
  1178.       }
  1179.  
  1180.    for (pSystemGroup = pReport->pSystemGroupFirst ;
  1181.         pSystemGroup ;
  1182.         pSystemGroup = pSystemGroup->pSystemGroupNext)
  1183.       {  // for
  1184.       if (strsamei (pSystemGroup->lpszSystemName, lpszSystemName))
  1185.          return (pSystemGroup) ;
  1186.       else if (!pSystemGroup->pSystemGroupNext)
  1187.          {  // if
  1188.          pSystemGroup->pSystemGroupNext = 
  1189.             SystemGroupCreate (lpszSystemName) ;
  1190.          if (pSystemGroup->pSystemGroupNext)
  1191.             {
  1192.             (pSystemGroup->pSystemGroupNext)->pSystemGroupPrevious =
  1193.                pSystemGroup ;
  1194.             }
  1195.          return (pSystemGroup->pSystemGroupNext) ;
  1196.          }  // if
  1197.       }  // for
  1198.    }  // GetSystemGroup
  1199.  
  1200.  
  1201. BOOL SystemGroupRemove (PSYSTEMGROUP *ppSystemGroupFirst,
  1202.                         PSYSTEMGROUP pSystemGroupRemove)
  1203.    {
  1204.    PSYSTEMGROUP  pSystemGroup ;
  1205.  
  1206.    if (*ppSystemGroupFirst == pSystemGroupRemove)
  1207.       {
  1208.       *ppSystemGroupFirst = (*ppSystemGroupFirst)->pSystemGroupNext ;
  1209.       if (*ppSystemGroupFirst)
  1210.          {
  1211.          (*ppSystemGroupFirst)->pSystemGroupPrevious = NULL ;
  1212.          }
  1213.       MemoryFree (pSystemGroupRemove->lpszSystemName) ;
  1214.       MemoryFree (pSystemGroupRemove) ;
  1215.       return (TRUE) ;
  1216.       }
  1217.  
  1218.    for (pSystemGroup = *ppSystemGroupFirst ;
  1219.         pSystemGroup->pSystemGroupNext ;
  1220.         pSystemGroup = pSystemGroup->pSystemGroupNext)
  1221.       {   // for
  1222.       if (pSystemGroup->pSystemGroupNext == pSystemGroupRemove)
  1223.          {
  1224.          pSystemGroup->pSystemGroupNext = pSystemGroupRemove->pSystemGroupNext ;
  1225.          if (pSystemGroup->pSystemGroupNext)
  1226.             {
  1227.             (pSystemGroup->pSystemGroupNext)->pSystemGroupPrevious =
  1228.                pSystemGroup ;
  1229.             }
  1230.          MemoryFree (pSystemGroupRemove->lpszSystemName) ;
  1231.          MemoryFree (pSystemGroupRemove) ;
  1232.          return (TRUE) ;
  1233.          }  // if
  1234.       }  // for
  1235.  
  1236.    return (FALSE) ;
  1237.    }  // SystemGroupRemove
  1238.  
  1239.  
  1240. // SystemRemoveItem is called when user deletes the selected System
  1241. PCOUNTERGROUP SystemRemoveItem (PREPORT      pReport,
  1242.                                 PSYSTEMGROUP pSystemGroup,
  1243.                                 BOOL         bCleanUpLink,
  1244.                                 enum REPORT_ITEM_TYPE  *pNewItemType)
  1245.    {
  1246.    POBJECTGROUP   pObjectGroup, pNextObjectGroup ;
  1247.    PCOUNTERGROUP  pRetCounterGroup = NULL ;
  1248.    
  1249.    // remove all object groups from this system      
  1250.    for (pObjectGroup = pSystemGroup->pObjectGroupFirst ;
  1251.         pObjectGroup ;
  1252.         pObjectGroup = pNextObjectGroup )
  1253.       {
  1254.       pNextObjectGroup = pObjectGroup->pObjectGroupNext ;
  1255.       ObjectRemoveItem (pReport, pObjectGroup, FALSE, NULL) ;
  1256.       }
  1257.  
  1258.  
  1259.    if (bCleanUpLink)
  1260.       {
  1261.       if (pNewItemType)
  1262.          {
  1263.          pRetCounterGroup = GetNextCounter (
  1264.             pSystemGroup,
  1265.             NULL,
  1266.             NULL) ;
  1267.  
  1268.          if (pRetCounterGroup)
  1269.             {
  1270.             *pNewItemType = REPORT_TYPE_COUNTER ;
  1271.             }
  1272.          }
  1273.  
  1274.       SystemGroupRemove (&pReport->pSystemGroupFirst, pSystemGroup) ;
  1275.       }
  1276.    else
  1277.       {
  1278.       // delete data from this system
  1279.       MemoryFree (pSystemGroup->lpszSystemName) ;
  1280.       MemoryFree (pSystemGroup) ;
  1281.       }
  1282.  
  1283.    return (pRetCounterGroup) ;
  1284.    
  1285.    }  // SystemRemoveItem
  1286.  
  1287.                       
  1288. BOOL  ReportChangeFocus (HWND                   hWnd,
  1289.                          PREPORT                pReport,
  1290.                          REPORT_ITEM            SelectedItem,
  1291.                          enum REPORT_ITEM_TYPE  SelectedItemType,
  1292.                          int                    xOffset,
  1293.                          int                    yOffset,
  1294.                          RECT                   *pRect)
  1295.    {
  1296.    HDC         hDC ;
  1297.    BOOL        RetCode = FALSE ; // FALSE ==> same item being hit
  1298.    RECT        Rect ;
  1299.    REPORT_ITEM            PreviousItem ;
  1300.    enum REPORT_ITEM_TYPE  PreviousItemType ;
  1301.    
  1302.    if (pReport->CurrentItem.pLine != SelectedItem.pLine)
  1303.       {
  1304.       // not the same item
  1305.       RetCode = TRUE ;
  1306.  
  1307.       PreviousItemType = pReport->CurrentItemType ;
  1308.       PreviousItem.pLine = pReport->CurrentItem.pLine ;
  1309.  
  1310.       pReport->CurrentItemType = SelectedItemType ;
  1311.       pReport->CurrentItem.pLine = SelectedItem.pLine ;
  1312.  
  1313.       hDC = GetDC (hWnd) ;
  1314.  
  1315.       if (SelectedItemType == REPORT_TYPE_LINE)
  1316.          {
  1317.          SetWindowOrgEx (hDC, xOffset, yOffset, NULL) ;
  1318.          SelectFont (hDC, pReport->hFont) ;
  1319.          SetTextAlign (hDC, TA_RIGHT) ;
  1320.          SetBkColor (hDC, GetSysColor(COLOR_WINDOW)) ;
  1321.          DrawReportValue (hDC, pReport, SelectedItem.pLine) ;
  1322.          SetWindowOrgEx (hDC, -xOffset, -yOffset, NULL) ;
  1323.          }
  1324.       else
  1325.          {
  1326.          Rect = *pRect ;
  1327.          Rect.top -= yOffset ;
  1328.          Rect.bottom -= yOffset ;
  1329.          Rect.right -= xOffset ;
  1330.          Rect.left -= xOffset ;
  1331.          InvalidateRect (hWnd, &Rect, TRUE) ;
  1332.          }
  1333.  
  1334.       if (PreviousItemType == REPORT_TYPE_LINE)
  1335.          {
  1336.          SetWindowOrgEx (hDC, xOffset, yOffset, NULL) ;
  1337.          SelectFont (hDC, pReport->hFont) ;
  1338.          SetTextAlign (hDC, TA_RIGHT) ;
  1339.          SetBkColor (hDC, GetSysColor(COLOR_WINDOW)) ;
  1340.          DrawReportValue (hDC, pReport, PreviousItem.pLine) ;
  1341.          }
  1342.       else if (PreviousItemType != REPORT_TYPE_NOTHING)
  1343.          {
  1344.          if (PreviousItemType == REPORT_TYPE_SYSTEM)
  1345.             {
  1346.             ReportSystemRect (pReport, PreviousItem.pSystem, &Rect) ;
  1347.             }
  1348.          else if (PreviousItemType == REPORT_TYPE_OBJECT)
  1349.             {
  1350.             ReportObjectRect (pReport, PreviousItem.pObject, &Rect) ;
  1351.             }
  1352.          else if (PreviousItemType == REPORT_TYPE_COUNTER)
  1353.             {
  1354.             ReportCounterRect (pReport, PreviousItem.pCounter, &Rect) ;
  1355.             }
  1356.          else if (PreviousItemType == REPORT_TYPE_COLUMN)
  1357.             {
  1358.             ReportColumnRect (pReport, PreviousItem.pColumn, &Rect) ;
  1359.             }
  1360.          Rect.top -= yOffset ;
  1361.          Rect.bottom -= yOffset ;
  1362.          Rect.right -= xOffset ;
  1363.          Rect.left -= xOffset ;
  1364.          InvalidateRect (hWnd, &Rect, TRUE) ;
  1365.          }
  1366.       ReleaseDC (hWnd, hDC) ;
  1367.       }
  1368.  
  1369.    return (RetCode) ;
  1370.    }  // ReportChangeFocus
  1371.  
  1372.  
  1373. BOOL  OnReportLButtonDown (HWND hWnd, 
  1374.                            WORD xPos,
  1375.                            WORD yPos)
  1376.    {
  1377.    PREPORT     pReport ;
  1378.    PLINE       pLine ;
  1379.    REPORT_ITEM PreviousItem ;
  1380.    REPORT_ITEM CurrentSelectedItem ;
  1381.    enum REPORT_ITEM_TYPE PreviousItemType ;
  1382.    RECT        rect ;
  1383.    POINT       pt ;
  1384.    int         xOffset, yOffset ;
  1385.    PSYSTEMGROUP   pSystemGroup ;
  1386.    POBJECTGROUP   pObjectGroup ;
  1387.    PCOUNTERGROUP  pCounterGroup ;
  1388.    PCOLUMNGROUP   pColumnGroup ;
  1389.    
  1390.  
  1391.    pReport = ReportData (hWnd) ;
  1392.    if (!pReport)
  1393.       return (FALSE) ;
  1394.  
  1395.    xOffset = GetScrollPos (hWnd, SB_HORZ) ;
  1396.    yOffset = GetScrollPos (hWnd, SB_VERT) ;
  1397.    pt.x = xPos + xOffset ;
  1398.    pt.y = yPos + yOffset ;
  1399.    PreviousItem = pReport->CurrentItem ;
  1400.    PreviousItemType = pReport->CurrentItemType ;
  1401.  
  1402.    for (pLine = pReport->pLineFirst ;
  1403.         pLine ;
  1404.         pLine = pLine->pLineNext)
  1405.       {  // for
  1406.       ReportLineValueRect (pReport, pLine, &rect) ;
  1407.       if (PtInRect (&rect, pt))
  1408.          {
  1409.          CurrentSelectedItem.pLine = pLine ;
  1410.          return (ReportChangeFocus (
  1411.             hWnd,
  1412.             pReport,
  1413.             CurrentSelectedItem,
  1414.             REPORT_TYPE_LINE,
  1415.             xOffset,
  1416.             yOffset,
  1417.             &rect)) ;
  1418.          }
  1419.       }  // for
  1420.  
  1421.    // check on hit on system, object, counter, column (parent+isntance names)
  1422.    for (pSystemGroup = pReport->pSystemGroupFirst ;
  1423.         pSystemGroup ;
  1424.         pSystemGroup = pSystemGroup->pSystemGroupNext)
  1425.       {  // for System...
  1426.  
  1427.       ReportSystemRect (pReport, pSystemGroup, &rect) ;
  1428.       if (PtInRect (&rect, pt))
  1429.          {
  1430.          CurrentSelectedItem.pSystem = pSystemGroup ;
  1431.          return (ReportChangeFocus (
  1432.             hWnd,
  1433.             pReport,
  1434.             CurrentSelectedItem,
  1435.             REPORT_TYPE_SYSTEM,
  1436.             xOffset,
  1437.             yOffset,
  1438.             &rect)) ;
  1439.          }
  1440.  
  1441.  
  1442.       for (pObjectGroup = pSystemGroup->pObjectGroupFirst ;
  1443.            pObjectGroup ;
  1444.            pObjectGroup = pObjectGroup->pObjectGroupNext)
  1445.          {  // for Object...
  1446.  
  1447.          ReportObjectRect (pReport, pObjectGroup, &rect) ;
  1448.          if (PtInRect (&rect, pt))
  1449.             {
  1450.             CurrentSelectedItem.pObject = pObjectGroup ;
  1451.             return (ReportChangeFocus (
  1452.                hWnd,
  1453.                pReport,
  1454.                CurrentSelectedItem,
  1455.                REPORT_TYPE_OBJECT,
  1456.                xOffset,
  1457.                yOffset,
  1458.                &rect)) ;
  1459.             }
  1460.  
  1461.          for (pColumnGroup = pObjectGroup->pColumnGroupFirst ;
  1462.               pColumnGroup ;
  1463.               pColumnGroup = pColumnGroup->pColumnGroupNext)
  1464.             {  // for Column...
  1465.             ReportColumnRect (pReport, pColumnGroup, &rect) ;
  1466.             if (PtInRect (&rect, pt))
  1467.                {
  1468.                CurrentSelectedItem.pColumn = pColumnGroup ;
  1469.                return (ReportChangeFocus (
  1470.                   hWnd,
  1471.                   pReport,
  1472.                   CurrentSelectedItem,
  1473.                   REPORT_TYPE_COLUMN,
  1474.                   xOffset,
  1475.                   yOffset,
  1476.                   &rect)) ;
  1477.                }
  1478.             }  // for Column
  1479.  
  1480.          for (pCounterGroup = pObjectGroup->pCounterGroupFirst ;
  1481.               pCounterGroup ;
  1482.               pCounterGroup = pCounterGroup->pCounterGroupNext)
  1483.             {  // for Counter...
  1484.             ReportCounterRect (pReport, pCounterGroup, &rect) ;
  1485.             if (PtInRect (&rect, pt))
  1486.                {
  1487.                CurrentSelectedItem.pCounter = pCounterGroup ;
  1488.                return (ReportChangeFocus (
  1489.                   hWnd,
  1490.                   pReport,
  1491.                   CurrentSelectedItem,
  1492.                   REPORT_TYPE_COUNTER,
  1493.                   xOffset,
  1494.                   yOffset,
  1495.                   &rect)) ;
  1496.  
  1497.                }
  1498.             }  // for Counter...
  1499.          }  // for Object...
  1500.       }  // for System...
  1501.  
  1502.    // nothing hit
  1503.    return (FALSE) ;
  1504.    }  // OnReportLButtonDown
  1505.  
  1506. BOOL ReportDeleteItem (HWND hWnd)
  1507. /*
  1508.    Effect:        Delete the current selected item.
  1509.  
  1510. */
  1511.    {  // ReportDeleteItem
  1512.  
  1513.    HDC                     hDC ;
  1514.    PREPORT                 pReport ;
  1515.    REPORT_ITEM             NextItem ;
  1516.    enum  REPORT_ITEM_TYPE  NextItemType ;
  1517.  
  1518.    NextItemType = REPORT_TYPE_NOTHING ;
  1519.    NextItem.pLine = NULL ;
  1520.  
  1521.    pReport = ReportData (hWnd) ;
  1522.    if (pReport->CurrentItemType == REPORT_TYPE_NOTHING)
  1523.       {
  1524.       // nothing to delete...
  1525.       return (TRUE) ;
  1526.       }
  1527.    else if (pReport->CurrentItemType == REPORT_TYPE_LINE)
  1528.       {
  1529.       NextItem.pLine = LineRemoveItem (pReport, &NextItemType) ;
  1530.       }
  1531.    else if (pReport->CurrentItemType == REPORT_TYPE_SYSTEM)
  1532.       {
  1533.       NextItem.pCounter = SystemRemoveItem (
  1534.             pReport,
  1535.             pReport->CurrentItem.pSystem,
  1536.             TRUE,
  1537.             &NextItemType) ;
  1538.       }
  1539.    else if (pReport->CurrentItemType == REPORT_TYPE_OBJECT)
  1540.       {
  1541.       NextItem.pCounter = ObjectRemoveItem (
  1542.             pReport,
  1543.             pReport->CurrentItem.pObject,
  1544.             TRUE,
  1545.             &NextItemType) ;
  1546.       }
  1547.    else if (pReport->CurrentItemType == REPORT_TYPE_COUNTER)
  1548.       {
  1549.       NextItem.pCounter = CounterRemoveItem (
  1550.             pReport,
  1551.             pReport->CurrentItem.pCounter,
  1552.             TRUE,
  1553.             &NextItemType) ;
  1554.       }
  1555.    else if (pReport->CurrentItemType == REPORT_TYPE_COLUMN)
  1556.       {
  1557.       NextItem.pColumn = ColumnRemoveItem (
  1558.             pReport,
  1559.             pReport->CurrentItem.pColumn,
  1560.             TRUE,
  1561.             &NextItemType) ;
  1562.       }
  1563.    
  1564.    if (NextItemType != REPORT_TYPE_NOTHING)
  1565.       {
  1566.       pReport->CurrentItem.pLine = NextItem.pLine ;
  1567.       pReport->CurrentItemType = NextItemType ;
  1568.       }
  1569.    else
  1570.       {
  1571.       pReport->CurrentItem.pLine = pReport->pLineFirst ;
  1572.       pReport->CurrentItemType = REPORT_TYPE_LINE ;
  1573.       }
  1574.  
  1575.    if (pReport->pLineFirst)
  1576.       {
  1577.       BuildValueListForSystems (
  1578.          pReport->pSystemFirst,
  1579.          pReport->pLineFirst) ;
  1580.       }
  1581.    else
  1582.       {
  1583.       // no more line, no more timer...
  1584.       pReport->xWidth = 0 ;
  1585.       pReport->yHeight = 0 ;
  1586.       pReport->xMaxCounterWidth = 0 ;
  1587.       ClearReportTimer (pReport) ;
  1588.  
  1589.       FreeSystems (pReport->pSystemFirst) ;
  1590.       pReport->pSystemFirst = NULL ;
  1591.       pReport->pSystemGroupFirst = NULL ;
  1592.       pReport->CurrentItemType = REPORT_TYPE_NOTHING ;
  1593.       pReport->CurrentItem.pLine = NULL ;
  1594.  
  1595.       }
  1596.  
  1597.    //=============================//
  1598.    // Calculate report positions  //
  1599.    //=============================//
  1600.    
  1601.    hDC = GetDC (hWnd) ;
  1602.    SetReportPositions (hDC, pReport) ;
  1603.  
  1604.    if (!pReport->pLineFirst)
  1605.       {
  1606.       SelectFont (hDC, pReport->hFont) ;
  1607.       pReport->xValueWidth = TextWidth (hDC, szValuePlaceholder) ;
  1608.       }
  1609.  
  1610.    ReleaseDC (hWnd, hDC) ;
  1611.    WindowInvalidate (hWnd) ;
  1612.  
  1613.    return (TRUE) ;
  1614.    }  // ReportDeleteItem
  1615.  
  1616.