/*
==============================================================================

  Application:

            Microsoft Windows NT (TM) Performance Monitor

  File:
            legend.c - legend window routines.                          

            This file contains code creating the legend window, which is
            a child of the graph windows. The legend window displays a
            legend line for each line in the associated graph. It also 
            includes an area called the label, which are headers for those
            lines.


  Copyright 1992-1996, Microsoft Corporation. All Rights Reserved.
==============================================================================
*/


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


#include <stdio.h>      // for sprintf
#include "perfmon.h"
#include "legend.h"     // external declarations for this file

#include "owndraw.h"

#include "alert.h"      // for EditAlert
#include "grafdata.h"   // for EditChart
#include "pmemory.h"    // for MemoryXXX (mallloc-type) routines
#include "utils.h"
#include "valuebar.h"   // for StatusTimer
#include "playback.h"   // for PlayingBackLog()
#include "perfmops.h"   // for COnvertDecimalPoint

#define eScaleValueSpace       TEXT(">9999999999.0")

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


#define dwGraphLegendClassStyle     (CS_HREDRAW | CS_VREDRAW)
#define iGraphLegendClassExtra      (0)
#define iGraphLegendWindowExtra     (sizeof (PLEGEND))
#define dwGraphLegendWindowStyle    (WS_CHILD | WS_VISIBLE) 

#define xLegendBorderWidth          (xDlgBorderWidth)
#define yLegendBorderHeight         (yDlgBorderHeight)

#define iLabelLen                   30
#define iLegendColMax               1000

#define LEFTORIENTATION             1
#define CENTERORIENTATION           2
#define RIGHTORIENTATION            3

#define LegendColorCol              0
#define LegendScaleCol              1
#define LegendCounterCol            2
#define LegendInstanceCol           3
#define LegendParentCol             4
#define LegendObjectCol             5
#define LegendSystemCol             6  

#define iLegendNumCols              7

#define iLegendMaxVisibleItems      8

#define dwGraphLegendItemsWindowClass  TEXT("ListBox")
#define dwGraphLegendItemsWindowStyle           \
   (LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | LBS_OWNERDRAWFIXED | \
    WS_VISIBLE | WS_CHILD | WS_VSCROLL)


//==========================================================================//
//                                  Typedefs                                //
//==========================================================================//


typedef struct LEGENDCOLSTRUCT
   {
   TCHAR          szLabel [iLabelLen] ;
   int            xMinWidth ;
   int            xMaxWidth ;
   int            xWidth ;
   int            xPos ;
   int            iOrientation ;
   } LEGENDCOL ;

typedef LEGENDCOL *PLEGENDCOL ;


typedef struct LEGENDTRUCT
   {
   HWND           hWndItems ;
   HFONT          hFontItems ;
   HFONT          hFontLabels ;
   int            xMinWidth ;
   int            yLabelHeight ;
   int            yItemHeight ;
   int            iNumItemsVisible ;
   LEGENDCOL      aCols [iLegendNumCols] ;
   int            iLineType ;
   PLINE          pCurrentLine ;    // current selected line
   }  LEGEND ;

typedef LEGEND *PLEGEND ;


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


#define LabelTopMargin()      (2)
#define LegendLeftMargin()    (3 + ThreeDPad)

#define LegendItemTopMargin() (yBorderHeight)


#define IsLegendLabelVisible()  (TRUE)

#define ColCanGrow(pCol)                     \
   (pCol->xMaxWidth > pCol->xWidth)


#define LegendHorzMargin()    (3)



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


// LEGEND            Legend ;
PLEGEND     pGraphLegendData ;
PLEGEND     pAlertLegendData ;

#define  LegendData(hWnd)     \
   ((hWnd == hWndGraphLegend) ? pGraphLegendData : pAlertLegendData )

//==========================================================================//
//                              Local Functions                             //
//==========================================================================//
void LegendSetCurrentLine (PLEGEND pLegend, int iIndex)
   {
   pLegend->pCurrentLine =
      (PLINESTRUCT) LBData (pLegend->hWndItems, iIndex) ;

   if (pLegend->pCurrentLine == (PLINESTRUCT) LB_ERR)
      {
      pLegend->pCurrentLine = NULL ;
      }
   }  // LegendSetCurrentLine

#if 0
PLEGEND LegendData (HWND hWnd)
   {
   return ((PLEGEND) GetWindowLong (hWnd, 0)) ;
   }
#endif


PLEGEND AllocateLegendData (HWND hWnd, BOOL bChartLegend)
   {
   PLEGEND        pLegend ;

   pLegend = MemoryAllocate (sizeof (LEGEND)) ;
//   SetWindowLong (hWnd, 0, (LONG) pLegend) ;

   if (bChartLegend)
      {
      hWndGraphLegend = hWnd ;
      pGraphLegendData = pLegend ;
      }
   else
      {
      hWndAlertLegend = hWnd ;
      pAlertLegendData = pLegend ;
      }

   return (pLegend) ;
   }



