VALUEBAR.C

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

Application:

Microsoft Windows NT (TM) Performance Monitor

File:
status.c - Status window procedure and supporting routines.

This file contains code creating the status window, which is
a child of the legend window. The status window shows the
time duration of the chart, and the last, avg, min and max
of the currently-selected chart line.


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


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


#include <stdio.h>
#include <stdlib.h> // for mbstowcs


#include "perfmon.h"
#include "perfmops.h" // for ConvertDecimalPoint
#include "valuebar.h"

#include "grafdata.h" // for CurrentGraphLine
#include "graph.h"
#include "playback.h" // for PlayingBackLog
#include "legend.h"
#include "utils.h"



//==========================================================================//
// Constants //
//==========================================================================//
HDC hVBarDC ;

#define szGraphStatusClass TEXT("PerfmonGraphStatusClass")
#define dwGraphStatusClassStyle (CS_HREDRAW | CS_VREDRAW | CS_OWNDC)
#define iGraphStatusClassExtra (0)
#define iGraphStatusWindowExtra (0)
#define dwGraphStatusWindowStyle (WS_CHILD | WS_VISIBLE)


#define szStatusValueFormat TEXT("%10.3f")
#define szStatusMediumnValueFormat TEXT("%10.0f")
#define szStatusLargeValueFormat TEXT("%10.4e")
#define eStatusValueTooLarge ((FLOAT) 1.0E+20)
#define eStatusValueMax ((FLOAT) 999999.999)
#define eStatusLargeValueMax ((FLOAT) 9999999999.0)


#define szValueTooHigh TEXT("+ + + +")
#define szValueTooLow TEXT("- - - -")

#define StatusLastElt 0
#define StatusAvgElt 1
#define StatusMinElt 2
#define StatusMaxElt 3
#define StatusTimeElt 4

#define StatusNumElts 5

#define StatusDrawAvg(hDC, eValue, bForceRedraw) \
DrawStatusValue (hDC, StatusAvgElt, eValue, bForceRedraw)

#define StatusDrawMax(hDC, eValue, bForceRedraw) \
DrawStatusValue (hDC, StatusMaxElt, eValue, bForceRedraw)

#define StatusDrawMin(hDC, eValue, bForceRedraw) \
DrawStatusValue (hDC, StatusMinElt, eValue, bForceRedraw)

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


typedef struct StatusEltStruct
{
TCHAR szText [20] ;
int xTextPos ;
int xValuePos ;
FLOAT eValue ;
} StatusEltType ;


// This structure represents the "instance data" for a StatusWindow. The
// information is collected in a structure to ease the conversion of this
// code when later adding multiple graph windows. For now, one copy of this
// struct is defined as local data to this file.
typedef struct StatusDataStruct
{
StatusEltType aElts [StatusNumElts] ;
SIZE sizeValue ;
int yHeight ;
} StatusDataType ;



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


StatusDataType StatusData ;


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


#define StatusTopMargin() (2)
#define StatusBottomMargin() (4)

#define StatusLeftMargin() (3)
#define StatusTextMargin() (5 + ThreeDPad)
#define StatusValueMargin() (3 + ThreeDPad)


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


void DrawStatusValue (HDC hDC,
int iEltOffset,
FLOAT eValue,
BOOL bForceRedraw)
/*
Called By: StatusDrawTime, StatusDrawLast, StatusDrawAvg,
StatusDrawMin, StatusDrawMax.
*/
{ // DrawStatusValue
RECT rectValue ;
TCHAR szValue [20] ;

if (!bForceRedraw && eValue == StatusData.aElts[iEltOffset].eValue)
return ;
StatusData.aElts[iEltOffset].eValue = eValue ;

rectValue.left = StatusData.aElts[iEltOffset].xValuePos ;
rectValue.top = StatusTopMargin () + ThreeDPad + 1;
rectValue.right = rectValue.left + StatusData.sizeValue.cx ;
rectValue.bottom = rectValue.top + StatusData.sizeValue.cy ;


if (eValue > eStatusValueMax)
{
if (eValue > eStatusValueTooLarge)
{
lstrcpy (szValue, szValueTooHigh) ;
}
else
{
TSPRINTF (szValue,
(eValue > eStatusLargeValueMax) ? szStatusLargeValueFormat :
szStatusMediumnValueFormat,
eValue) ;
ConvertDecimalPoint (szValue) ;
}
}
else if (eValue < -eStatusValueMax)
lstrcpy (szValue, szValueTooLow) ;
else
{
TSPRINTF (szValue, szStatusValueFormat, eValue) ;
ConvertDecimalPoint (szValue) ;
}

ExtTextOut (hDC, rectValue.right, rectValue.top, ETO_OPAQUE, &rectValue,
szValue, lstrlen (szValue), NULL) ;
} // DrawStatusValue


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


