RPTFCT.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:51k
源码类别:

Windows编程

开发平台:

Visual C++

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