void DrawLegendLabel (PLEGEND pLegend,
                      HDC hDC,
                      HWND hWnd)
   {  // DrawLegendLabel
   int            i ;
   int            xPos ;
   RECT           rect ;
   RECT           rectLB ;
   INT            LocalThreeDPad = ThreeDPad - 1 ;

   SetBkColor (hDC, crLightGray) ;

   SelectFont (hDC, pLegend->hFontLabels) ;

   GetClientRect (hWnd, &rect) ;
//   Fill (hDC, crLightGray, &rect) ;
   ExtTextOut (hDC, rect.left, rect.top,
      ETO_OPAQUE, &(rect), NULL, 0, NULL ) ;

   GetWindowRect (pLegend->hWndItems, &rectLB) ;
   ScreenRectToClient (hWnd, &rectLB) ;
   ThreeDConcave1 (hDC, 
                  rectLB.left - LocalThreeDPad,
                  rectLB.top - LocalThreeDPad,
                  rectLB.right + LocalThreeDPad,
                  rectLB.bottom + LocalThreeDPad) ;

   for (i = 0 ;
        i < iLegendNumCols ;
        i++)
      {  // for
      rect.left = pLegend->aCols[i].xPos ;
      rect.top = yBorderHeight ;
      rect.right = rect.left + pLegend->aCols[i].xWidth - LegendHorzMargin () ;
      rect.bottom = pLegend->yLabelHeight ;

      switch (pLegend->aCols[i].iOrientation)
         {  // switch
         case LEFTORIENTATION:
            SetTextAlign (hDC, TA_LEFT) ;
            xPos = rect.left ;
            break ;

         case CENTERORIENTATION:
            SetTextAlign (hDC, TA_CENTER) ;
            xPos = (rect.left + rect.right) / 2 ;
            break ;

         case RIGHTORIENTATION:
            SetTextAlign (hDC, TA_RIGHT) ;
            xPos = rect.right ;
            break ;

         default:
            xPos = rect.left ;
            break ;
         }  // switch

      ExtTextOut (hDC, 
                  xPos, rect.top, 
                  ETO_OPAQUE,
                  &rect,
                  pLegend->aCols[i].szLabel,
                  lstrlen (pLegend->aCols[i].szLabel),
                  NULL) ;
      }  // for
   }  // DrawLegendLabel



void DrawColorCol (PLEGEND pLegend,
                   PLINE pLine,
                   int iCol,
                   HDC hDC,
                   int yPos)
/*
   Effect:        Draw the "color" column of a legend entry. The color 
                  column displays a small sample of the line drawing.

                  For charts, the sample is a line of the correct style,
                  color, and width.  Since we are using wide lines, 
                  the round endcaps of the lines will breach the column.
                  Therefore we need to set the clip region. We could
                  remove this clipping if we find a way to change the
                  end cap design.  

                  For alerts, the sample is a small circle which echos
                  the design in the alert log itself.
*/      
   {  // DrawColorCol
   HBRUSH         hBrush, hBrushPrevious ;
   RECT           rect ;
   int            yMiddle ;
   int            iCircle ;



   rect.left = pLegend->aCols[iCol].xPos - LegendLeftMargin () + 2 ;
   rect.top = yPos + 1 ;
   rect.right = rect.left + pLegend->aCols[iCol].xWidth - LegendHorzMargin () ;
   rect.bottom = yPos + pLegend->yItemHeight - 1 ;

   yMiddle = rect.top + (rect.bottom - rect.top) / 2 ;
   iCircle = rect.bottom - rect.top - 2 ;
 
  switch (pLegend->iLineType)
      {  // switch
      case LineTypeChart:
   
         if (pLine->Visual.iWidth == 1)
            {
            // simple case with thin pen
            hBrush = SelectBrush (hDC, hbLightGray) ;
            Rectangle (hDC, rect.left, rect.top, rect.right, rect.bottom) ;
   
            HLine (hDC, pLine->hPen, 
                   rect.left + 1, rect.right - 1, yMiddle) ;
            SelectBrush (hDC, hBrush) ;
            }
         else
            {
            // thicker pen width, have to set ClipRect so
            // it will not draw otherside the rect.
            SaveDC (hDC) ;
            hBrush = SelectBrush (hDC, hbLightGray) ;
            Rectangle (hDC, rect.left, rect.top, rect.right, rect.bottom) ;
   
            IntersectClipRect (hDC, 
                               rect.left + 1,
                               rect.top + 1,
                               rect.right - 1,
                               rect.bottom - 1) ;
            HLine (hDC, pLine->hPen, 
                   rect.left + 1, rect.right - 1, yMiddle) ;
            SelectBrush (hDC, hBrush) ;
            RestoreDC (hDC, -1) ;
            }
         break ;


      case LineTypeAlert:
         hBrushPrevious = SelectBrush (hDC, pLine->hBrush) ;

         Ellipse (hDC,
                  rect.left + 2,
                  rect.top + 2,
                  rect.left + 2 + iCircle,
                  rect.top + 2 + iCircle) ;

         SelectBrush (hDC, hBrushPrevious) ;
         break ;
      }  // switch

   }  // DrawColorCol


