//==========================================================================//
//                                  Includes                                //
//==========================================================================//



#include <stdio.h>
#include <math.h>

#include "perfmon.h"
#include "addline.h"

#include "alert.h"      // for AlertInsertLine, AlertDeleteLine
#include "grafdata.h"   // for ChartInsertLine, ChartDeleteLine
#include "graph.h"      // for SizeGraphComponents
#include "legend.h"     // for LegendAddItem
#include "line.h"       // for LineAllocate, LineFree.
#include "pmemory.h"    // for MemoryXXX (mallloc-type) routines
#include "perfdata.h"   // for QueryPerformanceData
#include "perfmops.h"   // for dlg_error_box
#include "playback.h"   // for PlaybackLines
#include "report.h"     // for ReportInsertLine, ReportDeleteLine
#include "system.h"     // for SystemGet
#include "utils.h"
#include "playback.h"   // for PlayingBackLog
#include "counters.h"
#include "pmhelpid.h"   // Help IDs

//==========================================================================//
//                                  Constants                               //
//==========================================================================//


#ifdef ADVANCED_PERFMON
#define ADDLINEDETAILLEVEL    PERF_DETAIL_WIZARD
#else
#define ADDLINEDETAILLEVEL    PERF_DETAIL_NOVICE
#endif

#define iInitialExplainLen    256

// defines used in owner-drawn items
#define OWNER_DRAWN_ITEM      2
#define OWNER_DRAW_FOCUS      1

//==========================================================================//
//                                Local Data                                //
//==========================================================================//

// defined in PerfData.c
extern WCHAR   NULL_NAME[] ;


COLORREF argbColors[] = 
   {
   RGB (0xff, 0x00, 0x00), 
   RGB (0x00, 0x80, 0x00), 
   RGB (0x00, 0x00, 0xff), 
   RGB (0xff, 0xff, 0x00), 
   RGB (0xff, 0x00, 0xff), 
   RGB (0x00, 0xff, 0xff), 
   RGB (0x80, 0x00, 0x00), 
   RGB (0x40, 0x40, 0x40), 
   RGB (0x00, 0x00, 0x80), 
   RGB (0x80, 0x80, 0x00), 
   RGB (0x80, 0x00, 0x80), 
   RGB (0x00, 0x80, 0x80), 
   RGB (0x40, 0x00, 0x00), 
   RGB (0x00, 0x40, 0x00), 
   RGB (0x00, 0x00, 0x40), 
   RGB (0x00, 0x00, 0x00)
   } ;


TCHAR *apszScaleFmt[] =
   {
   TEXT("%6.6f"),
   TEXT("%5.5f"),
   TEXT("%4.4f"),
   TEXT("%3.3f"),
   TEXT("%2.2f"),
   TEXT("%1.1f"),
   TEXT("%2.1f"),
   TEXT("%3.1f"),
   TEXT("%4.1f"),
   TEXT("%5.1f"),
   TEXT("%6.1f"),
   TEXT("%7.1f")
   } ;
#define DEFAULT_SCALE 0
#define NUMBER_OF_SCALE sizeof(apszScaleFmt)/sizeof(apszScaleFmt[0])

int               iLineType ;
static PPERFDATA  pPerfData ;
PPERFSYSTEM       pSystem ;
PLINESTRUCT       pLineEdit ;
PPERFSYSTEM       *ppSystemFirst ;
PLINEVISUAL       pVisual ;

#define bEditLine (pLineEdit != NULL)


BOOL              ComputerChange ;

//=============================//
// Alert-related locals        //
//=============================//


BOOL           bAlertOver ;         // over or under?
FLOAT          eAlertValue ;        // value to compare
BOOL           bEveryTime ;         // run every time or once?

// program to run; 
LPTSTR          pszAlertProgram ;  


//==========================================================================//
//                                   Macros                                 //
//==========================================================================//


#define InChartAdd()             \
   (iLineType == LineTypeChart)


#define InAlertAdd()             \
   (iLineType == LineTypeAlert)

#define InReportAdd()            \
   (iLineType == LineTypeReport)




#define NumColorIndexes()     \
   (sizeof (argbColors) / sizeof (argbColors[0]))

#define NumWidthIndexes()  5

#define NumStyleIndexes()  4


//==========================================================================//
//                              Forward Declarations                        //
//==========================================================================//

BOOL /*static*/ OnObjectChanged (HDLG hDlg) ;


//==========================================================================//
//                              Local Functions                             //
//==========================================================================//


PPERFINSTANCEDEF ParentInstance (PPERFINSTANCEDEF pInstance)
   {  // ParentInstance
   PPERFOBJECT          parent_obj ;
   PPERFINSTANCEDEF     parent_instance ;
   PERF_COUNTER_BLOCK   *counter_blk;
   LONG                 i ;

   parent_obj = 
      GetObjectDefByTitleIndex (pPerfData, 
                                pInstance->ParentObjectTitleIndex) ;
   if (!parent_obj)
      return (NULL) ;


   // Then get the parent instance.
   // NOTE: can use unique ID field to match here instead
   // of name compare.
   for (i = 0, 
        parent_instance = (PERF_INSTANCE_DEFINITION *) ( (PBYTE)parent_obj
        + parent_obj->DefinitionLength);
        i < parent_obj->NumInstances;
        i++, parent_instance = (PERF_INSTANCE_DEFINITION *) ( (PBYTE)counter_blk
        + counter_blk->ByteLength))
      {  // for
      counter_blk = (PERF_COUNTER_BLOCK *) ( (PBYTE)parent_instance
                   + parent_instance->ByteLength);
     if ((DWORD)i == pInstance->ParentObjectInstance)
        return (parent_instance) ;
     }

   return (NULL) ;
   }  // ParentInstance


PPERFOBJECT SelectedObject (HWND hWndObjects, 
                            LPTSTR lpszObjectName)
/*
   Effect:        Return the pObject associated with the currently selected
                  combo-box item of hWndObjects. Set lpszObjectName to 
                  the object's name.

                  If no item is selected in the combobox, return NULL.

   Assert:        The pObject for each CB item was added when the string
                  was added to the CB, by CBLoadObjects.

   See Also:      LoadObjects.
*/
   {  // SelectedObject
   int            iIndex ;

   iIndex = CBSelection (hWndObjects) ;
   if (iIndex == CB_ERR)
      return (NULL) ;

   if (lpszObjectName)
      CBString (hWndObjects, iIndex, lpszObjectName) ;

   return ((PPERFOBJECT) CBData (hWndObjects, iIndex)) ;
   }  // SelectedObject



PPERFCOUNTERDEF SelectedCounter (HWND hWndCounters, 
                                 LPTSTR lpszCounterName)
/*
   Effect:        Return the pCounter associated with the currently selected
                  LB item of hWndCounters. Set lpszCounterName to 
                  the Counter's name.

                  If no item is selected in the listbox, return NULL.

   Assert:        The pCounter for each LB item was added when the string
                  was added to the LB, by LoadCounters.

   See Also:      LoadCounters.
*/
   {  // SelectedCounter
   int            iIndex ;

   iIndex = LBSelection (hWndCounters) ;
   if (iIndex == LB_ERR)
      return (NULL) ;

   if (lpszCounterName)
      LBString (hWndCounters, iIndex, lpszCounterName) ;
   return ((PPERFCOUNTERDEF) LBData (hWndCounters, iIndex)) ;
   }  // SelectedCounter



void VisualIncrement (PLINEVISUAL pVisual)
/*
   Effect:        Cycle through the combinations of color, width, and
                  style to distinguish between lines.  The color attributes
                  are like a number:
                        <style> <width> <color>

                  Since color is the LSB, it is always incremented. The
                  others are incremented whenever the color rolls over.

                  If a current index is -1, that means don't increment
                  that visual attribute.
*/
   {  // VisualIncrement
   pVisual->iColorIndex = 
      (pVisual->iColorIndex + 1) % NumColorIndexes () ;

   if (pVisual->iColorIndex)
      return ;


   if (pVisual->iWidthIndex == -1)
      return ;


   pVisual->iWidthIndex =
      (pVisual->iWidthIndex + 1) % NumWidthIndexes () ;

   if (pVisual->iWidthIndex)
       return ;


   if (pVisual->iStyleIndex == -1)
      return ;


   pVisual->iStyleIndex = 
      (pVisual->iStyleIndex + 1) % NumStyleIndexes () ;
   }  // VisualIncrement