//void static OnCreate (HWND hWnd)
void OnVBarCreate (HWND hWnd)
/*
Effect: Perform any actions needed when a status window is created.
In particular, set the instance data to initial values,
determine the size and placement of the various elements
of the status display.

Called By: GraphStatusWndProc only, in response to a WM_CREATE message.
*/
{ // OnCreate
TCHAR szValue [20] ;
HDC hDC ;
int iLen ;
int i ;


hDC = hVBarDC = GetDC (hWnd) ;
SelectFont (hDC, hFontScales) ;
SetBkColor (hDC, ColorBtnFace) ;
SetTextAlign (hDC, TA_RIGHT | TA_TOP) ;

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

StringLoad (IDS_STATUSLAST, StatusData.aElts[StatusLastElt].szText) ;
StringLoad (IDS_STATUSAVG, StatusData.aElts[StatusAvgElt].szText) ;
StringLoad (IDS_STATUSMIN, StatusData.aElts[StatusMinElt].szText) ;
StringLoad (IDS_STATUSMAX, StatusData.aElts[StatusMaxElt].szText) ;
StringLoad (IDS_STATUSTIME, StatusData.aElts[StatusTimeElt].szText) ;

//=============================//
// Determine Status Height //
//=============================//

StatusData.yHeight = StatusTopMargin () +
StatusBottomMargin () +
FontHeight (hDC, TRUE) +
2 * ThreeDPad ;

//=============================//
// Set position/size of elts //
//=============================//

// Determine the bounding box for each status value by using a max value.
#ifdef JAPAN
iLen = TSPRINTF (szValue, szStatusMediumnValueFormat, -((FLOAT) 9999.999)) ;
#else
iLen = TSPRINTF (szValue, szStatusLargeValueFormat, -eStatusValueMax) ;
#endif

GetTextExtentPoint (hDC, szValue, lstrlen(szValue), &StatusData.sizeValue) ;
for (i = 0 ;
i < StatusNumElts ;
i++)
{ // for
StatusData.aElts[i].eValue = (FLOAT) 0.0 ;

if (i)
StatusData.aElts[i].xTextPos =
StatusTextMargin () +
StatusData.aElts[i - 1].xValuePos +
StatusData.sizeValue.cx ;
else
StatusData.aElts[i].xTextPos = StatusLeftMargin () ;
StatusData.aElts[i].xValuePos = StatusData.aElts[i].xTextPos +
StatusValueMargin () +
TextWidth (hDC, StatusData.aElts[i].szText) ;
} // for
} // OnCreate



void static OnPaint (HWND hWnd)
/*
Effect: Paint the invalid surface of hWnd. Draw each label, each
recessed value box, and each value.

Called By: GraphStatusWndProc only, in response to a WM_PAINT message.
*/
{
HDC hDC ;
PAINTSTRUCT ps ;
RECT rectClient ;
int i ;
PLINESTRUCT pLine;

hDC = BeginPaint (hWnd, &ps) ;
SetBkMode (hDC, TRANSPARENT) ;

GetClientRect (hWnd, &rectClient) ;
HLine (hDC, GetStockObject (BLACK_PEN),
rectClient.left, rectClient.right,
rectClient.bottom - 1) ;

if ((pGraphs->gOptions.bStatusBarChecked) &&
(pGraphs->gOptions.bLegendChecked))
{ // if
UINT hPrevTextAlign = SetTextAlign (hDC, TA_LEFT | TA_TOP) ;

for (i = 0 ;
i < StatusNumElts ;
i++)
{ // for
// Draw the label
TextOut (hDC, StatusData.aElts[i].xTextPos, StatusTopMargin () + ThreeDPad,
StatusData.aElts[i].szText,
lstrlen (StatusData.aElts[i].szText)) ;

// Draw the recesed value box
ThreeDConcave1 (hDC,
StatusData.aElts[i].xValuePos - ThreeDPad,
StatusTopMargin (),
StatusData.aElts[i].xValuePos + StatusData.sizeValue.cx + ThreeDPad,
StatusTopMargin () + StatusData.sizeValue.cy + 2 * ThreeDPad ) ;
} // for

// restore TextAlign for drawing values
SetTextAlign (hDC, hPrevTextAlign) ;

// Draw the values themselves

pLine = CurrentGraphLine (hWndGraph) ;
StatusDrawTime (hDC, TRUE) ;

StatusDrawLast (hDC, pLine, TRUE) ;

if (pLine)
{
StatusDrawAvg (hDC, pLine->lnAveValue, TRUE) ;
StatusDrawMin (hDC, pLine->lnMinValue, TRUE) ;
StatusDrawMax (hDC, pLine->lnMaxValue, TRUE) ;
}
else
{
StatusDrawAvg (hVBarDC, (FLOAT)0.0, TRUE) ;
StatusDrawMax (hVBarDC, (FLOAT)0.0, TRUE) ;
StatusDrawMin (hVBarDC, (FLOAT)0.0, TRUE) ;
}
} // if

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


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



void StatusDrawTime (HDC hDC, BOOL bForceRedraw)
/*
Called By: StatusTimer, StatusPaint.
*/
{ // StatusDrawTime
FLOAT eTimeSeconds ;

if (PlayingBackLog ())
eTimeSeconds = (FLOAT) PlaybackSelectedSeconds () ;
else
eTimeSeconds = pGraphs->gOptions.eTimeInterval *
(FLOAT) pGraphs->gMaxValues;

DrawStatusValue (hDC, StatusTimeElt, eTimeSeconds, bForceRedraw) ;
} // StatusDrawTime


void StatusDrawLast (HDC hDC, PLINESTRUCT pLine, BOOL bForceRedraw)
/*
Called By: StatusTimer, StatusPaint.
*/
{
INT iKnownValue ;
int iMaxValues ;
FLOAT eValue ;

if (!pLine || pGraphs->gKnownValue == -1)
eValue = (FLOAT) 0.0 ;
else
{
iKnownValue = pGraphs->gKnownValue ;
iMaxValues = pGraphs->gMaxValues ;
eValue = pLine->lnValues [iKnownValue % iMaxValues] ;
}

DrawStatusValue (hDC, StatusLastElt, eValue, bForceRedraw) ;
}

#if 0
void StatusDrawAvg (HDC hDC, PLINESTRUCT pLine, BOOL bForceRedraw)
/*
Called By: StatusTimer, StatusPaint.
*/
{
FLOAT eValue ;

if (!pLine)
eValue = (FLOAT) 0.0 ;
else
eValue = pLine->lnAveValue ;

DrawStatusValue (hDC, StatusAvgElt, eValue, bForceRedraw) ;
}


void StatusDrawMax (HDC hDC, PLINESTRUCT pLine, BOOL bForceRedraw)
/*
Called By: StatusTimer, StatusPaint.
*/
{
FLOAT eValue ;

if (!pLine)
eValue = (FLOAT) 0.0 ;
else
eValue = pLine->lnMaxValue ;

DrawStatusValue (hDC, StatusMaxElt, eValue, bForceRedraw) ;
}


void StatusDrawMin (HDC hDC, PLINESTRUCT pLine, BOOL bForceRedraw)
/*
Called By: StatusTimer, StatusPaint.
*/
{
FLOAT eValue ;

if (!pLine)
eValue = (FLOAT) 0.0 ;
else
eValue = pLine->lnMinValue ;

DrawStatusValue (hDC, StatusMinElt, eValue, bForceRedraw) ;
}
#endif


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


bCallDefProc = FALSE ;
lReturnValue = 0L ;

switch (wMsg)
{ // switch
case WM_CREATE:
//OnCreate (hWnd) ;
OnVBarCreate (hWnd) ;
break ;


case WM_PAINT:
OnPaint (hWnd) ;
break ;

case WM_DESTROY:
ReleaseDC (hWnd, hVBarDC) ;
break ;

default:
bCallDefProc = TRUE ;
} // switch


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

return (lReturnValue);
} // GraphStatusWndProc