void DrawLegendCol (PLEGEND pLegend,
                    int iCol,
                    HDC hDC,
                    int yPos,
                    LPTSTR lpszValue)
/*
   Effect:        Draw the value lpszValue for the column iCol on hDC.

   Assert:        The foreground and background text colors of hDC are
                  properly set.
*/
   {  // DrawLegendCol
   RECT           rect ;
   int            xPos ;

   rect.left = pLegend->aCols[iCol].xPos - LegendLeftMargin () ;
   rect.top = yPos ;
   rect.right = rect.left + pLegend->aCols[iCol].xWidth - LegendHorzMargin () ;
   rect.bottom = yPos + pLegend->yItemHeight ;

//   SetTextAlign (hDC, TA_TOP) ;   
   switch (pLegend->aCols[iCol].iOrientation)
      {  // switch
      case LEFTORIENTATION:
         SetTextAlign (hDC, TA_LEFT) ;
         xPos = rect.left ;
         break ;

      case CENTERORIENTATION:
         SetTextAlign (hDC, TA_CENTER) ;
         xPos = (rect.left + rect.right) / 2 ;
         break ;

      case RIGHTORIENTATION:
         SetTextAlign (hDC, TA_RIGHT) ;
         xPos = rect.right ;
         break ;

      default:
         xPos = rect.left ;
         break ;
      }  // switch

   ExtTextOut (hDC, 
               xPos, rect.top + LegendItemTopMargin (), 
               ETO_OPAQUE | ETO_CLIPPED,
               &rect,
               lpszValue,
               lstrlen (lpszValue),
               NULL) ;
   }  // DrawLegendCol


void DrawLegendItem (PLEGEND pLegend,
                     PLINESTRUCT pLine,
                     int yPos,
                     HDC hDC)
   {
   TCHAR          szValue [256] ;
   TCHAR          szInstance [256] ;
   TCHAR          szParent [256] ;
   TCHAR          szNoName[4] ;

   szNoName[0] = szNoName[1] = szNoName[2] = TEXT('-') ;
   szNoName[3] = TEXT('\0') ;

   //=============================//
   // Determine Instance, Parent  //
   //=============================//

   // It's possible that there will be no instance, therefore
   // the lnInstanceName would be NULL.

   if (pLine->lnObject.NumInstances > 0)
      {
      // Test for the parent object instance name title index.
      // If there is one, it implies that there will be a valid
      // Parent Object Name and a valid Parent Object Instance Name.

      // If the Parent Object title index is 0 then
      // just display the instance name.

      lstrcpy (szInstance, pLine->lnInstanceName) ;
      if (pLine->lnInstanceDef.ParentObjectTitleIndex &&
          pLine->lnPINName)
         {
         // Get the Parent Object Name.
         lstrcpy (szParent, pLine->lnPINName) ;
         }
      else
         {
         lstrcpy (szParent, szNoName) ;
         }
      }
   else
      {
      lstrcpy (szInstance, szNoName) ;
      lstrcpy (szParent, szNoName) ;
      }

   //=============================//
   // Draw Color                  //
   //=============================//

   DrawColorCol (pLegend, pLine, LegendColorCol, hDC, yPos) ;

   //=============================//
   // Draw Scale/Value            //
   //=============================//

   if (pLegend->iLineType == LineTypeChart)
      {
      if (pLine->eScale < (FLOAT) 1.0)
         {
         TSPRINTF (szValue, TEXT("%6.6f"), pLine->eScale) ;
         }
      else
         {
         TSPRINTF (szValue, TEXT("%10.3f"), pLine->eScale) ;
         }
      ConvertDecimalPoint (szValue) ;
      }
   else
      {
      FLOAT tempAlertValue ;

      tempAlertValue = pLine->eAlertValue ;
      if (tempAlertValue < (FLOAT) 0.0)
         {
         tempAlertValue = -tempAlertValue ;
         }

      if (tempAlertValue >= (FLOAT) 10000.0)
         {
         if (tempAlertValue < (FLOAT) 1.0E+8)
            {
            TSPRINTF (szValue, TEXT("%c%10.0f"),
               pLine->bAlertOver ? TEXT('>') : TEXT('<'),
               pLine->eAlertValue) ;
            }
         else
            {
            TSPRINTF (szValue, TEXT("%c%10.3e"),
               pLine->bAlertOver ? TEXT('>') : TEXT('<'),
               pLine->eAlertValue) ;
            }
         ConvertDecimalPoint (szValue) ;
         }
      else
         {
         TSPRINTF (szValue, TEXT("%c%10.4f"),
            pLine->bAlertOver ? TEXT('>') : TEXT('<'),
            pLine->eAlertValue) ;
         ConvertDecimalPoint (szValue) ;
         }
      }

   SetTextAlign (hDC, TA_TOP) ;   

   DrawLegendCol (pLegend, LegendScaleCol,
                  hDC, yPos, szValue) ;


   //=============================//
   // Draw Counter                //
   //=============================//

   DrawLegendCol (pLegend, LegendCounterCol,
                  hDC, yPos, pLine->lnCounterName) ;

   
   //=============================//
   // Draw Instance               //
   //=============================//

   DrawLegendCol (pLegend, LegendInstanceCol,
                  hDC, yPos, szInstance) ;

   //=============================//
   // Draw Parent                 //
   //=============================//

   DrawLegendCol (pLegend, LegendParentCol,  
                  hDC, yPos, szParent) ;

   //=============================//
   // Draw Object                 //
   //=============================//

   DrawLegendCol (pLegend, LegendObjectCol,
                  hDC, yPos, pLine->lnObjectName) ;

   //=============================//
   // Draw System                 //
   //=============================//

   DrawLegendCol (pLegend, LegendSystemCol,
                  hDC, yPos, pLine->lnSystemName) ;

   }


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