COLORREF LineColor (int iColorIndex)
   {
   return (argbColors [iColorIndex]) ;
   }


int LineWidth (int iWidthIndex)
   {
   switch (iWidthIndex)
      {  // switch
      case 0:
          return (1) ;
          break ;

      case 1:
          return (3) ;
          break ;

      case 2:
          return (5) ;
          break ;

      case 3:
          return (7) ;
          break ;

      case 4:
          return (9) ;
          break ;
      }  // switch
   }  // LineWidth



int LineStyle (int iStyleIndex)
   {
   return (iStyleIndex) ;
   }


BOOL /*static*/ LoadInstances (HDLG hDlg)
   {
   PPERFOBJECT       pObject ;
   PPERFINSTANCEDEF  pInstance, pInstanceParent ;
   TCHAR             szInstance [256], szInstanceParent [256] ;
   TCHAR             szCompositeName [256] ;
   TCHAR             szInstCompositeName [256] ;
                           
   LONG              iInstance ;
   UINT              iIndex ;

   int               xTextExtent = 0 ;
   int               currentTextExtent ;
   HFONT             hFont ;
   HDC               hDC = 0 ;
   HWND              hWndObjects = DialogControl (hDlg, IDD_ADDLINEOBJECT);
   HWND              hWndInstances = DialogControl (hDlg, IDD_ADDLINEINSTANCE);

   // turn off horiz. scrollbar
   LBSetHorzExtent (hWndInstances, 0) ;
   LBReset (hWndInstances) ;

   pObject = SelectedObject (hWndObjects, NULL) ;
   if (!pObject)
      return (FALSE) ;

   if (pObject->NumInstances <= 0)
      {
      MLBSetSelection (hWndInstances, 0, TRUE) ;
      return (FALSE) ;
      }

   // turn off Listbox redraw
   LBSetRedraw (hWndInstances, FALSE) ;

   if (bEditLine)
      {
      if (pLineEdit->lnObject.NumInstances > 0)
         {
         if (pLineEdit->lnInstanceDef.ParentObjectTitleIndex)
            {
            // Get the Parent Object Instance Name.
            // and prefix it to the Instance Name, to make
            // the string we want to display.
            TSPRINTF (szInstCompositeName,
                     TEXT("%s ==> %s"),
                     pLineEdit->lnPINName,
                     pLineEdit->lnInstanceName) ;
            }
         else
            {
            lstrcpy (szInstCompositeName, pLineEdit->lnInstanceName) ;
            }
         }
      else
         {
         szInstCompositeName[0] = TEXT('\0');
         }
      }

   if (!bEditLine && (hDC = GetDC (hWndInstances)))
      {
      hFont = (HFONT)SendMessage(hWndInstances, WM_GETFONT, 0, 0L);
      if (hFont)
         SelectObject(hDC, hFont);
      }


   for (iInstance = 0, pInstance = FirstInstance (pObject) ;
        iInstance < pObject->NumInstances; 
        iInstance++, pInstance = NextInstance (pInstance))
      {  // for
      GetInstanceName (pInstance, szInstance) ;
      pInstanceParent = ParentInstance (pInstance) ;
    
      if (pInstanceParent)
         {
         GetInstanceName (pInstanceParent, szInstanceParent) ;
         TSPRINTF (szCompositeName, TEXT("%s ==> %s"), 
                   szInstanceParent, szInstance) ;
         }
     else
        lstrcpy (szCompositeName, szInstance) ;

  	iIndex = LBAdd (hWndInstances, szCompositeName) ;
   if (iIndex != LB_ERR)
      {
      LBSetData (hWndInstances, iIndex, (LPARAM) pInstance) ;
      }
 
   // get the biggest text width
   if (hDC)
      {
      currentTextExtent = TextWidth (hDC, szCompositeName) + xScrollWidth / 2  ;
      if (currentTextExtent > xTextExtent)
         {
         xTextExtent = currentTextExtent ;
         }
      }
   }  // for

   if (hDC)
      {
      // turn on horiz. scrollbar if necessary...
      LBSetHorzExtent (hWndInstances, xTextExtent) ;
      ReleaseDC (hWndInstances, hDC) ;
      }


   if (!bEditLine || szInstCompositeName[0] == TEXT('\0'))
      {
      MLBSetSelection (hWndInstances, 0, TRUE) ;
      }
   else
      {
      BOOL bSetSelection = TRUE ;

      iIndex = LBFind (hWndInstances, szInstCompositeName) ;
      if (iIndex == LB_ERR)
         {
         if (bEditLine)
            {
            bSetSelection = FALSE ;
            }
         iIndex = 0 ;
         }

      if (bSetSelection)
         {
         MLBSetSelection (hWndInstances, iIndex, TRUE) ;
         }

      LBSetVisible (hWndInstances, iIndex) ;
      }

   // turn on Listbox redraw
   LBSetRedraw (hWndInstances, TRUE) ;  

   }  // LoadInstances


BOOL OnCounterChanged (HDLG hDlg)
/*
   Effect:        Perform any actions necessary when the counter has changed.
                  In particular, display the explanation for the counter
                  that has the focus rectangle.
*/
   {
   LPTSTR         lpszText ;
   PPERFCOUNTERDEF pCounter ;
   int            iStatus ;
   int            iFocusIndex ;
   HWND           hWndCounters = DialogControl (hDlg, IDD_ADDLINECOUNTER);
   HWND           hWndScales = DialogControl (hDlg, IDD_ADDLINESCALE) ;

   iFocusIndex = LBFocus (hWndCounters) ;
   if  (iFocusIndex == LB_ERR)
      return (FALSE) ;

   if (PlayingBackLog())
      {
      DialogEnable (hDlg, IDD_ADDLINEEXPANDEXPLAIN, FALSE) ;   
      return (TRUE) ;
      }

   pCounter = (PPERFCOUNTERDEF) LBData (hWndCounters, iFocusIndex) ;
   if ((!pCounter) || (pCounter == (PPERFCOUNTERDEF)LB_ERR))
      return (FALSE) ;
   
   // no need to get help text before the button is clicked
   if (!bExplainTextButtonHit)
      return (FALSE) ;

   // Create initial string
   lpszText = MemoryAllocate (iInitialExplainLen * sizeof (TCHAR)) ;

   while (TRUE)
      {
      lpszText[0] = TEXT('\0') ;

      #ifdef UNICODE
      iStatus = QueryPerformanceName  (pSystem,
                                       pCounter->CounterHelpTitleIndex,
                                       iLanguage,
                                       MemorySize (lpszText) / sizeof(TCHAR),
                                       lpszText,
                                       TRUE) ;
      #else
      iStatus = QueryPerformanceNameW (pSystem,
                                       pCounter->CounterHelpTitleIndex,
                                       iLanguage,
                                       MemorySize (lpszText),
                                       lpszText,
                                       TRUE) ;
      #endif

      if (iStatus == ERROR_SUCCESS)
         break ;

      if (iStatus == ERROR_MORE_DATA)
         lpszText = 
            MemoryResize (lpszText, 
                          MemorySize (lpszText) + iInitialExplainLen) ;
      else
         break ;
      }  // while

   SetDlgItemText (hDlg, IDD_ADDLINEEXPLAIN, lpszText) ;
   MemoryFree (lpszText) ;

   return (TRUE) ;
   }  // OnCounterChanged