int ValuebarHeight (HWND hWnd)
/*
Effect: A status window has a preferred height, based on the font
used in its display. Return the preferred height, determined
when the window was created.

Assert: OnCreate has already been called, and it set
StatusData.yHeight.
*/
{
return (StatusData.yHeight) ;
}


void StatusTimer (HWND hWnd, BOOL bForceRedraw)
/*
Effect: Perform any status-window actions necessary when a timer
tick has been received. In particular, update (redraw)
any of the changed values in the status bar.

Internals: Each of these called functions compares the value to be
displayed with the previous value and doesn't draw if the
values are equal.
*/
{ // StatusTimer
PLINESTRUCT pLine;



pLine = CurrentGraphLine (hWndGraph) ;

StatusDrawLast (hVBarDC, pLine, bForceRedraw) ;

if (pLine)
{
StatusDrawAvg (hVBarDC, pLine->lnAveValue, bForceRedraw) ;
StatusDrawMin (hVBarDC, pLine->lnMinValue, bForceRedraw) ;
StatusDrawMax (hVBarDC, pLine->lnMaxValue, bForceRedraw) ;
}
else
{
StatusDrawAvg (hVBarDC, (FLOAT)0.0, bForceRedraw) ;
StatusDrawMax (hVBarDC, (FLOAT)0.0, bForceRedraw) ;
StatusDrawMin (hVBarDC, (FLOAT)0.0, bForceRedraw) ;
}
} // StatusTimer



HWND CreateGraphStatusWindow (HWND hWndGraph)
{
return (CreateWindow (szGraphStatusClass, // class
NULL, // caption
dwGraphStatusWindowStyle, // window style
0, 0, // position
0, 0, // size
hWndGraph, // parent window
NULL, // menu
hInstance, // program instance
NULL)) ; // user-supplied data
}




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

wc.style = dwGraphStatusClassStyle ;
wc.lpfnWndProc = (WNDPROC) GraphStatusWndProc ;
wc.hInstance = hInstance ;
wc.cbClsExtra = iGraphStatusClassExtra ;
wc.cbWndExtra = iGraphStatusWindowExtra ;
wc.hIcon = NULL ;
wc.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wc.hbrBackground = hbLightGray ;
wc.lpszMenuName = NULL ;
wc.lpszClassName = szGraphStatusClass ;

return (RegisterClass (&wc)) ;
}