BOOL OnLegendCreate (HWND hWnd, LPCREATESTRUCT lpCS)
   {
   PLEGEND        pLegend ;
   HDC            hDC ;
   int            iCol ;
   BOOL           bChartLegend ;

   bChartLegend = (lpCS->lpCreateParams) == (LPVOID) TRUE ;

   pLegend = AllocateLegendData (hWnd, bChartLegend) ;

   if (!pLegend)
      return (FALSE) ;

   if (bChartLegend)
      {
      pLegend->iLineType = LineTypeChart ;
      }
   else
      {
      pLegend->iLineType = LineTypeAlert ;
      }

   pLegend->hFontItems = hFontScales ;
   pLegend->hFontLabels = hFontScalesBold ;
   hDC = GetDC (hWnd) ;

   //=============================//
   // Load Labels                 //
   //=============================//

   StringLoad (IDS_LABELCOLOR, pLegend->aCols[LegendColorCol].szLabel) ;
   if (pLegend->iLineType == LineTypeChart)
      StringLoad (IDS_LABELSCALE, 
                  pLegend->aCols[LegendScaleCol].szLabel) ;
   else
      StringLoad (IDS_LABELVALUE,
                  pLegend->aCols[LegendScaleCol].szLabel) ;
   StringLoad (IDS_LABELCOUNTER, pLegend->aCols[LegendCounterCol].szLabel) ;
   StringLoad (IDS_LABELINSTANCE, pLegend->aCols[LegendInstanceCol].szLabel) ;
   StringLoad (IDS_LABELPARENT, pLegend->aCols[LegendParentCol].szLabel) ;
   StringLoad (IDS_LABELOBJECT, pLegend->aCols[LegendObjectCol].szLabel) ;
   StringLoad (IDS_LABELSYSTEM, pLegend->aCols[LegendSystemCol].szLabel) ;


   //=============================//
   // Label dimensions            //
   //=============================//

   SelectFont (hDC, pLegend->hFontLabels) ;
   pLegend->yLabelHeight = FontHeight (hDC, TRUE) + 2 * LabelTopMargin () ;

   //=============================//
   // Column dimensions           //
   //=============================//

   for (iCol = 0 ;
        iCol < iLegendNumCols ;
        iCol++)
      {  // for
      pLegend->aCols[iCol].iOrientation = LEFTORIENTATION ;
      pLegend->aCols[iCol].xMinWidth = 
         TextWidth (hDC, pLegend->aCols[iCol].szLabel) + LegendHorzMargin () ;
      }  // for

   SelectFont (hDC, pLegend->hFontItems) ;
   pLegend->yItemHeight = FontHeight (hDC, TRUE) + 2 * LegendItemTopMargin () ;

   pLegend->aCols[LegendColorCol].xMaxWidth = max (3 * xScrollWidth,
      pLegend->aCols[LegendColorCol].xMinWidth) ;

   pLegend->aCols[LegendScaleCol].xMaxWidth = TextWidth (hDC, eScaleValueSpace) ;
   pLegend->aCols[LegendCounterCol].xMaxWidth = iLegendColMax ;
   pLegend->aCols[LegendInstanceCol].xMaxWidth = iLegendColMax ;
   pLegend->aCols[LegendParentCol].xMaxWidth = iLegendColMax ;
   pLegend->aCols[LegendObjectCol].xMaxWidth = iLegendColMax ;
   pLegend->aCols[LegendSystemCol].xMaxWidth = iLegendColMax ;
   
   pLegend->aCols[LegendColorCol].iOrientation = LEFTORIENTATION ;
   pLegend->aCols[LegendScaleCol].iOrientation = RIGHTORIENTATION ;

   ReleaseDC (hWnd, hDC) ;   

   //=============================//
   // Create Legend Items Listbox //
   //=============================//

   pLegend->hWndItems =
      CreateWindow (TEXT("ListBox"),               // window class
                    NULL,                          // window caption
                    dwGraphLegendItemsWindowStyle, // window style
                    0, 0, 0, 0,                    // window size and pos
                    hWnd,                          // parent window
                    NULL,                          // menu
                    hInstance,                     // program instance
                    (LPVOID) TRUE) ;               // user-supplied data
   }  // OnLegendCreate
   