BOOL LoadCounters (HDLG hDlg, 
                   UINT iSelectCounterDefn)
   {  // LoadCounters
   PPERFOBJECT       pObject ;

   TCHAR             szCounterName [256] ;
   TCHAR             szDefaultCounterName [256] ;
   PPERFCOUNTERDEF   pCounter ;
   UINT              i ;
   int               iIndex ;
   int               xTextExtent = 0 ;
   int               currentTextExtent ;
   HFONT             hFont ;
   HDC               hDC = 0 ;
   BOOL              bSetSelection = TRUE ;
   HWND              hWndObjects = DialogControl (hDlg, IDD_ADDLINEOBJECT);
   HWND              hWndCounters = DialogControl (hDlg, IDD_ADDLINECOUNTER);


   strclr (szDefaultCounterName) ;

   // turn off horiz. scrollbar
   LBSetHorzExtent (hWndCounters, 0) ;
   LBReset (hWndCounters) ;

   pObject = SelectedObject (hWndObjects, NULL) ;
   if (!pObject)
      return (FALSE) ;

   if (!bEditLine && (hDC = GetDC (hWndCounters)))
      {
      hFont = (HFONT)SendMessage(hWndCounters, WM_GETFONT, 0, 0L);
      if (hFont)
         SelectObject(hDC, hFont);
      }

   // turn off Listbox redraw
   LBSetRedraw (hWndCounters, FALSE) ;  

   for (i = 0, pCounter = FirstCounter (pObject) ;
        i < pObject->NumCounters ;
        i++, pCounter = NextCounter (pCounter))
      {  // for
      if (pCounter->CounterType != PERF_SAMPLE_BASE &&
          pCounter->CounterType != PERF_COUNTER_NODATA &&
          pCounter->CounterType != PERF_AVERAGE_BASE &&
          pCounter->CounterType != PERF_COUNTER_QUEUELEN_TYPE &&
          pCounter->CounterType != PERF_COUNTER_MULTI_BASE &&
          pCounter->CounterType != PERF_RAW_BASE &&
          pCounter->DetailLevel <= ADDLINEDETAILLEVEL)
         {   // if
         szCounterName[0] = TEXT('\0') ;
         QueryPerformanceName (pSystem, 
                                 pCounter->CounterNameTitleIndex, 
                                 0, sizeof (szCounterName) / sizeof(TCHAR),
                                 szCounterName,
                                 FALSE) ;
         
         // if szCounterName is not empty, add it to the listbox
         if (!strsame(szCounterName, NULL_NAME))
            {
            iIndex = LBAdd (hWndCounters, szCounterName) ;
            LBSetData (hWndCounters, iIndex, (DWORD) pCounter) ;

            // get the biggest text width
            if (hDC)
               {
               currentTextExtent = TextWidth (hDC, szCounterName) + xScrollWidth / 2 ;
               if (currentTextExtent > xTextExtent)
                  {
                  xTextExtent = currentTextExtent ;
                  }
               }
       
            if (iSelectCounterDefn == i)
               lstrcpy (szDefaultCounterName, szCounterName) ;
            } // if szCounterName is not empty
         }   // if
      }  // for

   if (bEditLine)
      lstrcpy (szDefaultCounterName, pLineEdit->lnCounterName) ;

   iIndex = LBFind (hWndCounters, szDefaultCounterName) ;
   if (iIndex == LB_ERR)
      {
      if (bEditLine)
         {
         bSetSelection = FALSE ;
         }
      iIndex = 0 ;
      }

   if (bSetSelection)
      {
      MLBSetSelection (hWndCounters, iIndex, TRUE) ;
      }
   LBSetVisible (hWndCounters, iIndex) ;
   
   if (hDC)
      {
      // turn on horiz. scrollbar if necessary...
      LBSetHorzExtent (hWndCounters, xTextExtent) ;
      ReleaseDC (hWndCounters, hDC) ;
      }

   // turn on Listbox redraw
   LBSetRedraw (hWndCounters, TRUE) ;  

   OnCounterChanged (hDlg) ;
   }  // LoadCounters




void LoadObjects (HDLG hDlg,
                  PPERFDATA pPerfData)
/*
   Effect:        Load into the object CB the objects for the current
                  pPerfData.
*/
   {
   LPTSTR         lpszObject ;
   HWND           hWndObjects = DialogControl (hDlg, IDD_ADDLINEOBJECT);


   lpszObject = bEditLine ? pLineEdit->lnObjectName : NULL ;

   CBLoadObjects (hWndObjects, 
                  pPerfData,
                  pSystem,
                  ADDLINEDETAILLEVEL,
                  lpszObject,
                  FALSE) ;
   OnObjectChanged (hDlg) ;
//   UpdateWindow (hDlg) ;
   }  // LoadObjects



void OnComputerChanged (HDLG hDlg)
   {

   PPERFSYSTEM pLocalSystem;
   PPERFDATA   pLocalPerfData;

   pLocalPerfData = pPerfData;
   pLocalSystem = GetComputer (hDlg,
                          IDD_ADDLINECOMPUTER,
                          TRUE,
                          &pLocalPerfData,
                          ppSystemFirst) ;
   if (pLocalSystem && pLocalPerfData)
      {
      pSystem = pLocalSystem;
      pPerfData = pLocalPerfData;
      LoadObjects (hDlg, pPerfData) ;
      ComputerChange = FALSE ;
      DialogEnable (hDlg, IDD_ADDLINEADD, TRUE) ;
      }

   }  // OnComputerChanged


   