void static OnSize (HWND hWnd, int xWidth, int yHeight)
/*
   Effect:        Perform all actions necessary when the legend window is
                  being resized. In particular, set the width and starting
                  position for each column of the legend list and labels.

   Internals:     What we do is determine the amount of space in the
                  width that is greater than the minimum, and allocate
                  that space among each of the columns that is willing to
                  be larger than minimum. If there is not enough room for
                  the minimum, don't bother with calculations.
*/

   {  // OnSize
   PLEGEND        pLegend ;
   PLEGENDCOL     pCol ;
   int            xMin ;
   int            xSlack ;
   int            iColsToGrow ;
   int            iCol ;

   pLegend = LegendData (hWnd) ;

   //=============================//
   // Resize Legend Items         //
   //=============================//

   if (IsLegendLabelVisible ())
      MoveWindow (pLegend->hWndItems, 
                  LegendLeftMargin (), pLegend->yLabelHeight + ThreeDPad,
                  xWidth - 2 * LegendLeftMargin (),
                  yHeight - pLegend->yLabelHeight - yLegendBorderHeight,
                  TRUE) ;
   else
      MoveWindow (pLegend->hWndItems,
                  0, 0, 
                  xWidth, yHeight,
                  TRUE) ;
                  
      
   //=============================//
   // Allocate width to Columns   //
   //=============================//

   xMin = LegendMinWidth (hWnd) ;
   xSlack = max (xWidth - xMin, 0) ;
   iColsToGrow = 0 ;

   for (iCol = 0 ;
        iCol < iLegendNumCols ;
        iCol++)
      {  // for
      pCol = &(pLegend->aCols[iCol]) ;

      pCol->xWidth = pCol->xMinWidth ;
      if (ColCanGrow (pCol))
         iColsToGrow++ ;
      }  // for

   for (iCol = 0 ;
        iCol < iLegendNumCols ;
        iCol++)
      {  // for
      pCol = &(pLegend->aCols[iCol]) ;

      if (ColCanGrow (pCol))
         {
         if ((pCol->xWidth + xSlack / iColsToGrow) >
             pCol->xMaxWidth)
            {  // if
            pCol->xWidth = pCol->xMaxWidth ;
            xSlack -= (pCol->xMaxWidth - pCol->xMinWidth) ;
            iColsToGrow-- ;
            }  // if
         }  // if
      }  // for

   for (iCol = 0 ;
        iCol < iLegendNumCols ;
        iCol++)
      {  // for
      pCol = &(pLegend->aCols[iCol]) ;

      if (ColCanGrow (pCol))
        pCol->xWidth += xSlack / iColsToGrow ;
      }  // for

   if (pLegend->aCols[LegendCounterCol].xWidth <
      pLegend->aCols[LegendCounterCol].xMaxWidth)
      {
      // cut some from the other columns and give them to CounterCol
      if (pLegend->aCols[LegendColorCol].xWidth - xScrollWidth >=
          pLegend->aCols[LegendColorCol].xMinWidth)
         {
         pLegend->aCols[LegendColorCol].xWidth -= xScrollWidth ;      
         pLegend->aCols[LegendCounterCol].xWidth += xScrollWidth ;
         }
      if (pLegend->aCols[LegendInstanceCol].xWidth - xScrollWidth >=
          pLegend->aCols[LegendInstanceCol].xMinWidth)
         {
         pLegend->aCols[LegendInstanceCol].xWidth -= xScrollWidth ;      
         pLegend->aCols[LegendCounterCol].xWidth += xScrollWidth ;
         }
      if (pLegend->aCols[LegendParentCol].xWidth - xScrollWidth >=
          pLegend->aCols[LegendParentCol].xMinWidth)
         {
         pLegend->aCols[LegendParentCol].xWidth -= xScrollWidth ;      
         pLegend->aCols[LegendCounterCol].xWidth += xScrollWidth ;
         }
      }

   
   //=============================//
   // Set Column positions        //
   //=============================//

   pLegend->aCols[0].xPos = LegendLeftMargin () ;
   for (iCol = 1 ;
        iCol < iLegendNumCols ;
        iCol++)
      {  // for
      pLegend->aCols[iCol].xPos = 
         pLegend->aCols[iCol - 1].xPos + pLegend->aCols[iCol - 1].xWidth ;

      if (iCol == LegendCounterCol)
         {
         // add some space between Scale/Value col & Counter col
         pLegend->aCols[LegendCounterCol].xPos += LegendLeftMargin () ;
         }
      }  // for

   }  // OnSize


void static OnPaint (HWND hWnd)
   {  // OnPaint
   PLEGEND        pLegend ;
   HDC            hDC ;
   PAINTSTRUCT    ps ;

   hDC = BeginPaint (hWnd, &ps) ;

   if (IsLegendLabelVisible ())
      {
      pLegend = LegendData (hWnd) ;
      DrawLegendLabel (pLegend, hDC, hWnd) ;
      }

   if (LBNumItems (pLegend->hWndItems) == 0)
      {
      WindowInvalidate(pLegend->hWndItems) ;
      }

   EndPaint (hWnd, &ps) ;
   }  // OnPaint