BOOL AddOneChartLine (HWND hDlg,
                      PPERFCOUNTERDEF pCounter,
                      LPTSTR lpszCounter,
                      PPERFINSTANCEDEF pInstance)                     
   {
   TCHAR             szComputer [MAX_SYSTEM_NAME_LENGTH] ;

   PPERFOBJECT       pObject ;
   TCHAR             szObject [PerfObjectLen] ;

   TCHAR             szInstance [256] ;

   PLINE             pLine ;
//   int               i ;
   int               iCounterIndex ;
   int               j ;

   PPERFINSTANCEDEF  pInstanceParent ;
   PERF_COUNTER_BLOCK *pCounterBlock ;
   TCHAR          szInstanceParent [256] ;
   TCHAR          szObjectParent [PerfObjectLen+1] ;
   HWND           hWndColors = DialogControl (hDlg, IDD_ADDLINECOLOR) ;
   HWND           hWndWidths = DialogControl (hDlg, IDD_ADDLINEWIDTH) ;
   HWND           hWndStyles = DialogControl (hDlg, IDD_ADDLINESTYLE) ;
   HWND           hWndScales = DialogControl (hDlg, IDD_ADDLINESCALE) ;
   HWND           hWndObjects = DialogControl (hDlg, IDD_ADDLINEOBJECT);


   //=============================//
   // Get selected data values    //
   //=============================//



   DialogText (hDlg, IDD_ADDLINECOMPUTER, szComputer) ;

   pObject = SelectedObject (hWndObjects, szObject) ;
   if (!pObject)
      return (FALSE) ;

   if (pInstance)
      GetInstanceName (pInstance, szInstance) ;

   //=============================//
   // Allocate the line           //
   //=============================//

   pLine = LineAllocate () ;
   if (!pLine)
      {
      DlgErrorBox (hDlg, ERR_NO_MEMORY);
      return (FALSE) ;
      }


   //=============================//
   // Set line's data values      //
   //=============================//

   pLine->iLineType = iLineType ;
   pLine->lnSystemName = StringAllocate (szComputer) ;

   pLine->lnObject = *pObject ;
   pLine->lnObjectName = StringAllocate (szObject) ;

   pLine->lnCounterDef = *pCounter ;
   pLine->lnCounterName = StringAllocate (lpszCounter) ;


   if (pObject->NumInstances > 0 && pInstance)
      {
      pLine->lnInstanceDef = *pInstance ;          
      pLine->lnInstanceName = StringAllocate (szInstance) ;

      pLine->lnUniqueID = pInstance->UniqueID ;

      if (pInstance->ParentObjectTitleIndex)
         {
         szObjectParent[0] = (TCHAR)'\0';
         QueryPerformanceName (pSystem,
                               pInstance->ParentObjectTitleIndex,
                               0,  PerfObjectLen, szObjectParent, FALSE) ;
         pLine->lnParentObjName = StringAllocate (szObjectParent) ;
         }

      pInstanceParent = ParentInstance (pInstance) ;
      if (pInstanceParent)
         {
         GetInstanceName (pInstanceParent, szInstanceParent) ;
         if (pInstance->ParentObjectTitleIndex)
            {
            pLine->lnPINName = StringAllocate (szInstanceParent) ;
            }
         }
      }  // if

   pLine->lnCounterType = pCounter->CounterType;
   pLine->lnCounterLength = pCounter->CounterSize;

   pLine->lnOldTime = pPerfData->PerfTime ;
   pLine->lnNewTime = pPerfData->PerfTime ;

   for (j = 0 ; j < 2 ; j++)
      {
      pLine->lnaCounterValue[j].LowPart = 0 ;
      pLine->lnaCounterValue[j].HighPart = 0 ;
      }


   //=============================//
   // Chart-related Values        //
   //=============================//

   pLine->iScaleIndex = CBSelection (hWndScales) ;
   if (pLine->iScaleIndex == 0)
      {
      // use the default scale
      pLine->eScale = (FLOAT) pow ((double)10.0,
          (double)pCounter->DefaultScale) ;
      }
   else
      {
      pLine->eScale = DialogFloat (hDlg, IDD_ADDLINESCALE, NULL) ;
      }

   if (pObject->NumInstances > 0 && pInstance)
      {
      pCounterBlock = (PERF_COUNTER_BLOCK *) ( (PBYTE) pInstance +
                       pInstance->ByteLength);
      }
   else
      {
      pCounterBlock = (PERF_COUNTER_BLOCK *) ( (PBYTE) pObject +
                       pObject->DefinitionLength);
      }

   if (pLine->lnCounterLength <= 4)
       pLine->lnaOldCounterValue[0].LowPart =
               * ( (DWORD FAR *) ( (PBYTE)pCounterBlock +
               pCounter[0].CounterOffset));
   else
      {
      pLine->lnaOldCounterValue[0] =
              * ( (LARGE_INTEGER *) ( (PBYTE)pCounterBlock +
              pCounter[0].CounterOffset));
      }

   // Get second counter, only if we are not at
   // the end of the counters; some computations
   // require a second counter

   iCounterIndex = CounterIndex (pCounter, pObject) ;
   if ((UINT) iCounterIndex < pObject->NumCounters - 1 &&
       iCounterIndex != -1)
      {
      if (pLine->lnCounterLength <= 4)
          pLine->lnaOldCounterValue[1].LowPart =
                  * ( (DWORD FAR *) ( (PBYTE)pCounterBlock +
                  pCounter[1].CounterOffset));
      else
         pLine->lnaOldCounterValue[1] =
                 * ( (LARGE_INTEGER *) ( (PBYTE)pCounterBlock +
                 pCounter[1].CounterOffset));
      }

//   pLine->valNext = CounterFuncEntry;
   pLine->valNext = CounterEntry;

   pLine->lnaOldCounterValue[0] = pLine->lnaCounterValue[0];
   pLine->lnaOldCounterValue[1] = pLine->lnaCounterValue[1];

   //=============================//
   // Visual Values               //
   //=============================//

   pLine->Visual.iColorIndex = CBSelection (hWndColors) ;
   pLine->Visual.crColor = LineColor (pLine->Visual.iColorIndex) ;

   pLine->Visual.iWidthIndex = CBSelection (hWndWidths) ;
   pLine->Visual.iWidth = LineWidth (pLine->Visual.iWidthIndex) ;

   pLine->Visual.iStyleIndex = CBSelection (hWndStyles) ;
   pLine->Visual.iStyle = LineStyle (pLine->Visual.iStyleIndex) ;

   *pVisual = pLine->Visual ;
   if (!bEditLine)
      VisualIncrement (pVisual) ;

   CBSetSelection (hWndColors, pVisual->iColorIndex) ;
   CBSetSelection (hWndWidths, pVisual->iWidthIndex) ;
   CBSetSelection (hWndStyles, pVisual->iStyleIndex) ;

   if (iLineType == LineTypeChart)
      {
      pLine->hPen = LineCreatePen (NULL, &(pLine->Visual), FALSE) ;
      }

   //=============================//
   // Alert Related Values        //
   //=============================//

   if (iLineType == LineTypeAlert)
      {
      pLine->bAlertOver = bAlertOver ;
      pLine->eAlertValue = eAlertValue ;
      pLine->bEveryTime = 
         IsDlgButtonChecked (hDlg, IDD_ADDLINEPROGRAMEVERYTIME) ;
      pLine->bAlerted = FALSE ;
      pLine->hBrush = CreateSolidBrush (pLine->Visual.crColor) ;
      if (!PlayingBackLog ())
         {
         pLine->lpszAlertProgram = StringAllocate (pszAlertProgram) ;
         }
      else
         {
         pLine->lpszAlertProgram = NULL ;
         }
      }
   //=============================//
   // Insert the line!            //
   //=============================//

   if (InsertLine (pLine) == FALSE)
      {
      // no inert occurred due to either line already existed
      // or error detected.
      LineFree (pLine) ;
      }
   else
      {
      if (pSystem->lpszValue)
         {
         if (strsame(pSystem->lpszValue, TEXT("Global ")))
            {
            // take out the "Global " string
            *(pSystem->lpszValue) = 0 ;
            }
         AppendObjectToValueList (
            pLine->lnObject.ObjectNameTitleIndex,
            pSystem->lpszValue) ;
         }
      }
   }  // AddOneChartLine


BOOL AddCounter (HWND hDlg,
                      PPERFCOUNTERDEF pCounter,
                      LPTSTR lpszCounter)
   {  // AddCounter
   int               iInstanceIndex ;
   int               iInstanceNum ;
   PPERFINSTANCEDEF  pInstance ;
   HWND              hWndInstances = DialogControl (hDlg, IDD_ADDLINEINSTANCE);

   // NOTE: for now, we don't check for duplicate lines
   if (!IsCounterSupported (pCounter->CounterType))
      {
      DlgErrorBox (hDlg, ERR_COUNTER_NOT_IMP);
      return (FALSE) ;
      }


   if ((iInstanceNum = LBNumItems (hWndInstances)) && iInstanceNum != LB_ERR)
      {

      if (iInstanceNum > 1)
         {
         // delay some of the insert actions for performacne improvement
         bDelayAddAction = TRUE ;

         if (InChartAdd())
            {
            LegendSetRedraw (hWndGraphLegend, FALSE) ;
            }
         else if (InAlertAdd())
            {
            LegendSetRedraw (hWndAlertLegend, FALSE) ;
            }
         }

      for (iInstanceIndex = 0 ;
           iInstanceIndex < iInstanceNum ;
           iInstanceIndex++)
         {  // for
         if (LBSelected (hWndInstances, iInstanceIndex))
            {  // if
            pInstance = (PPERFINSTANCEDEF) LBData (hWndInstances, iInstanceIndex) ;
            if (pInstance == (PPERFINSTANCEDEF) LB_ERR)
               {
               pInstance = NULL;
               }
            AddOneChartLine (hDlg, pCounter, lpszCounter, pInstance) ;
            }  // if
         }  // for
      
      if (bDelayAddAction)
         {
         // now do the post add-line actions
         bDelayAddAction = FALSE ;
         if (InReportAdd())
            {
            PREPORT pReport ;
      
            pReport = ReportData (hWndReport) ;
            ReportAddAction (pReport) ;
            }
         else if (InChartAdd())
            {
            GraphAddAction () ;
            LegendSetRedraw (hWndGraphLegend, TRUE) ;
            }
         else if (InAlertAdd())
            {
            AlertAddAction () ;
            LegendSetRedraw (hWndAlertLegend, TRUE) ;
            }
         }
      }
   else
      {
      pInstance = NULL;
      AddOneChartLine (hDlg, pCounter, lpszCounter, pInstance) ;
      }

   return (TRUE) ;
   }  // AddCounter


//==========================================================================//
//                              Message Handlers                            //
//==========================================================================//