void OnDrawLegendItem (HWND hWnd,
                        LPDRAWITEMSTRUCT lpDI)
   {  // OnDrawItem
   HFONT          hFontPrevious ;
   HDC            hDC ;
   PLEGEND        pLegend ;
   PLINESTRUCT    pLine ;
   int            iLBIndex ;
   COLORREF       preBkColor ;
   COLORREF       preTextColor ;
   BOOL           ResetColor = FALSE ;

   
   hDC = lpDI->hDC ;
   iLBIndex = DIIndex (lpDI) ;

   pLegend = LegendData (hWnd) ;

   if (iLBIndex == -1)
      pLine = NULL ;
   else
      pLine = (PLINESTRUCT) LBData (pLegend->hWndItems, iLBIndex) ;

   //=============================//
   // Set DC attributes           //
   //=============================//


   if (DISelected (lpDI) || pLine == NULL)
      {  // if
      preTextColor = SetTextColor (hDC, GetSysColor (COLOR_HIGHLIGHTTEXT)) ;
      preBkColor = SetBkColor (hDC, GetSysColor (COLOR_HIGHLIGHT)) ;
      ResetColor = TRUE ;
      }  // if
   ExtTextOut (hDC, lpDI->rcItem.left, lpDI->rcItem.top,
      ETO_OPAQUE, &(lpDI->rcItem), NULL, 0, NULL ) ;
   

   //=============================//
   // Draw Legend Item            //
   //=============================//

   hFontPrevious = SelectFont (hDC, pLegend->hFontItems) ;
   if (pLine)
      DrawLegendItem (pLegend, pLine, lpDI->rcItem.top, hDC) ;
   SelectFont (hDC, hFontPrevious) ;

   //=============================//
   // Draw Focus                  //
   //=============================//

   if (DIFocus (lpDI))
      DrawFocusRect (hDC, &(lpDI->rcItem)) ;


   if (ResetColor == TRUE)
      {  // if
      preTextColor = SetTextColor (hDC, preTextColor) ;
      preBkColor = SetBkColor (hDC, preBkColor) ;
      }  // if
   }  // OnDrawItem


void static OnMeasureItem (HWND hWnd,
                           LPMEASUREITEMSTRUCT lpMI)
   {  // OnMeasureItem
   PLEGEND        pLegend ;

   pLegend = LegendData (hWnd) ;
   lpMI->itemHeight = pLegend->yItemHeight ;
   }  // OnMeasureItem


void static OnDestroy (HWND hWnd)
   {
   PLEGEND        pLegend ;

   pLegend = LegendData (hWnd) ;
   MemoryFree (pLegend) ;
   }  // OnDestroy



void static OnDoubleClick (HWND hWnd)
   {
   PLEGEND        pLegend ;

   pLegend = LegendData (hWnd) ;
   if (!pLegend)
      return ;

   switch (pLegend->iLineType)
      {  // switch
      case LineTypeChart:
         EditChart (hWndMain) ;
         break ;

      case LineTypeAlert:
         EditAlert (hWndAlert) ;
         break ;
      }  // switch
   }  // OnDoubleClick


void static OnSetFocus (HWND hWnd)
   {  // OnSetFocus
   PLEGEND        pLegend ;

   pLegend = LegendData (hWnd) ;

   SetFocus (pLegend->hWndItems) ;
   }

void static OnSelectionChanged (HWND hWnd)
   {  // OnSelectionChanged
   PLEGEND        pLegend ;
   PGRAPHSTRUCT         pGraph ;
   int            iIndex ;


   pLegend = LegendData (hWnd) ;

   // set the new selected line
   iIndex = LBSelection (pLegend->hWndItems) ;

   if (iIndex == LB_ERR)
      return ;

   LegendSetCurrentLine (pLegend, iIndex) ;

   if (pLegend->iLineType == LineTypeChart)
      {
      pGraph = GraphData (hWndGraph) ;
      
      if (!PlayingBackLog())
         {
         // update the min, max, & avg of the current line
         UpdateValueBarData (pGraph) ;
         }

      // update the valuebar display
      StatusTimer (hWndGraphStatus, TRUE) ;

      // change the Current highlighted line if necessary
      if (pGraph && pGraph->HighLightOnOff)
         {
         WindowInvalidate (hWndGraphDisplay) ;
         }
      }
   }  // OnSelectionChanged


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