BOOL /*static*/ OnInitDialog (HWND hDlg)
   {
   int            i ;
   FLOAT          ScaleFactor ;
   TCHAR          tempBuff[ShortTextLen] ;
   TCHAR          szCaption [WindowCaptionLen] ;
   TCHAR          szRemoteComputerName[MAX_COMPUTERNAME_LENGTH + 3] ;
   HWND           hWndComputer = DialogControl (hDlg, IDD_ADDLINECOMPUTER);
   HWND           hWndObjects = DialogControl (hDlg, IDD_ADDLINEOBJECT);
   HWND           hWndInstances = DialogControl (hDlg, IDD_ADDLINEINSTANCE);
   HWND           hWndCounters = DialogControl (hDlg, IDD_ADDLINECOUNTER);
   HWND           hWndColors = DialogControl (hDlg, IDD_ADDLINECOLOR) ;
   HWND           hWndWidths = DialogControl (hDlg, IDD_ADDLINEWIDTH) ;
   HWND           hWndStyles = DialogControl (hDlg, IDD_ADDLINESTYLE) ;
   HWND           hWndScales = DialogControl (hDlg, IDD_ADDLINESCALE) ;

   // this is used to tell UPdateLines not to mark any
   // system as not used
   bAddLineInProgress = TRUE ;
   
   // turn this off until the Explain text button is clicked
   bExplainTextButtonHit = FALSE ;

   if (InAlertAdd())
      {
      pszAlertProgram = (LPTSTR) MemoryAllocate (FilePathLen * sizeof (TCHAR)) ;
      }

   if (!PlayingBackLog ())
      pPerfData = MemoryAllocate (STARTING_SYSINFO_SIZE) ;

   pSystem = NULL ;

   if (!bEditLine && PlayingBackLog())
      {
      pPerfData = DataFromIndexPosition (&(PlaybackLog.StartIndexPos), NULL) ;
      GetPerfComputerName(pPerfData, szRemoteComputerName);
      DialogSetString (hDlg, IDD_ADDLINECOMPUTER, szRemoteComputerName);
      }
   else
      {
      DialogSetString (hDlg, IDD_ADDLINECOMPUTER,
                       bEditLine ? pLineEdit->lnSystemName : LocalComputerName) ;
      }

   OnComputerChanged (hDlg) ;

   //=============================//
   // Set default line values     //
   //=============================//

   bAlertOver = bEditLine ? pLineEdit->bAlertOver : TRUE ;
   bEveryTime = bEditLine ? pLineEdit->bEveryTime : TRUE ;

   //=============================//
   // Fill line attribute CBs     //
   //=============================//

   // Load the colors combobox, select the default color.
   for (i = 0 ; i < NumColorIndexes () ; i++)
       CBAdd (hWndColors, i) ;
   CBSetSelection (hWndColors, pVisual->iColorIndex) ;

   // Load the widths combobox, select the default width.
   for (i = 0 ; i < NumWidthIndexes () ; i++)
      CBAdd (hWndWidths, i) ;
   CBSetSelection (hWndWidths, pVisual->iWidthIndex) ;

   // Load the styles combobox, select the default style.
   for (i = 0 ; i < NumStyleIndexes () ; i++)
      CBAdd (hWndStyles, i) ;
   CBSetSelection (hWndStyles, pVisual->iStyleIndex) ;


   // Init the scale combo box.

   StringLoad (IDS_DEFAULT, tempBuff) ;
   CBAdd (hWndScales, tempBuff) ;

   // we are formatting the scale factors during run-time so
   // the c-runtime library will pick up the default locale
   // decimal "charatcer".
   ScaleFactor = (FLOAT)0.000001 ;
   for (i = 0 ; i < NUMBER_OF_SCALE ; i++)
      {
      TSPRINTF(tempBuff, apszScaleFmt[i], ScaleFactor) ;
      ScaleFactor *= (FLOAT) 10.0 ;
      CBAdd (hWndScales, tempBuff) ;
      }

   CBSetSelection (hWndScales, bEditLine ? pLineEdit->iScaleIndex : DEFAULT_SCALE) ;

   
   CheckRadioButton (hDlg, IDD_ADDLINEIFOVER, IDD_ADDLINEIFUNDER, 
                     bAlertOver ? IDD_ADDLINEIFOVER: IDD_ADDLINEIFUNDER) ;
   CheckRadioButton (hDlg, IDD_ADDLINEPROGRAMFIRSTTIME, IDD_ADDLINEPROGRAMEVERYTIME, 
                     bEveryTime ? IDD_ADDLINEPROGRAMEVERYTIME: IDD_ADDLINEPROGRAMFIRSTTIME) ;

   if (bEditLine)
      {
      DialogSetText (hDlg, IDD_ADDLINEADD, IDS_OK) ;
      DialogSetFloat (hDlg, IDD_ADDLINEIFVALUE, pLineEdit->eAlertValue) ;
      if (pLineEdit->lpszAlertProgram)
         DialogSetString (hDlg, IDD_ADDLINEPROGRAM, 
                          pLineEdit->lpszAlertProgram) ;

      DialogEnable (hDlg, IDD_ADDLINECOMPUTERTEXT, FALSE) ;
      DialogEnable (hDlg, IDD_ADDLINECOMPUTER, FALSE) ;
      DialogEnable (hDlg, IDD_ADDLINEOBJECTTEXT, FALSE) ;   
      DialogEnable (hDlg, IDD_ADDLINEOBJECT, FALSE) ;   
      DialogEnable (hDlg, IDD_ADDLINECOUNTERTEXT, FALSE) ;   
      DialogEnable (hDlg, IDD_ADDLINECOUNTER, FALSE) ;   
      DialogEnable (hDlg, IDD_ADDLINEINSTANCE, FALSE) ;   
      DialogEnable (hDlg, IDD_ADDLINEINSTANCETEXT, FALSE) ;   
      }
   else
      {
      // set the scroll limit on the edit box
      EditSetLimit (GetDlgItem(hDlg, IDD_CHOOSECOMPUTERNAME),
         MAX_SYSTEM_NAME_LENGTH-1) ;

      }

   if (PlayingBackLog())
      {
      DialogEnable (hDlg, IDD_ADDLINEEXPANDEXPLAIN, FALSE) ;   
      }

   //=============================//
   // LineType specific init      //
   //=============================//

   switch (iLineType)
      {
      case LineTypeChart:
         dwCurrentDlgID = bEditLine ?
                  HC_PM_idDlgEditChartLine : HC_PM_idDlgEditAddToChart ;

         StringLoad (bEditLine ? 
                     IDS_EDITCHART : IDS_ADDTOCHART, szCaption) ;

         DialogShow (hDlg, IDD_ADDLINEIFGROUP, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEIFVALUE, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEIFUNDER, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEIFOVER, FALSE) ;

         DialogShow (hDlg, IDD_ADDLINEPROGRAMGROUP, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEPROGRAM, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEPROGRAMFIRSTTIME, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEPROGRAMEVERYTIME, FALSE) ;
         break ;

      case LineTypeAlert:
         dwCurrentDlgID = bEditLine ?
                  HC_PM_idDlgEditAlertEntry : HC_PM_idDlgEditAddToAlert ;

         StringLoad (bEditLine ? 
                     IDS_EDITALERT : IDS_ADDTOALERT, szCaption) ;

         DialogShow (hDlg, IDD_ADDLINESCALE, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINESCALETEXT, FALSE) ;

         DialogShow (hDlg, IDD_ADDLINEWIDTH, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEWIDTHTEXT, FALSE) ;

         DialogShow (hDlg, IDD_ADDLINESTYLE, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINESTYLETEXT, FALSE) ;

         if (PlayingBackLog ())
            {
            DialogEnable (hDlg, IDD_ADDLINEPROGRAM, FALSE) ;
            DialogEnable (hDlg, IDD_ADDLINEPROGRAMGROUP, FALSE) ;
            DialogEnable (hDlg, IDD_ADDLINEPROGRAMEVERYTIME, FALSE) ;
            DialogEnable (hDlg, IDD_ADDLINEPROGRAMFIRSTTIME, FALSE) ;
            }

         // set the scroll limit on the edit boxes

         EditSetLimit (GetDlgItem(hDlg, IDD_ADDLINEIFVALUE), ShortTextLen) ;
         EditSetLimit (GetDlgItem(hDlg, IDD_ADDLINEPROGRAM), FilePathLen-1) ;

         break ;

      case LineTypeReport:

         dwCurrentDlgID = HC_PM_idDlgEditAddToReport ;

         StringLoad (bEditLine ? 
                     IDS_EDITREPORT : IDS_ADDTOREPORT, szCaption) ;

         DialogShow (hDlg, IDD_ADDLINEIFGROUP, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEIFVALUE, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEIFUNDER, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEIFOVER, FALSE) ;

         DialogShow (hDlg, IDD_ADDLINEPROGRAMGROUP, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEPROGRAM, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEPROGRAMFIRSTTIME, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEPROGRAMEVERYTIME, FALSE) ;

         DialogShow (hDlg, IDD_ADDLINECOLOR, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINECOLORTEXT, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINESCALE, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINESCALETEXT, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEWIDTH, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINEWIDTHTEXT, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINESTYLE, FALSE) ;
         DialogShow (hDlg, IDD_ADDLINESTYLETEXT, FALSE) ;
         break ;
      }  // switch

   SetWindowText (hDlg, szCaption) ;
   SendDlgItemMessage (hDlg, 
                       IDD_ADDLINEEXPLAIN, WM_SETFONT, 
                       (WPARAM) hFontScales, (LPARAM) FALSE) ;
   WindowCenter (hDlg) ;
   return (TRUE) ;
   }  // OnInitDialog


BOOL /*static*/ OnObjectChanged (HDLG hDlg)
/*
   Effect:        Perform any actions necessary when the user has selected
                  a new object category from the object CB, or when a default
                  object is first selected into the dialog. In particular,
                  find and load the counters, instances, etc., for this
                  object.

   Called by:     OnInitDialog, AddLineDlgProc (in response to an 
                  IDM_ADDLINEOBJECT notification).
*/
   {  // OnObjectChanged
   PPERFOBJECT    pObject ;
   HWND           hWndInstances = DialogControl (hDlg, IDD_ADDLINEINSTANCE);
   HWND           hWndCounters = DialogControl (hDlg, IDD_ADDLINECOUNTER);
   HWND           hWndObjects = DialogControl (hDlg, IDD_ADDLINEOBJECT);

   LBReset (hWndInstances) ;
   LBReset (hWndCounters) ;

   pObject = SelectedObject (hWndObjects, NULL) ;
   if (!pObject)
      return (FALSE) ;

   LoadCounters (hDlg, (UINT)pObject->DefaultCounter) ;
   LoadInstances (hDlg) ;  

   }  // OnObjectChanged



BOOL LineModifyAttributes (HWND hDlg, PLINE pLineToModify)
{
   LINEVISUAL  LineVisual ;
   HPEN        hLinePen ;
   int         iScaleIndex ;        // chart attribute
   FLOAT	      eScale ;             // chart attribute

   BOOL        bLocalAlertOver ;         // alert attribute - over or under?
   FLOAT       eLocalAlertValue ;        // alert attribute - value to compare
   LPTSTR      lpLocalszAlertProgram ;   // alert attribute - program to run
   BOOL        bLocalEveryTime ;         // alert attribute - run every time or once?
   BOOL        bLocalAlerted ;           // alert attribute - alert happened on line?

   HPEN        hTempPen ;
   LPTSTR      lpTempAlertProgram ;
   HWND        hWndColors = DialogControl (hDlg, IDD_ADDLINECOLOR) ;
   HWND        hWndWidths = DialogControl (hDlg, IDD_ADDLINEWIDTH) ;
   HWND        hWndStyles = DialogControl (hDlg, IDD_ADDLINESTYLE) ;
   HWND        hWndScales = DialogControl (hDlg, IDD_ADDLINESCALE) ;
   
   //=============================//
   // Visual Values               //
   //=============================//

   LineVisual.iColorIndex = CBSelection (hWndColors) ;
   LineVisual.crColor = LineColor (LineVisual.iColorIndex) ;

   LineVisual.iWidthIndex = CBSelection (hWndWidths) ;
   LineVisual.iWidth = LineWidth (LineVisual.iWidthIndex) ;

   LineVisual.iStyleIndex = CBSelection (hWndStyles) ;
   LineVisual.iStyle = LineStyle (LineVisual.iStyleIndex) ;

   hLinePen = LineCreatePen (NULL, &(LineVisual), FALSE) ;

   //=============================//
   // Chart-related Values        //
   //=============================//
   
   if (InChartAdd())
      {
      iScaleIndex = CBSelection (hWndScales) ;
      if (iScaleIndex == 0)
          {
          // use the default scale
          eScale = (FLOAT) pow ((double)10.0,
              (double)pLineToModify->lnCounterDef.DefaultScale) ;
          }
      else
          {
          eScale = DialogFloat (hDlg, IDD_ADDLINESCALE, NULL) ;
          }
      }

   //=============================//
   // Alert Related Values        //
   //=============================//
   
   if (InAlertAdd())
      {
      bLocalAlertOver = bAlertOver ;
      eLocalAlertValue = eAlertValue ;
      bLocalEveryTime =
         IsDlgButtonChecked (hDlg, IDD_ADDLINEPROGRAMEVERYTIME) ;
      bLocalAlerted = FALSE ;
      lpLocalszAlertProgram = StringAllocate (pszAlertProgram) ;
      }

   // Just do it..
   pLineToModify->Visual = LineVisual ;
   if (pLineToModify->hPen)
      {
      hTempPen = pLineToModify->hPen ;
      pLineToModify->hPen = hLinePen ;
      DeleteObject (hTempPen) ;
      }

   if (InChartAdd())
      {
      pLineToModify->iScaleIndex = iScaleIndex ;
      pLineToModify->eScale = eScale ;

      }

   if (InAlertAdd())
      {
      pLineToModify->bAlertOver = bLocalAlertOver ;
      pLineToModify->eAlertValue = eLocalAlertValue ;
      pLineToModify->bEveryTime = bLocalEveryTime ;
      pLineToModify->bAlerted = FALSE ;

      lpTempAlertProgram = pLineToModify->lpszAlertProgram ;
      pLineToModify->lpszAlertProgram = lpLocalszAlertProgram ;

      if (lpTempAlertProgram)
         {
         MemoryFree (lpTempAlertProgram) ;
         }
      }

   return (TRUE) ;

} // LineModifyAttributes

BOOL OnAddLines (HWND hDlg)
   {  
   PPERFCOUNTERDEF   pCounter ;
   TCHAR             szCounter [256] ;
   BOOL              bOK ;
   int               iCounter ;
   int               iCounterNum ;
   HWND              hWndCounters = DialogControl (hDlg, IDD_ADDLINECOUNTER);

   if (ComputerChange)
      {
      // if computer has changed, don't want to continue
      // because the perfdata may have changed
      OnComputerChanged (hDlg) ;
      return (TRUE) ;
      }

   //=============================//
   // Dialog Values Acceptable?   //
   //=============================//

   if (InAlertAdd ())
      {
      eAlertValue = DialogFloat (hDlg, IDD_ADDLINEIFVALUE, &bOK) ;
      if (!bOK)
         {
         DlgErrorBox (hDlg, ERR_NEEDALERTVALUE);
         SetFocus (DialogControl (hDlg, IDD_ADDLINEIFVALUE)) ;
         return (FALSE) ;
         }  // if
      }  // if

   if (bEditLine)
      {
      LineModifyAttributes (hDlg, pLineEdit) ;
      EndDialog (hDlg, TRUE) ;
      }

      // If the user changed the textbox for computer name and pressed enter,
      // the OnAddLines function would be called without a check of the 
      // computer name. This solves that problem.
   else
      {

      iCounterNum = LBNumItems (hWndCounters) ;
      for (iCounter = 0 ;
           iCounter < iCounterNum ;
           iCounter++)
         {  // for
         // NOTE: for now, we don't check for duplicate lines
         if (LBSelected (hWndCounters, iCounter))
            {  // if
            pCounter = (PPERFCOUNTERDEF) LBData (hWndCounters, iCounter) ;
            LBString (hWndCounters, iCounter, szCounter) ;

            if (!IsCounterSupported (pCounter->CounterType))
               {
               DlgErrorBox (hDlg, ERR_COUNTER_NOT_IMP);
               }
            else
               {
               AddCounter (hDlg, pCounter, szCounter) ;
               }
            }  // if
         }  // for
      DialogSetText (hDlg, IDCANCEL, IDS_DONE) ;
      } // else (not bEditLine)

   if (InChartAdd ())
      SizeGraphComponents (hWndGraph) ;
   if (InAlertAdd ())
      SizeAlertComponents (hWndAlert) ;

   WindowInvalidate (PerfmonViewWindow ()) ;

   }  // OnAddLines