LRESULT APIENTRY GraphLegendWndProc (HWND hWnd,
                                     WORD wMsg,
                                     WPARAM wParam,
                                     LONG lParam)
   {  // GraphLegendWndProc
   BOOL           bCallDefProc ;
   LRESULT        lReturnValue ;


   bCallDefProc = FALSE ;
   lReturnValue = 0L ;

   switch (wMsg)
      {  // switch
      case WM_DELETEITEM:
         break ;

      case WM_COMMAND:
         switch (HIWORD (wParam))
            {  // switch
            case LBN_DBLCLK:
               OnDoubleClick (hWnd) ;
               break ;

            case LBN_SELCHANGE:
               OnSelectionChanged (hWnd) ;
               break ;

            default:
               break ;
            }  // switch
         break ;

      case WM_CREATE:
         OnLegendCreate (hWnd, (LPCREATESTRUCT) lParam) ;
         break ;

      case WM_DESTROY:
         OnDestroy (hWnd) ;
         break ;

      case WM_DRAWITEM:
         OnDrawLegendItem (hWnd, (LPDRAWITEMSTRUCT) lParam) ;
         break ;

      case WM_MEASUREITEM:
         OnMeasureItem (hWnd, (LPMEASUREITEMSTRUCT) lParam) ;
         break ;

      case WM_PAINT:
         OnPaint (hWnd) ;
         break ;

      case WM_SIZE:
         OnSize (hWnd, LOWORD (lParam), HIWORD (lParam)) ;
         break ;

      case WM_SETFOCUS:
         OnSetFocus (hWnd) ;
         break ;

      default:
         bCallDefProc = TRUE ;
      }  // switch


   if (bCallDefProc)
      lReturnValue = DefWindowProc (hWnd, wMsg, wParam, lParam) ;

   return (lReturnValue);
   }  // GraphLegendWndProc


int LegendMinWidth (HWND hWnd)
   {
   PLEGEND        pLegend ;
   int            iCol ;
   int            xMinWidth ;

   pLegend = LegendData (hWnd) ;
   xMinWidth = 0 ;

   for (iCol = 0 ;
        iCol < iLegendNumCols ;
        iCol++)
      {  // for
      xMinWidth += pLegend->aCols[iCol].xMinWidth ;
      }  // for

   return (xMinWidth) ;
   }


int LegendMinHeight (HWND hWnd)
   {
   PLEGEND        pLegend ;

   pLegend = LegendData (hWnd) ;

   if (IsLegendLabelVisible ())
      return (pLegend->yLabelHeight + pLegend->yItemHeight) ;
   else
      return (pLegend->yItemHeight) ;
   }


int LegendHeight (HWND hWnd, int yGraphHeight)
/*
   Effect:        Return the best height for the Legend window given the 
                  number of items and label visibility of the legend, as
                  well as the overall height of the graph.
*/
   {  // LegendHeight
   PLEGEND        pLegend ;
   int            yPreferredHeight ;

   pLegend = LegendData (hWnd) ;

   yPreferredHeight = yLegendBorderHeight +
                      pLegend->yItemHeight * 
                      PinInclusive (LBNumItems (pLegend->hWndItems),
                                    1, iLegendMaxVisibleItems) ;
   if (IsLegendLabelVisible ())
      yPreferredHeight += pLegend->yLabelHeight ;

   return (min (yPreferredHeight, yGraphHeight / 2)) ;
   }  // LegendHeight



int LegendFullHeight (HWND hWnd, HDC hDC)
/*
   Effect:        Return the best height for the Legend window given the 
                  number of items and label visibility of the legend, as
                  well as the overall height of the graph.
*/
   {  // LegendHeight
   PLEGEND        pLegend ;
   int            yPreferredHeight ;

   pLegend = LegendData (hWnd) ;

   yPreferredHeight = yLegendBorderHeight +
                      pLegend->yItemHeight * 
                      LBNumItems (pLegend->hWndItems) ;
   if (IsLegendLabelVisible ())
      yPreferredHeight += pLegend->yLabelHeight ;

   return (yPreferredHeight) ;
   }  // LegendFullHeight




HWND CreateGraphLegendWindow (HWND hWndGraph)
   {
   return (CreateWindow (szGraphLegendClass,       // class
                         NULL,                     // caption
                         dwGraphLegendWindowStyle, // window style
                         0, 0,                     // position
                         0, 0,                     // size
                         hWndGraph,                // parent window
                         NULL,                     // menu
                         hInstance,                // program instance
                         (LPVOID) TRUE)) ;         // user-supplied data
   }


BOOL GraphLegendInitializeApplication (void)
/*
   Called By:     GraphInitializeApplication only
*/
   {
   WNDCLASS       wc ;

   wc.style          = dwGraphLegendClassStyle ;
   wc.lpfnWndProc    = (WNDPROC) GraphLegendWndProc ;
   wc.hInstance      = hInstance ;
   wc.cbClsExtra     = iGraphLegendClassExtra ;
   wc.cbWndExtra     = iGraphLegendWindowExtra ;
   wc.hIcon          = NULL ;
   wc.hCursor        = LoadCursor (NULL, IDC_ARROW) ;
   wc.hbrBackground  = hbLightGray ;
   wc.lpszMenuName   = NULL ;
   wc.lpszClassName  = szGraphLegendClass ;

   return (RegisterClass (&wc)) ;
   }