void OnExpandExplain (HWND hDlg)
/*
   Effect:        Perform actions needed when user clicks on the Explain...
                  button. In particular, expand the dialog size to 
                  uncover the explain edit box, and gray out the button.
*/
   {  // OnExpandExplain
   RECT           rectWindow ;

   // Disable button first
   DialogEnable (hDlg, IDD_ADDLINEEXPANDEXPLAIN, FALSE) ;

   // go get the help text
   bExplainTextButtonHit = TRUE ;
   OnCounterChanged (hDlg) ;

   GetWindowRect (hDlg, &rectWindow) ;
   MoveWindow (hDlg,
               rectWindow.left,
               rectWindow.top,
               rectWindow.right - rectWindow.left,
               rectWindow.bottom - rectWindow.top + 
               DialogHeight (hDlg, IDD_ADDLINEEXPLAINGROUP) + 
               yScrollHeight,
               TRUE) ;
   }  // OnExpandExplain



BOOL /*static*/ OnCommand (HWND hDlg, 
                       WPARAM wParam, 
                       LPARAM lParam)
   {
   int            iWidthIndex ;
   int            iStyleIndex ;
   HWND           hWndWidths = DialogControl (hDlg, IDD_ADDLINEWIDTH) ;
   HWND           hWndStyles = DialogControl (hDlg, IDD_ADDLINESTYLE) ;

   switch (LOWORD (wParam))
      {
      case IDD_ADDLINEIFVALUE:
         // We don't get the value when the user types it. We get it when
         // the user attempts to add the line.
         break ;

      case IDD_ADDLINEPROGRAM:
         GetDlgItemText (hDlg,
            IDD_ADDLINEPROGRAM,
            pszAlertProgram,
            FilePathLen - 1) ;
         break ;

      case IDD_ADDLINEIFOVER:
      case IDD_ADDLINEIFUNDER:
         bAlertOver = !bAlertOver ;
         CheckRadioButton (hDlg, IDD_ADDLINEIFOVER, IDD_ADDLINEIFUNDER, 
                           bAlertOver ? IDD_ADDLINEIFOVER: IDD_ADDLINEIFUNDER) ;
         break ;

      case IDD_ADDLINEPROGRAMFIRSTTIME:
      case IDD_ADDLINEPROGRAMEVERYTIME:
         bEveryTime = !bEveryTime ;
         CheckRadioButton (hDlg, IDD_ADDLINEPROGRAMFIRSTTIME, IDD_ADDLINEPROGRAMEVERYTIME, 
                           bEveryTime ? IDD_ADDLINEPROGRAMEVERYTIME: IDD_ADDLINEPROGRAMFIRSTTIME) ;
         break ;

      case IDD_ADDLINEWIDTH:
         iWidthIndex = CBSelection (hWndWidths) ;
#if (!WIDESTYLES)
         DialogEnable (hDlg, IDD_ADDLINESTYLETEXT, 
                       iWidthIndex == 0  || iWidthIndex == CB_ERR) ;
         DialogEnable (hDlg, IDD_ADDLINESTYLE, 
                       iWidthIndex == 0  || iWidthIndex == CB_ERR) ;
#endif
         break ;

      case IDD_ADDLINESTYLE:
         iStyleIndex = CBSelection (hWndStyles) ;
#if (!WIDESTYLES)
         DialogEnable (hDlg, IDD_ADDLINEWIDTHTEXT, 
                       iStyleIndex == 0  || iStyleIndex == CB_ERR) ;
         DialogEnable (hDlg, IDD_ADDLINEWIDTH, 
                       iStyleIndex == 0  || iStyleIndex == CB_ERR) ;
#endif
         break ;

      case IDCANCEL:
         EndDialog (hDlg, 0);
         return (TRUE);
         break ;

      case IDD_ADDLINEADD :
         SetHourglassCursor() ;
         OnAddLines (hDlg) ;
         SetArrowCursor() ;
         break;

      case IDD_ADDLINEEXPANDEXPLAIN :
         OnExpandExplain (hDlg) ;
         break;

      case IDD_ADDLINECOUNTER:
         if (HIWORD (wParam) == LBN_SELCHANGE)
            OnCounterChanged (hDlg) ;
         break ;
           
      
      case IDD_ADDLINEOBJECT:
         if (HIWORD (wParam) == CBN_SELCHANGE)
            OnObjectChanged (hDlg) ;
         break ;

      case IDD_ADDLINECOMPUTER:
         if (HIWORD (wParam) == EN_UPDATE)
            {
            ComputerChange = TRUE ;
            DialogEnable (hDlg, IDD_ADDLINEADD, FALSE) ;
            }
         else if (HIWORD (wParam) == EN_KILLFOCUS)
            {
            if (ComputerChange)
               {
               SetHourglassCursor() ;
               OnComputerChanged (hDlg) ;
               }
            }
         break ;

      case IDD_ADDLINEHELP:
         CallWinHelp (dwCurrentDlgID) ;
         break ;

		default:
		    break;
	    } // switch

   return (FALSE) ;
   } // OnCommand


void /*static*/ OnMeasureItem (HWND hDlg, 
                           PMEASUREITEMSTRUCT pMI)
   {
   pMI->CtlType    = ODT_COMBOBOX ;
   pMI->CtlID      = IDD_ADDLINECOLOR ;
   pMI->itemData   = 0 ;
   pMI->itemWidth  = 0 ;

   // need 14 in order to draw the thickest line width
   pMI->itemHeight = 14 ;
//   pMI->itemHeight = 12 ;
   }

//***************************************************************************
//                                                                          *
//  FUNCTION   : HandleSelectionState(LPDRAWITEMSTRUCT)                     *
//                                                                          *
//  PURPOSE    : Handles a change in an item selection state. If an item is *
//               selected, a black rectangular frame is drawn around that   *
//               item; if an item is de-selected, the frame is removed.     *
//                                                                          *
//  COMMENT    : The black selection frame is slightly larger than the gray *
//               focus frame so they won't paint over each other.           *
//                                                                          *
//***************************************************************************
void static HandleSelectionState (LPDRAWITEMSTRUCT	lpdis)
{
	HBRUSH	hbr ;

	if (lpdis->itemState & ODS_SELECTED)
	{
		// selecting item -- paint a black frame
		hbr = GetStockObject(BLACK_BRUSH) ;
	}
	else
	{
		// de-selecting item -- remove frame 
		hbr = CreateSolidBrush(GetSysColor(COLOR_WINDOW)) ;
	}
	FrameRect(lpdis->hDC, (LPRECT)&lpdis->rcItem, hbr) ;
	DeleteObject (hbr) ;
   }  // HandleSelectionState

//***************************************************************************
//                                                                          *
//  FUNCTION   : HandleFocusState(LPDRAWITEMSTRUCT)                         *
//                                                                          *
//  PURPOSE    : Handle a change in item focus state. If an item gains the  *
//               input focus, a gray rectangular frame is drawn around that *
//               item; if an item loses the input focus, the gray frame is  *
//               removed.                                                   *
//                                                                          *
//  COMMENT    : The gray focus frame is slightly smaller than the black    *
//               selection frame so they won't paint over each other.       *
//                                                                          *
//***************************************************************************
void static HandleFocusState (LPDRAWITEMSTRUCT	lpdis)
   {
	RECT	   rc ;
	HBRUSH	hbr ;

	// Resize rectangle to place focus frame between the selection
	// frame and the item.
	CopyRect ((LPRECT)&rc, (LPRECT)&lpdis->rcItem) ;
	InflateRect ((LPRECT)&rc, -OWNER_DRAW_FOCUS, -OWNER_DRAW_FOCUS) ;

	if (lpdis->itemState & ODS_FOCUS)
	{
		// gaining input focus -- paint a gray frame
		hbr = GetStockObject(GRAY_BRUSH) ;
	}
	else
	{
		// losing input focus -- remove (paint over) frame
		hbr = CreateSolidBrush(GetSysColor(COLOR_WINDOW)) ;
	}
	FrameRect(lpdis->hDC, (LPRECT)&rc, hbr) ;
	DeleteObject (hbr) ;
   }  // HandleFocusState

void /*static*/ OnDrawItem (HWND hDlg, PDRAWITEMSTRUCT pDI)
   {  // OnDrawItem
   HDC            hDC ;
   PRECT          prect ;
   INT            itemData, 
                  itemID, 
                  CtlID, 
                  itemAction ;
   LOGBRUSH       logBrush ;
   HANDLE         hBrush, 
                  hOldBrush, 
                  hPen, 
                  hOldPen ;
   INT            x1, y1, x2, y2, cy ;
   POINT          point ;
   INT            iPenWidth ;
   COLORREF       BackgroundColor ;

   hDC        = pDI-> hDC ;
   CtlID      = pDI->CtlID ;
   prect      = &pDI->rcItem ;
   itemData   = pDI->itemData ;
   itemID     = pDI->itemID ;
   itemAction = pDI->itemAction ;


   if (itemID == -1)
      {
      // invalid ID, can't go on
      HandleFocusState (pDI) ;
      }
   else if (itemAction == ODA_SELECT)
      {
      HandleSelectionState(pDI);
      }
   else if (itemAction == ODA_FOCUS)
      {
      HandleFocusState (pDI) ;
      }
   else
      {
      
      // draw the entire item
      
      InflateRect (prect, -OWNER_DRAWN_ITEM, -OWNER_DRAWN_ITEM) ;

      switch (CtlID)
         {  // switch
         case IDD_ADDLINECOLOR:

            // Draw a color rectangle into the control area

            logBrush.lbStyle = BS_SOLID ;
            logBrush.lbColor = (COLORREF) argbColors[itemID] ;
            logBrush.lbHatch = 0 ;

            hBrush = CreateBrushIndirect (&logBrush) ;
            hOldBrush = SelectObject (hDC, hBrush) ;

            hPen = GetStockObject (NULL_PEN) ;
            hOldPen = SelectObject (hDC, hPen) ;

            x1 = prect->left ;
            y1 = prect->top ;
            x2 = prect->right ;
            y2 = prect->bottom ;

            Rectangle (hDC, x1, y1, x2, y2) ;

            SelectObject (hDC, hOldBrush) ;
            DeleteObject (hBrush) ;

            InflateRect (prect, OWNER_DRAWN_ITEM, OWNER_DRAWN_ITEM) ;

            HandleSelectionState (pDI) ;
            HandleFocusState (pDI) ;

            break ;

         case IDD_ADDLINEWIDTH:
         case IDD_ADDLINESTYLE:

            // First draw a rectangle, white interior, null border
            hBrush = GetStockObject (WHITE_BRUSH) ;
            hOldBrush = SelectObject (hDC, hBrush) ;

            // we need to set the bk color in order to draw
            // the dash lines coorectly during focus.  Otherwise,
            // the COLOR_WINDOW background will make all dash lines
            // look like solid line...
            BackgroundColor = SetBkColor (hDC, crWhite) ;

            hPen = GetStockObject (NULL_PEN) ;
            hOldPen = SelectObject (hDC, hPen) ;

            x1 = prect->left ;
            y1 = prect->top ;
            x2 = prect->right ;
            y2 = prect->bottom ;

            Rectangle (hDC, x1, y1, x2, y2) ;

            SelectObject (hDC, hOldPen) ;

            // Draw a line of the itemID width in the middle
            // of the control area.

            if (CtlID == IDD_ADDLINEWIDTH)
               {
               iPenWidth = LineWidth (itemID) ;
               hPen = CreatePen (PS_SOLID, iPenWidth, RGB (0, 0, 0)) ;
               }
            else
               {
               hPen = CreatePen (itemID, 1, RGB (0, 0, 0)) ;
               }

            hOldPen = SelectObject (hDC, hPen) ;

            x1 = prect->left + 8 ;
            cy = prect->bottom - prect->top ;
            y1 = prect->top + (cy / 2) - 1 ;
            x2 = prect->right - 8 ;
            MoveToEx (hDC, x1, y1, &point) ;
            LineTo (hDC, x2, y1) ;

            SelectObject (hDC, hOldPen) ;
            DeleteObject (hPen) ;
            SelectObject (hDC, hOldBrush) ;
            BackgroundColor = SetBkColor (hDC, BackgroundColor) ;

            InflateRect (prect, OWNER_DRAWN_ITEM, OWNER_DRAWN_ITEM) ;

            HandleSelectionState (pDI) ;
            HandleFocusState (pDI) ;

            break ;
         }   // switch
      }  // draw entire item   
   }  // OnDrawItem
                        

void /*static*/ OnDestroy (HDLG hDlg)
   {  // OnDestroy
#if 0
   if (!bEditLine)
      {
      switch (iLineType)
         {
         case LineTypeAlert:
            BuildNewValueListForAlert ();
            break;

         case LineTypeChart:
            BuildNewValueListForGraph ();
            break;

         case LineTypeReport:
            BuildNewValueListForReport ();
            break;

         default:
            break;
         }
      }
#endif
   if (!PlayingBackLog ())
      MemoryFree (pPerfData) ;

   if (pszAlertProgram)
      {
      MemoryFree (pszAlertProgram) ;
      pszAlertProgram = NULL ;
      }

   pLineEdit = NULL ;
   bAddLineInProgress = FALSE ;
   dwCurrentDlgID = 0 ;
   bExplainTextButtonHit = FALSE ;
   }  // OnDestroy
   


//==========================================================================//
//                             Exported Functions                           //
//==========================================================================//



BOOL FAR PASCAL AddLineDlgProc (HWND hDlg, 
                                WORD msg, 
                                DWORD wParam, 
                                LONG lParam)
   {  // AddLineDlgProc

   BOOL Status;

   switch (msg)
      {  // switch
      case WM_COMMAND:
         OnCommand (hDlg, wParam, lParam) ;
         return (FALSE) ;
         break ;

#if 0
      case WM_DLGKILLFOCUS:
      case WM_KILLFOCUS:
         if (EditModified (hWndComputer))
            {
            SetHourglassCursor() ;
            OnComputerChanged (hDlg) ;
            SetArrowCursor() ;
            }
         EditSetModified (hWndComputer, FALSE) ;
         break ;
#endif

   	case WM_INITDIALOG:
         SetHourglassCursor() ;
         Status = OnInitDialog (hDlg) ;
         SetArrowCursor() ;

         // set focus on the "Add" button instead of the "Computer"
         SetFocus (DialogControl (hDlg, IDD_ADDLINEADD)) ;
         return FALSE ;
         break ;

      case WM_MEASUREITEM:
         OnMeasureItem (hDlg, (PMEASUREITEMSTRUCT) lParam) ;
         return (TRUE) ;
         break ;

      case WM_DRAWITEM:
         OnDrawItem (hDlg, (PDRAWITEMSTRUCT) lParam) ;
         return (TRUE) ;
         break ;

      case WM_DESTROY:
         OnDestroy (hDlg) ;
         break ;
           
   	default:
	      break;
      }  // switch

   return (FALSE) ;
   }  // AddLineDlgProc




BOOL AddLine (HWND hWndParent, 
              PPERFSYSTEM *ppSystemFirstView,
              PLINEVISUAL pLineVisual,
              int iLineTypeToAdd)
/*
   Effect:        Display the add line dialog box, allowing the user
                  to specify the computer, object, counter, instance,
                  and scale for a line.  The user can also select the
                  visual aspects of color, width and line style.

*/                  
   {  // AddLine
   pLineEdit = NULL ;

   ppSystemFirst = ppSystemFirstView ;   
   iLineType = iLineTypeToAdd ;
   pVisual = pLineVisual ;


   return (DialogBox (hInstance, idDlgAddLine, 
                      hWndParent, (DLGPROC) AddLineDlgProc)) ;
   }  // AddLine



BOOL EditLine (HWND hWndParent,
               PPERFSYSTEM *ppSystemFirstView,
               PLINE pLineToEdit,
               int iLineTypeToEdit)
   {  // EditLine
   if (!pLineToEdit)
      {
      MessageBeep (0) ;
      return (FALSE) ;
      }

   pLineEdit = pLineToEdit ;

   ppSystemFirst = ppSystemFirstView ;   
   iLineType = iLineTypeToEdit ;
   pVisual = &(pLineToEdit->Visual) ;
   
   return (DialogBox (hInstance, idDlgAddLine, 
                      hWndParent, (DLGPROC) AddLineDlgProc)) ;
   }  // EditLine