BOOL LegendAddItem (HWND hWnd, PLINESTRUCT pLine)
/*
   Effect:        Add a legend entry for the line pLine. Don't select
                  the line as current entry here, because this will cause
                  the scroll bar to unneccesarily appear for a moment.
                  If you want this line to be the current line, resize 
                  the legend first, then set as current line.

   See Also:      ChartInsertLine, AlertInsertLine.                  
*/
   {  // LegendAddItem
   PLEGEND        pLegend ;

   pLegend = LegendData (hWnd) ;
   LBAdd (pLegend->hWndItems, pLine) ;

   return (TRUE) ;
   }  // LegendAddItem


void LegendDeleteItem (HWND hWndLegend, 
                       PLINE pLine)
   {  // LegendDeleteItem
   PLEGEND        pLegend ;
   int            iIndex ;
   int            iNextIndex ;
   int            iNumItems ;
   PGRAPHSTRUCT   pGraph ;

   pLegend = LegendData (hWndLegend) ;
   if (!pLegend)
      return ;

   iNumItems = LBNumItems (pLegend->hWndItems) ;

   iIndex = LBFind (pLegend->hWndItems, pLine) ;

   if (iIndex != LB_ERR)
      {
      LBDelete (pLegend->hWndItems, iIndex) ;
      }

   // no need to do anything if iNumItems is 1 to begin with
   if (iNumItems != LB_ERR && iNumItems > 1)
      {
      if (iIndex == iNumItems - 1)
         {
         // deleting the last line, then set selection
         // to the previous legend line
         iNextIndex = iIndex - 1 ;
         }
      else
         {
         // set the selection to the next legend line
         iNextIndex = iIndex ;
         }

      LBSetSelection (pLegend->hWndItems, iNextIndex) ;
      LegendSetCurrentLine (pLegend, iNextIndex) ;

      if (pLegend->iLineType == LineTypeChart)
         {
         // update the min, max, & avg of the current line
         pGraph = GraphData (hWndGraph) ;

         if (!PlayingBackLog())
            {
            // update the min, max, & avg of the current line
            UpdateValueBarData (pGraph) ;
            }
   
         // update the valuebar display
         StatusTimer (hWndGraphStatus, TRUE) ;
         }
      }

   }  // LegendDeleteItem


PLINE LegendCurrentLine (HWND hWndLegend)
   {  // LegendCurrentLine
   PLEGEND        pLegend ;

   pLegend = LegendData (hWndLegend) ;

   if (!pLegend)
      return (NULL) ;

   return (pLegend->pCurrentLine) ;

   }  // LegendCurrentLine



int LegendNumItems (HWND hWndLegend)
   {  // LegendNumItems
   PLEGEND        pLegend ;

   pLegend = LegendData (hWndLegend) ;

   return (LBNumItems (pLegend->hWndItems)) ;
   }  // LegendNumItems



void LegendSetSelection (HWND hWndLegend, int iIndex)
   {  // LegendSetSelection
   PLEGEND        pLegend ;

   pLegend = LegendData (hWndLegend) ;
   LBSetSelection (pLegend->hWndItems, iIndex) ;
   LegendSetCurrentLine (pLegend, iIndex) ;
   }  // LegendSetSelection   



#ifdef KEEP_PRINT

void PrintLegend (HDC hDC, PGRAPHSTRUCT pGraph, HWND hWndLegend,
                  RECT rectLegend)
   {
   PLEGEND        pLegend ;
   int            yItemHeight ;
   HFONT          hFontItems ;
   PLINE          pLine ;
   int            iIndex ;
   int            iIndexNum ;


   pLegend = LegendData (hWndLegend) ;

   yItemHeight = pLegend->yItemHeight ;
   hFontItems = pLegend->hFontItems ;

   pLegend->hFontItems = hFontPrinterScales ;
   SelectFont (hDC, pLegend->hFontItems) ;

   pLegend->yItemHeight = FontHeight (hDC, TRUE) ;

   iIndexNum = LBNumItems (pLegend->hWndItems) ;
   for (iIndex = 0 ;
        iIndex < iIndexNum ;
        iIndex++)
      {  // for
      pLine = (PLINE) LBData (pLegend->hWndItems, iIndex) ;
      DrawLegendItem (pLegend, pLine, iIndex * pLegend->yItemHeight, hDC) ;
      }  // for

   pLegend->hFontItems = hFontItems ;
   pLegend->yItemHeight = yItemHeight ;


   SelectBrush (hDC, GetStockObject (HOLLOW_BRUSH)) ;
   SelectPen (hDC, GetStockObject (BLACK_PEN)) ;
   Rectangle (hDC, 0, 0, 
              rectLegend.right - rectLegend.left,
              rectLegend.bottom - rectLegend.top) ;
   }
#endif


void ClearLegend (HWND hWndLegend)
   {
   PLEGEND        pLegend ;

   pLegend = LegendData (hWndLegend) ;
   if (!pLegend)
      return ;

   LBReset (pLegend->hWndItems) ;
   pLegend->pCurrentLine = NULL ;
   }

void LegendSetRedraw (HWND hWndLegend, BOOL bRedraw)
   {
   PLEGEND        pLegend ;

   pLegend = LegendData (hWndLegend) ;
   if (!pLegend)
      return ;

   LBSetRedraw (pLegend->hWndItems, bRedraw) ;
}


