1353 lines
42 KiB
C++
1353 lines
42 KiB
C++
/*++
|
|
|
|
Copyright (C) 1996-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
grphdsp.cpp
|
|
|
|
Abstract:
|
|
|
|
<abstract>
|
|
|
|
--*/
|
|
|
|
#include "polyline.h"
|
|
#include "winhelpr.h"
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
|
|
#define ThreeDPad 1
|
|
#define BORDER ThreeDPad
|
|
#define TEXT_MARGIN (ThreeDPad + 2)
|
|
|
|
static HPEN hPenWhite;
|
|
static HPEN hPenBlack;
|
|
|
|
INT
|
|
CGraphDisp::RGBToLightness ( COLORREF clrValue )
|
|
{
|
|
INT iLightness;
|
|
INT iRed;
|
|
INT iGreen;
|
|
INT iBlue;
|
|
INT iMax;
|
|
INT iMin;
|
|
|
|
// The complete algorithm for computing lightness is:
|
|
// Lightness = (Max(R,G,B)+ Min(R,G,B))/2*225.
|
|
// Only need to compute enought to determine whether to draw black or white highlight.
|
|
|
|
iRed = GetRValue( clrValue );
|
|
iGreen = GetGValue (clrValue );
|
|
iBlue = GetBValue (clrValue );
|
|
|
|
if ( iRed > iGreen ) {
|
|
iMax = iRed;
|
|
iMin = iGreen;
|
|
} else {
|
|
iMax = iGreen;
|
|
iMin = iRed;
|
|
}
|
|
|
|
if ( iBlue > iMax ) {
|
|
iMax = iBlue;
|
|
} else if ( iBlue < iMin ) {
|
|
iMin = iBlue;
|
|
}
|
|
|
|
iLightness = iMin + iMax;
|
|
|
|
return iLightness;
|
|
}
|
|
|
|
CGraphDisp::CGraphDisp ( void )
|
|
: m_pCtrl ( NULL ),
|
|
m_pGraph ( NULL ),
|
|
m_pHiliteItem ( NULL ),
|
|
m_hFontVertical ( NULL ),
|
|
m_bBarConfigChanged ( TRUE )
|
|
{
|
|
}
|
|
|
|
CGraphDisp::~CGraphDisp ( void )
|
|
{
|
|
if (m_hFontVertical != NULL)
|
|
DeleteObject(m_hFontVertical);
|
|
|
|
if (m_hPenTimeBar != 0) {
|
|
DeleteObject ( m_hPenTimeBar );
|
|
m_hPenTimeBar = 0;
|
|
}
|
|
|
|
if (m_hPenGrid != 0) {
|
|
DeleteObject ( m_hPenGrid );
|
|
m_hPenGrid = 0;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CGraphDisp::Init (
|
|
CSysmonControl *pCtrl,
|
|
PGRAPHDATA pGraph
|
|
)
|
|
{
|
|
BOOL bRetStatus = TRUE;
|
|
|
|
m_pCtrl = pCtrl;
|
|
m_pGraph = pGraph;
|
|
|
|
m_clrCurrentGrid = m_pCtrl->clrGrid();
|
|
m_clrCurrentTimeBar = m_pCtrl->clrTimeBar();
|
|
|
|
// Create the highlight, timebar and grid pens.
|
|
|
|
m_hPenTimeBar = CreatePen(PS_SOLID, 2, m_clrCurrentTimeBar );
|
|
|
|
// if can't do it, use a stock object (this can't fail)
|
|
if (m_hPenTimeBar == NULL)
|
|
m_hPenTimeBar = (HPEN)GetStockObject(BLACK_PEN);
|
|
|
|
m_hPenGrid = CreatePen(PS_SOLID, 1, m_clrCurrentGrid );
|
|
|
|
// if can't do it, use a stock object (this can't fail)
|
|
if (m_hPenGrid == NULL)
|
|
m_hPenGrid = (HPEN)GetStockObject(BLACK_PEN);
|
|
|
|
// Highlight pens are shared among all Sysmon instances.
|
|
BEGIN_CRITICAL_SECTION
|
|
|
|
if (hPenWhite == 0) {
|
|
hPenWhite = CreatePen(PS_SOLID, 3, RGB(255,255,255));
|
|
hPenBlack = CreatePen(PS_SOLID, 3, RGB(0,0,0));
|
|
}
|
|
|
|
END_CRITICAL_SECTION
|
|
|
|
return bRetStatus;
|
|
}
|
|
|
|
void CGraphDisp::HiliteItem( PCGraphItem pItem )
|
|
{
|
|
m_pHiliteItem = pItem;
|
|
}
|
|
|
|
|
|
VOID
|
|
CGraphDisp::Draw(
|
|
HDC hDC,
|
|
HDC hAttribDC,
|
|
BOOL fMetafile,
|
|
BOOL fEntire,
|
|
PRECT /* prcUpdate */ )
|
|
{
|
|
|
|
RECT rectFrame;
|
|
RECT rectTitle;
|
|
CStepper locStepper;
|
|
DWORD dwPrevLayout = 0;
|
|
DWORD dwNewLayout = 0;
|
|
|
|
if ( ( m_rect.right > m_rect.left ) && ( m_rect.bottom > m_rect.top ) ) {
|
|
|
|
if ( NULL != hDC ) {
|
|
|
|
dwPrevLayout = GetLayout ( hDC );
|
|
dwNewLayout = dwPrevLayout;
|
|
|
|
if ( dwNewLayout & LAYOUT_RTL ) {
|
|
dwNewLayout &= ~LAYOUT_RTL;
|
|
SetLayout (hDC, dwNewLayout);
|
|
}
|
|
|
|
// Fill plot area
|
|
Fill(hDC, m_pCtrl->clrBackPlot(), &m_rectPlot);
|
|
|
|
rectFrame = m_rectPlot;
|
|
// Draw 3D border around plot area
|
|
if ( eAppear3D == m_pCtrl->Appearance() ) {
|
|
InflateRect(&rectFrame,BORDER,BORDER);
|
|
DrawEdge(hDC, &rectFrame, BDR_SUNKENOUTER, BF_RECT);
|
|
}
|
|
|
|
// Select colors for all text
|
|
SetBkMode(hDC, TRANSPARENT);
|
|
SetTextColor(hDC, m_pCtrl->clrFgnd());
|
|
|
|
// Draw the scale
|
|
if (m_pGraph->Options.bLabelsChecked) {
|
|
SelectFont(hDC, m_pCtrl->Font()) ;
|
|
m_pGraph->Scale.Draw(hDC);
|
|
}
|
|
|
|
// Draw the main title
|
|
if (m_pGraph->Options.pszGraphTitle != NULL) {
|
|
|
|
SelectFont(hDC, m_pCtrl->Font()) ;
|
|
SetTextAlign(hDC, TA_TOP|TA_CENTER);
|
|
|
|
rectTitle = rectFrame;
|
|
rectTitle.top = m_rect.top;
|
|
FitTextOut(
|
|
hDC,
|
|
hAttribDC,
|
|
0,
|
|
&rectTitle,
|
|
m_pGraph->Options.pszGraphTitle,
|
|
lstrlen(m_pGraph->Options.pszGraphTitle),
|
|
TA_CENTER,
|
|
FALSE );
|
|
}
|
|
|
|
// Draw the Y axis title
|
|
if (m_pGraph->Options.pszYaxisTitle != NULL && m_hFontVertical != NULL) {
|
|
SelectFont(hDC, m_hFontVertical) ;
|
|
SetTextAlign(hDC, TA_TOP|TA_CENTER);
|
|
|
|
rectTitle = rectFrame;
|
|
rectTitle.left = m_rect.left;
|
|
FitTextOut(
|
|
hDC,
|
|
hAttribDC,
|
|
0,
|
|
&rectTitle,
|
|
m_pGraph->Options.pszYaxisTitle,
|
|
lstrlen(m_pGraph->Options.pszYaxisTitle),
|
|
TA_CENTER,
|
|
TRUE);
|
|
}
|
|
|
|
// setup stepper reset to start
|
|
locStepper = m_pGraph->TimeStepper;
|
|
locStepper.Reset();
|
|
|
|
// Set clipping area. Fill executed above, so bFill= FALSE.
|
|
StartUpdate(hDC, fMetafile, fEntire, 0, (m_rectPlot.right - m_rectPlot.left), FALSE );
|
|
|
|
// draw the grid lines
|
|
DrawGrid(hDC, 0, m_rectPlot.right - m_rectPlot.left);
|
|
|
|
m_pCtrl->LockCounterData();
|
|
|
|
switch (m_pGraph->Options.iDisplayType) {
|
|
|
|
case LINE_GRAPH:
|
|
|
|
// Finish and restart update so that wide lines are cropped at the timeline.
|
|
FinishUpdate(hDC, fMetafile);
|
|
|
|
StartUpdate(
|
|
hDC,
|
|
fMetafile,
|
|
FALSE,
|
|
0,
|
|
m_pGraph->TimeStepper.Position(),
|
|
FALSE );
|
|
|
|
// Plot points from start of graph to time line
|
|
PlotData(hDC, m_pGraph->TimeStepper.StepNum() + m_pGraph->History.nBacklog,
|
|
m_pGraph->TimeStepper.StepNum(), &locStepper);
|
|
|
|
FinishUpdate(hDC, fMetafile);
|
|
|
|
// Plot points from time line to end of graph
|
|
locStepper = m_pGraph->TimeStepper;
|
|
|
|
// Restart update. Left-hand ends and internal gaps of wide lines are not cropped.
|
|
StartUpdate(
|
|
hDC,
|
|
fMetafile,
|
|
FALSE,
|
|
locStepper.Position(),
|
|
m_rectPlot.right - m_rectPlot.left,
|
|
FALSE );
|
|
|
|
PlotData(hDC, m_pGraph->TimeStepper.StepCount() + m_pGraph->History.nBacklog,
|
|
m_pGraph->TimeStepper.StepCount() - m_pGraph->TimeStepper.StepNum(),
|
|
&locStepper);
|
|
|
|
DrawTimeLine(hDC, m_pGraph->TimeStepper.Position());
|
|
|
|
if ( MIN_TIME_VALUE != m_pGraph->LogViewTempStart )
|
|
DrawStartStopLine(hDC, m_pGraph->LogViewStartStepper.Position());
|
|
if ( MAX_TIME_VALUE != m_pGraph->LogViewTempStop )
|
|
DrawStartStopLine(hDC, m_pGraph->LogViewStopStepper.Position());
|
|
break;
|
|
|
|
case BAR_GRAPH:
|
|
PlotBarGraph(hDC, FALSE);
|
|
break;
|
|
}
|
|
|
|
FinishUpdate(hDC, fMetafile);
|
|
|
|
if ( dwNewLayout != dwPrevLayout ) {
|
|
SetLayout (hDC, dwPrevLayout);
|
|
}
|
|
|
|
m_pCtrl->UnlockCounterData();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
CGraphDisp::UpdateTimeBar(
|
|
HDC hDC,
|
|
BOOL bPlotData )
|
|
{
|
|
INT nBacklog;
|
|
INT iUpdateCnt;
|
|
INT i;
|
|
CStepper locStepper;
|
|
|
|
nBacklog = m_pGraph->History.nBacklog;
|
|
|
|
// Work off backlogged sample intervals
|
|
while ( nBacklog > 0) {
|
|
|
|
// If we are going to wrap around, update in two steps
|
|
if (nBacklog > m_pGraph->TimeStepper.StepCount()
|
|
- m_pGraph->TimeStepper.StepNum()) {
|
|
iUpdateCnt = m_pGraph->TimeStepper.StepCount()
|
|
- m_pGraph->TimeStepper.StepNum();
|
|
} else {
|
|
iUpdateCnt = nBacklog;
|
|
}
|
|
|
|
// step to position of current data
|
|
locStepper = m_pGraph->TimeStepper;
|
|
for (i=0; i<iUpdateCnt; i++)
|
|
m_pGraph->TimeStepper.NextPosition();
|
|
|
|
if ( bPlotData ) {
|
|
StartUpdate(
|
|
hDC,
|
|
FALSE,
|
|
FALSE,
|
|
locStepper.Position(),
|
|
m_pGraph->TimeStepper.Position(),
|
|
TRUE );
|
|
|
|
DrawGrid(hDC, locStepper.Position(), m_pGraph->TimeStepper.Position());
|
|
|
|
PlotData(hDC, nBacklog, iUpdateCnt, &locStepper);
|
|
|
|
FinishUpdate ( hDC, FALSE );
|
|
}
|
|
|
|
if (m_pGraph->TimeStepper.StepNum() >= m_pGraph->TimeStepper.StepCount())
|
|
m_pGraph->TimeStepper.Reset();
|
|
|
|
nBacklog -= iUpdateCnt;
|
|
}
|
|
|
|
if ( bPlotData ) {
|
|
DrawTimeLine(hDC, m_pGraph->TimeStepper.Position());
|
|
}
|
|
|
|
m_pGraph->History.nBacklog = 0;
|
|
}
|
|
|
|
VOID
|
|
CGraphDisp::Update( HDC hDC )
|
|
{
|
|
DWORD dwPrevLayout = 0;
|
|
DWORD dwNewLayout = 0;
|
|
|
|
m_pCtrl->LockCounterData();
|
|
|
|
if ( NULL != hDC ) {
|
|
|
|
dwPrevLayout = GetLayout ( hDC );
|
|
dwNewLayout = dwPrevLayout;
|
|
|
|
if ( dwNewLayout & LAYOUT_RTL ) {
|
|
dwNewLayout &= ~LAYOUT_RTL;
|
|
SetLayout (hDC, dwNewLayout);
|
|
}
|
|
|
|
if ( ( m_rect.right > m_rect.left ) && ( m_rect.bottom > m_rect.top ) ) {
|
|
|
|
switch (m_pGraph->Options.iDisplayType) {
|
|
|
|
case LINE_GRAPH:
|
|
// Update the line graph and time bar based on history
|
|
// backlog. Reset history backlog to 0, signalling collection
|
|
// thread to post another WM_GRAPH_UPDATE message.
|
|
UpdateTimeBar ( hDC, TRUE );
|
|
break;
|
|
|
|
case BAR_GRAPH:
|
|
PlotBarGraph(hDC, TRUE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If updating histogram or report, update thetimebar step based on
|
|
// history backlog. Reset history backlog to 0, signalling collection
|
|
// thread to post another WM_GRAPH_UPDATE message.
|
|
UpdateTimeBar ( hDC, FALSE );
|
|
if ( dwNewLayout != dwPrevLayout ) {
|
|
SetLayout (hDC, dwPrevLayout);
|
|
}
|
|
}
|
|
m_pCtrl->UnlockCounterData();
|
|
}
|
|
|
|
|
|
|
|
void
|
|
CGraphDisp::StartUpdate(
|
|
HDC hDC,
|
|
BOOL fMetafile,
|
|
BOOL fEntire,
|
|
INT xLeft,
|
|
INT xRight,
|
|
BOOL bFill )
|
|
{
|
|
RECT rect;
|
|
|
|
// Preserve clipping region
|
|
|
|
if ( FALSE == fMetafile ) {
|
|
|
|
m_rgnClipSave = CreateRectRgn(0,0,0,0);
|
|
|
|
if (m_rgnClipSave != NULL) {
|
|
if (GetClipRgn(hDC, m_rgnClipSave) != 1) {
|
|
DeleteObject(m_rgnClipSave);
|
|
m_rgnClipSave = NULL;
|
|
}
|
|
}
|
|
|
|
xLeft += m_rectPlot.left;
|
|
xRight += m_rectPlot.left;
|
|
IntersectClipRect (
|
|
hDC,
|
|
max ( m_rectPlot.left, xLeft ),
|
|
m_rectPlot.top,
|
|
min (m_rectPlot.right, xRight + 1), // Extra pixel for TimeBar
|
|
m_rectPlot.bottom ) ;
|
|
|
|
} else if( TRUE == fEntire ){
|
|
m_rgnClipSave = NULL;
|
|
IntersectClipRect (
|
|
hDC,
|
|
m_rectPlot.left,
|
|
m_rectPlot.top,
|
|
m_rectPlot.right,
|
|
m_rectPlot.bottom ) ;
|
|
}
|
|
|
|
|
|
// Fill performed before this method for metafiles and complete draw.
|
|
if ( !fMetafile && bFill ) {
|
|
SetRect(
|
|
&rect,
|
|
max ( m_rectPlot.left, xLeft - 1 ),
|
|
m_rectPlot.top - 1,
|
|
min (m_rectPlot.right, xRight + 1),
|
|
m_rectPlot.bottom);
|
|
|
|
Fill(hDC, m_pCtrl->clrBackPlot(), &rect);
|
|
}
|
|
}
|
|
|
|
|
|
void CGraphDisp::FinishUpdate( HDC hDC, BOOL fMetafile )
|
|
{
|
|
// Restore saved clip region
|
|
if ( !fMetafile ) {
|
|
|
|
if (m_rgnClipSave != NULL) {
|
|
SelectClipRgn(hDC, m_rgnClipSave);
|
|
DeleteObject(m_rgnClipSave);
|
|
m_rgnClipSave = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CGraphDisp::DrawGrid(HDC hDC, INT xLeft, INT xRight)
|
|
{
|
|
INT xPos;
|
|
INT nTics;
|
|
INT *piScaleTic;
|
|
INT i;
|
|
|
|
|
|
if ( (m_pGraph->Options.bVertGridChecked)
|
|
|| (m_pGraph->Options.bHorzGridChecked) ) {
|
|
|
|
if ( m_clrCurrentGrid != m_pCtrl->clrGrid() ) {
|
|
|
|
m_clrCurrentGrid = m_pCtrl->clrGrid();
|
|
|
|
DeleteObject ( m_hPenGrid );
|
|
|
|
m_hPenGrid = CreatePen(PS_SOLID, 1, m_clrCurrentGrid );
|
|
|
|
// if can't do it, use a stock object (this can't fail)
|
|
if (m_hPenGrid == NULL)
|
|
m_hPenGrid = (HPEN)GetStockObject(BLACK_PEN);
|
|
}
|
|
}
|
|
|
|
if (m_pGraph->Options.bVertGridChecked) {
|
|
|
|
SelectObject(hDC, m_hPenGrid);
|
|
|
|
m_GridStepper.Reset();
|
|
xPos = m_GridStepper.NextPosition();
|
|
|
|
while (xPos < xLeft)
|
|
xPos =m_GridStepper.NextPosition();
|
|
|
|
while (xPos < xRight) {
|
|
MoveToEx(hDC, xPos + m_rectPlot.left, m_rectPlot.bottom, NULL);
|
|
LineTo(hDC, xPos + m_rectPlot.left, m_rectPlot.top - 1);
|
|
xPos = m_GridStepper.NextPosition();
|
|
}
|
|
}
|
|
|
|
if (m_pGraph->Options.bHorzGridChecked) {
|
|
xLeft += m_rectPlot.left;
|
|
xRight += m_rectPlot.left;
|
|
|
|
SelectObject(hDC,m_hPenGrid);
|
|
|
|
nTics = m_pGraph->Scale.GetTicPositions(&piScaleTic);
|
|
|
|
for (i=1; i<nTics; i++) {
|
|
MoveToEx(hDC, xLeft, m_rectPlot.top + piScaleTic[i], NULL);
|
|
LineTo(hDC, xRight + 1, m_rectPlot.top + piScaleTic[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CGraphDisp::CalcYPosition (
|
|
PCGraphItem pItem,
|
|
INT iHistIndex,
|
|
BOOL bLog,
|
|
INT y[3] )
|
|
{
|
|
BOOL bReturn; // True = good, False = bad.
|
|
PDH_STATUS stat;
|
|
DWORD dwCtrStat;
|
|
double dValue[3];
|
|
double dTemp;
|
|
INT iVal;
|
|
INT nVals = bLog ? 3 : 1;
|
|
|
|
if (bLog)
|
|
stat = pItem->GetLogEntry(iHistIndex, &dValue[1], &dValue[2], &dValue[0], &dwCtrStat);
|
|
else
|
|
stat = pItem->HistoryValue(iHistIndex, &dValue[0], &dwCtrStat);
|
|
|
|
if (ERROR_SUCCESS == stat && IsSuccessSeverity(dwCtrStat)) {
|
|
|
|
for (iVal = 0; iVal < nVals; iVal++) {
|
|
|
|
dTemp = dValue[iVal] * pItem->Scale();
|
|
|
|
if (dTemp > m_dMax)
|
|
dTemp = m_dMax;
|
|
else if (dTemp < m_dMin)
|
|
dTemp = m_dMin;
|
|
|
|
// Plot minimum value as 1 pixel above the bottom of the plot area, since
|
|
// clipping and fill regions crop the bottom and right pixels.
|
|
y[iVal] = m_rectPlot.bottom - (INT)((dTemp - m_dMin) * m_dPixelScale);
|
|
if ( y[iVal] == m_rectPlot.bottom ) {
|
|
y[iVal] = m_rectPlot.bottom - 1;
|
|
}
|
|
}
|
|
bReturn = TRUE;
|
|
} else {
|
|
bReturn = FALSE;
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
void CGraphDisp::PlotData(HDC hDC, INT iHistIndx, INT nSteps, CStepper *pStepper)
|
|
{
|
|
INT i;
|
|
INT x;
|
|
INT y[3];
|
|
PCGraphItem pItem;
|
|
CStepper locStepper;
|
|
BOOL bSkip;
|
|
BOOL bPrevGood;
|
|
BOOL bLog;
|
|
BOOL bLogMultiVal;
|
|
|
|
if (m_pGraph->Options.iVertMax <= m_pGraph->Options.iVertMin)
|
|
return;
|
|
|
|
bSkip = TRUE;
|
|
|
|
bLog = m_pCtrl->IsLogSource();
|
|
bLogMultiVal = bLog && !DisplaySingleLogSampleValue();
|
|
|
|
// If possible, back up to redraw previous segment
|
|
if (pStepper->StepNum() > 0) {
|
|
iHistIndx++;
|
|
nSteps++;
|
|
pStepper->PrevPosition();
|
|
}
|
|
|
|
// Set background color, in case of dashed lines
|
|
SetBkMode(hDC, TRANSPARENT);
|
|
|
|
pItem = m_pCtrl->FirstCounter();
|
|
while (pItem != NULL) {
|
|
locStepper = *pStepper;
|
|
|
|
// Skip hilited item the first time
|
|
if (!(pItem == m_pHiliteItem && bSkip)) {
|
|
INT iPolyIndex = 0;
|
|
POINT arrptDataPoints[MAX_GRAPH_SAMPLES] ;
|
|
|
|
if ( pItem == m_pHiliteItem) {
|
|
// Arbitrary 450 (out of 510) chosen as cutoff for white vs. black
|
|
if ( 450 > RGBToLightness( m_pCtrl->clrBackPlot() ) )
|
|
SelectObject(hDC, hPenWhite);
|
|
else
|
|
SelectObject(hDC, hPenBlack);
|
|
} else {
|
|
SelectObject(hDC,pItem->Pen());
|
|
}
|
|
|
|
bPrevGood = FALSE;
|
|
|
|
// For each GOOD current value:
|
|
// If the previous value is good, draw line from previous value to current value.
|
|
// If the previous value is bad, MoveTo the current value point.
|
|
//
|
|
// For the first step, the previous value is false by definition, so the first operation
|
|
// is a MoveTo.
|
|
|
|
//
|
|
// Polyline code:
|
|
// For each GOOD current value:
|
|
// Add the current (good) point to the polyline point array.
|
|
// For each BAD current value:
|
|
// If the polyline index is > 1 (2 points), draw the polyline and reset the polyline index to 0.
|
|
// After all values:
|
|
// If the polyline index is > 1 (2 points), draw the polyline.
|
|
|
|
for (i = 0; i <= nSteps; i++) {
|
|
|
|
// True = Good current value
|
|
if ( CalcYPosition ( pItem, iHistIndx - i, bLog, y ) ) {
|
|
|
|
x = m_rectPlot.left + locStepper.Position();
|
|
|
|
// Add point to polyline, since the current value is good.
|
|
arrptDataPoints[iPolyIndex].x = x;
|
|
arrptDataPoints[iPolyIndex].y = y[0];
|
|
iPolyIndex++;
|
|
|
|
// No polyline optimization for extra Max and Min log points.
|
|
if (bLogMultiVal) {
|
|
MoveToEx(hDC, x, y[1], NULL);
|
|
LineTo(hDC, x, y[2]);
|
|
MoveToEx(hDC, x, y[0], NULL);
|
|
}
|
|
|
|
bPrevGood = TRUE;
|
|
|
|
} else {
|
|
// Current value is not good.
|
|
bPrevGood = FALSE;
|
|
|
|
// Current value is not good, so don't add to polyline point array.
|
|
if ( iPolyIndex > 1 ) {
|
|
// Draw polyline for any existing good points.
|
|
Polyline(hDC, arrptDataPoints, iPolyIndex) ;
|
|
}
|
|
// Reset polyline point index to 0.
|
|
iPolyIndex = 0;
|
|
}
|
|
|
|
locStepper.NextPosition();
|
|
}
|
|
|
|
// Draw the final line.
|
|
if ( iPolyIndex > 1 ) {
|
|
// Draw polyline
|
|
Polyline(hDC, arrptDataPoints, iPolyIndex) ;
|
|
}
|
|
|
|
// Exit loop after plotting hilited item
|
|
if (pItem == m_pHiliteItem)
|
|
break;
|
|
}
|
|
|
|
pItem = pItem->Next();
|
|
|
|
// After last item, go back to highlighted item
|
|
if (pItem == NULL) {
|
|
pItem = m_pHiliteItem;
|
|
bSkip = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGraphDisp::PlotBarGraph(HDC hDC, BOOL fUpdate)
|
|
{
|
|
|
|
if ( (m_pGraph->CounterTree.NumCounters() > 0 )
|
|
&& (m_pGraph->Options.iVertMax > m_pGraph->Options.iVertMin) ) {
|
|
|
|
CStepper BarStepper;
|
|
PCGraphItem pItem;
|
|
RECT rectBar;
|
|
INT iValue,iPrevValue;
|
|
HRESULT hr;
|
|
LONG lCtrStat;
|
|
double dValue = 0.0;
|
|
double dMax;
|
|
double dMin;
|
|
double dAvg;
|
|
double dTemp;
|
|
HRGN hrgnRedraw,hrgnTemp;
|
|
eReportValueTypeConstant eValueType;
|
|
BOOL bLog;
|
|
INT iNumCounters = m_pGraph->CounterTree.NumCounters();
|
|
BOOL bSkip = TRUE;
|
|
INT iHighlightStepNum = 0;
|
|
BOOL bLocalUpdate;
|
|
HANDLE hPenSave;
|
|
|
|
bLocalUpdate = fUpdate;
|
|
|
|
hrgnRedraw = NULL;
|
|
|
|
eValueType = m_pCtrl->ReportValueType();
|
|
|
|
// Todo: Move DisplaySingleLogSampleValue() to CSystemMonitor.
|
|
bLog = m_pCtrl->IsLogSource();
|
|
|
|
// Force total redraw if the number of counters has changed in case
|
|
// Update is called immediately after.
|
|
if ( m_bBarConfigChanged ) {
|
|
SetBarConfigChanged ( FALSE );
|
|
if ( bLocalUpdate ) {
|
|
bLocalUpdate = FALSE;
|
|
}
|
|
// Clear and fill the entire plot region.
|
|
hrgnRedraw = CreateRectRgn(
|
|
m_rectPlot.left,
|
|
m_rectPlot.top,
|
|
m_rectPlot.right,
|
|
m_rectPlot.bottom);
|
|
|
|
if (hrgnRedraw) {
|
|
SelectClipRgn(hDC, hrgnRedraw);
|
|
Fill(hDC, m_pCtrl->clrBackPlot(), &m_rectPlot);
|
|
DrawGrid(hDC, 0, (m_rectPlot.right - m_rectPlot.left));
|
|
DeleteObject(hrgnRedraw);
|
|
hrgnRedraw = NULL;
|
|
}
|
|
}
|
|
|
|
// Intialize stepper for number of bars to plot
|
|
BarStepper.Init ( ( m_rectPlot.right - m_rectPlot.left), iNumCounters );
|
|
|
|
hPenSave = SelectPen ( hDC, GetStockObject(NULL_PEN) );
|
|
|
|
// Do for all counters
|
|
pItem = m_pGraph->CounterTree.FirstCounter();
|
|
while ( NULL != pItem ) {
|
|
|
|
hr = ERROR_SUCCESS;
|
|
|
|
// Skip highlighted item the first time through
|
|
|
|
if (!(pItem == m_pHiliteItem && bSkip)) {
|
|
|
|
// Get display value
|
|
if ( sysmonDefaultValue == eValueType ) {
|
|
if (bLog) {
|
|
hr = pItem->GetStatistics(&dMax, &dMin, &dAvg, &lCtrStat);
|
|
} else
|
|
hr = pItem->GetValue(&dValue, &lCtrStat);
|
|
} else {
|
|
|
|
if ( sysmonCurrentValue == eValueType ) {
|
|
hr = pItem->GetValue(&dValue, &lCtrStat);
|
|
} else {
|
|
double dAvg;
|
|
|
|
hr = pItem->GetStatistics(&dMax, &dMin, &dAvg, &lCtrStat);
|
|
|
|
switch ( eValueType ) {
|
|
|
|
case sysmonAverage:
|
|
dValue = dAvg;
|
|
break;
|
|
|
|
case sysmonMinimum:
|
|
dValue = dMin;
|
|
break;
|
|
|
|
case sysmonMaximum:
|
|
dValue = dMax;
|
|
break;
|
|
|
|
default:
|
|
assert (FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Erase bar if the counter value is invalid.
|
|
if (SUCCEEDED(hr) && IsSuccessSeverity(lCtrStat)) {
|
|
// Convert value to pixel units
|
|
dTemp = dValue * pItem->Scale();
|
|
|
|
if (dTemp > m_dMax)
|
|
dTemp = m_dMax;
|
|
else if (dTemp < m_dMin)
|
|
dTemp = m_dMin;
|
|
|
|
iValue = m_rectPlot.bottom - (INT)((dTemp - m_dMin) * m_dPixelScale);
|
|
if ( iValue == m_rectPlot.bottom ) {
|
|
// Draw single pixel for screen visibility.
|
|
iValue--;
|
|
}
|
|
} else {
|
|
// The current value is 0. Draw single pixel for screen visibility.
|
|
iValue = m_rectPlot.bottom - 1;
|
|
}
|
|
|
|
if ( !bSkip ) {
|
|
assert ( pItem == m_pHiliteItem );
|
|
BarStepper.StepTo ( iHighlightStepNum );
|
|
}
|
|
|
|
// Setup left and right edges of bar
|
|
rectBar.left = m_rectPlot.left + BarStepper.Position();
|
|
rectBar.right = m_rectPlot.left + BarStepper.NextPosition();
|
|
|
|
// If doing an update (never called for log sources) and not drawing the highlighted item
|
|
if ( bLocalUpdate && !( ( pItem == m_pHiliteItem ) && !bSkip) ) {
|
|
|
|
assert ( !m_bBarConfigChanged );
|
|
|
|
// Get previous plot value
|
|
iPrevValue = 0;
|
|
hr = pItem->HistoryValue(1, &dValue, (ULONG*)&lCtrStat);
|
|
if (SUCCEEDED(hr) && IsSuccessSeverity(lCtrStat)) {
|
|
|
|
// Convert value to pixel units
|
|
dTemp = dValue * pItem->Scale();
|
|
|
|
if (dTemp > m_dMax)
|
|
dTemp = m_dMax;
|
|
else if (dTemp < m_dMin)
|
|
dTemp = m_dMin;
|
|
|
|
iPrevValue = m_rectPlot.bottom - (INT)((dTemp - m_dMin) * m_dPixelScale);
|
|
if ( iPrevValue == m_rectPlot.bottom ) {
|
|
// Single pixel was drawn for screen visibility.
|
|
iPrevValue--;
|
|
}
|
|
} else {
|
|
// The previous value was 0. Single pixel was drawn for screen visibility.
|
|
iPrevValue = m_rectPlot.bottom - 1;
|
|
}
|
|
|
|
// If bar has grown (smaller y coord)
|
|
if (iPrevValue > iValue) {
|
|
|
|
// Draw the new part
|
|
rectBar.bottom = iPrevValue;
|
|
rectBar.top = iValue;
|
|
|
|
if ( pItem == m_pHiliteItem) {
|
|
// Arbitrary 450 (out of 510) chosen as cutoff for white vs. black
|
|
if ( 450 > RGBToLightness( m_pCtrl->clrBackPlot() ) )
|
|
SelectBrush(hDC, GetStockObject(WHITE_BRUSH));
|
|
else
|
|
SelectBrush(hDC, GetStockObject(BLACK_BRUSH));
|
|
} else {
|
|
SelectBrush(hDC, pItem->Brush());
|
|
}
|
|
// Bars are drawn with Null pen, so bottom and right are cropped by 1 pixel.
|
|
// Add 1 pixel to compensate.
|
|
Rectangle(hDC, rectBar.left, rectBar.top, rectBar.right + 1, rectBar.bottom + 1);
|
|
|
|
} else if (iPrevValue < iValue) {
|
|
|
|
// Else if bar has shrunk
|
|
|
|
// Add part to be erased to redraw region
|
|
// Erase to the top of the grid, to eliminate random pixels left over.
|
|
rectBar.bottom = iValue;
|
|
rectBar.top = m_rectPlot.top; // set to stop of grid rather than to prevValue
|
|
|
|
hrgnTemp = CreateRectRgn(rectBar.left, rectBar.top, rectBar.right, rectBar.bottom);
|
|
if (hrgnRedraw && hrgnTemp) {
|
|
CombineRgn(hrgnRedraw,hrgnRedraw,hrgnTemp,RGN_OR);
|
|
DeleteObject(hrgnTemp);
|
|
} else {
|
|
hrgnRedraw = hrgnTemp;
|
|
}
|
|
}
|
|
} else {
|
|
// Erase and redraw complete bar
|
|
|
|
// Erase top first
|
|
// Add part to be erased to redraw region
|
|
// Erase to the top of the grid, to eliminate random pixels left over.
|
|
if ( iValue != m_rectPlot.top ) {
|
|
rectBar.bottom = iValue;
|
|
rectBar.top = m_rectPlot.top; // set to stop of grid rather than to prevValue
|
|
|
|
hrgnTemp = CreateRectRgn(rectBar.left, rectBar.top, rectBar.right, rectBar.bottom);
|
|
if (hrgnRedraw && hrgnTemp) {
|
|
CombineRgn(hrgnRedraw,hrgnRedraw,hrgnTemp,RGN_OR);
|
|
DeleteObject(hrgnTemp);
|
|
} else {
|
|
hrgnRedraw = hrgnTemp;
|
|
}
|
|
}
|
|
|
|
// Then draw the bar.
|
|
rectBar.bottom = m_rectPlot.bottom;
|
|
rectBar.top = iValue;
|
|
|
|
if ( pItem == m_pHiliteItem) {
|
|
// Arbitrary 450 (out of 510) chosen as cutoff for white vs. black
|
|
if ( 450 > RGBToLightness( m_pCtrl->clrBackPlot() ) )
|
|
SelectBrush(hDC, GetStockObject(WHITE_BRUSH));
|
|
else
|
|
SelectBrush(hDC, GetStockObject(BLACK_BRUSH));
|
|
} else {
|
|
SelectBrush(hDC, pItem->Brush());
|
|
}
|
|
// Bars are drawn with Null pen, so bottom and right are cropped by 1 pixel.
|
|
// Add 1 pixel to compensate.
|
|
Rectangle(hDC, rectBar.left, rectBar.top, rectBar.right + 1, rectBar.bottom + 1);
|
|
} // Update
|
|
|
|
// Exit loop after plotting highlighted item
|
|
if (pItem == m_pHiliteItem)
|
|
break;
|
|
|
|
} else {
|
|
if ( bSkip ) {
|
|
// Save position of highlighted item the first time through
|
|
iHighlightStepNum = BarStepper.StepNum();
|
|
}
|
|
BarStepper.NextPosition();
|
|
}
|
|
|
|
pItem = pItem->Next();
|
|
|
|
// After last item, go back to highlighted item
|
|
if ( NULL == pItem && NULL != m_pHiliteItem ) {
|
|
pItem = m_pHiliteItem;
|
|
bSkip = FALSE;
|
|
}
|
|
|
|
} // Do for all counters
|
|
|
|
// If redraw region accumulated, erase and draw grid lines
|
|
if (hrgnRedraw) {
|
|
SelectClipRgn(hDC, hrgnRedraw);
|
|
Fill(hDC, m_pCtrl->clrBackPlot(), &m_rectPlot);
|
|
DrawGrid(hDC, 0, (m_rectPlot.right - m_rectPlot.left));
|
|
DeleteObject(hrgnRedraw);
|
|
}
|
|
SelectObject(hDC, hPenSave);
|
|
}
|
|
}
|
|
|
|
|
|
void CGraphDisp::SizeComponents(HDC hDC, PRECT pRect)
|
|
{
|
|
INT iStepNum;
|
|
INT iScaleWidth;
|
|
INT iTitleHeight;
|
|
INT iAxisTitleWidth;
|
|
RECT rectScale;
|
|
SIZE size;
|
|
INT iWidth;
|
|
INT i;
|
|
|
|
static INT aiWidthTable[] = {20,50,100,150,300,500,1000000};
|
|
static INT aiTicTable[] = {0,2,4,5,10,20,25};
|
|
|
|
m_rect = *pRect;
|
|
|
|
// if no space, return
|
|
if (m_rect.right <= m_rect.left || m_rect.bottom - m_rect.top <= 0)
|
|
return;
|
|
|
|
// For now use the horizontal font height for both horizontal and vertical text
|
|
// because the GetTextExtentPoint32 is returning the wrong height for vertical text
|
|
SelectFont(hDC, m_pCtrl->Font());
|
|
GetTextExtentPoint32(hDC, TEXT("Sample"), 6, &size);
|
|
|
|
if (m_pGraph->Options.pszGraphTitle != NULL) {
|
|
//SelectFont(hDC, m_pCtrl->Font()) ;
|
|
//GetTextExtentPoint32(hDC, m_pGraph->Options.pszGraphTitle,
|
|
// lstrlen(m_pGraph->Options.pszGraphTitle), &size);
|
|
iTitleHeight = size.cy + TEXT_MARGIN;
|
|
} else
|
|
iTitleHeight = 0;
|
|
|
|
if (m_pGraph->Options.pszYaxisTitle != NULL && m_hFontVertical != NULL) {
|
|
//SelectFont(hDC, m_hFontVertical);
|
|
//GetTextExtentPoint32(hDC, m_pGraph->Options.pszYaxisTitle,
|
|
// lstrlen(m_pGraph->Options.pszYaxisTitle), &size);
|
|
|
|
iAxisTitleWidth = size.cy + TEXT_MARGIN;
|
|
} else
|
|
iAxisTitleWidth = 0;
|
|
|
|
if (m_pGraph->Options.bLabelsChecked) {
|
|
//SelectFont(hDC, m_pCtrl->Font());
|
|
iScaleWidth = m_pGraph->Scale.GetWidth(hDC);
|
|
} else
|
|
iScaleWidth = 0;
|
|
|
|
SetRect(&rectScale, pRect->left + iAxisTitleWidth,
|
|
pRect->top + iTitleHeight,
|
|
pRect->left + iAxisTitleWidth + iScaleWidth,
|
|
pRect->bottom);
|
|
|
|
m_pGraph->Scale.SetRect(&rectScale); // Just to set grid line positions
|
|
|
|
SetRect(&m_rectPlot, pRect->left + iScaleWidth + iAxisTitleWidth + BORDER,
|
|
pRect->top + iTitleHeight + BORDER,
|
|
pRect->right - BORDER,
|
|
pRect->bottom - BORDER);
|
|
|
|
// Reinitialize steppers for new width
|
|
iWidth = m_rectPlot.right - m_rectPlot.left;
|
|
|
|
iStepNum = m_pGraph->TimeStepper.StepNum();
|
|
m_pGraph->TimeStepper.Init(iWidth, m_pGraph->History.nMaxSamples - 2);
|
|
m_pGraph->TimeStepper.StepTo(iStepNum);
|
|
|
|
iStepNum = m_pGraph->LogViewStartStepper.StepNum();
|
|
m_pGraph->LogViewStartStepper.Init(iWidth, m_pGraph->History.nMaxSamples - 2);
|
|
m_pGraph->LogViewStartStepper.StepTo(iStepNum);
|
|
|
|
iStepNum = m_pGraph->LogViewStopStepper.StepNum();
|
|
m_pGraph->LogViewStopStepper.Init(iWidth, m_pGraph->History.nMaxSamples - 2);
|
|
m_pGraph->LogViewStopStepper.StepTo(iStepNum);
|
|
|
|
// Find best grid count for this width
|
|
for (i=0; iWidth > aiWidthTable[i]; i++) {};
|
|
|
|
m_GridStepper.Init(iWidth, aiTicTable[i]);
|
|
|
|
// Compute conversion factors for plot, hit test.
|
|
m_dMin = (double)m_pGraph->Options.iVertMin;
|
|
m_dMax = (double)m_pGraph->Options.iVertMax;
|
|
|
|
m_dPixelScale = (double)(m_rectPlot.bottom - m_rectPlot.top) / (m_dMax - m_dMin);
|
|
|
|
}
|
|
|
|
|
|
void CGraphDisp::DrawTimeLine(HDC hDC, INT x)
|
|
{
|
|
HPEN hPenSave;
|
|
|
|
// No time line for log playback
|
|
if (m_pCtrl->IsLogSource())
|
|
return;
|
|
|
|
x += m_rectPlot.left + 1;
|
|
|
|
if ( m_clrCurrentTimeBar != m_pCtrl->clrTimeBar() ) {
|
|
LOGBRUSH lbrush;
|
|
|
|
m_clrCurrentTimeBar = m_pCtrl->clrTimeBar();
|
|
|
|
DeleteObject ( m_hPenTimeBar );
|
|
|
|
// When called from Update(), DrawTimeLine is called after the clipping region
|
|
// is deactivated. Create a geometric pen in order to specify flat end cap style.
|
|
// This eliminates any extra pixels drawn at the end.
|
|
|
|
lbrush.lbStyle = BS_SOLID;
|
|
lbrush.lbColor = m_clrCurrentTimeBar;
|
|
lbrush.lbHatch = 0;
|
|
|
|
m_hPenTimeBar = ExtCreatePen (
|
|
PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT, 2, &lbrush, 0, NULL );
|
|
|
|
// if can't do it, use a stock object (this can't fail)
|
|
if (m_hPenTimeBar == NULL)
|
|
m_hPenTimeBar = (HPEN)GetStockObject(BLACK_PEN);
|
|
}
|
|
|
|
hPenSave = SelectPen ( hDC, m_hPenTimeBar );
|
|
MoveToEx ( hDC, x, m_rectPlot.top, NULL );
|
|
|
|
// Specify 1 less pixel. All fills and clip regions clip bottom and
|
|
// right pixels, so match their behavior.
|
|
LineTo ( hDC, x, m_rectPlot.bottom - 1 );
|
|
|
|
SelectObject(hDC, hPenSave);
|
|
}
|
|
|
|
void CGraphDisp::DrawStartStopLine(HDC hDC, INT x)
|
|
{
|
|
HPEN hPenSave;
|
|
|
|
// Log view start/stop lines only for log playback
|
|
if (!m_pCtrl->IsLogSource())
|
|
return;
|
|
|
|
if ( x > 0 && x < ( m_rectPlot.right - m_rectPlot.left ) ) {
|
|
x += m_rectPlot.left;
|
|
|
|
if ( m_clrCurrentGrid != m_pCtrl->clrGrid() ) {
|
|
|
|
m_clrCurrentGrid = m_pCtrl->clrGrid();
|
|
|
|
DeleteObject ( m_hPenGrid );
|
|
|
|
m_hPenGrid = CreatePen(PS_SOLID, 1, m_clrCurrentGrid );
|
|
|
|
// if can't do it, use a stock object (this can't fail)
|
|
if (m_hPenGrid == NULL)
|
|
m_hPenGrid = (HPEN)GetStockObject(BLACK_PEN);
|
|
}
|
|
|
|
hPenSave = SelectPen(hDC, m_hPenGrid);
|
|
MoveToEx(hDC, x, m_rectPlot.top, NULL);
|
|
LineTo(hDC, x, m_rectPlot.bottom);
|
|
|
|
SelectObject(hDC, hPenSave);
|
|
}
|
|
}
|
|
|
|
void CGraphDisp::ChangeFont( HDC hDC )
|
|
{
|
|
TEXTMETRIC TextMetrics, newTextMetrics;
|
|
LOGFONT LogFont;
|
|
HFONT hFontOld;
|
|
|
|
// Select the new font
|
|
hFontOld = SelectFont(hDC, m_pCtrl->Font());
|
|
|
|
// Get attributes
|
|
GetTextMetrics(hDC, &TextMetrics);
|
|
|
|
// Create LOGFONT for vertical font with same attributes
|
|
LogFont.lfHeight = TextMetrics.tmHeight;
|
|
LogFont.lfWidth = 0;
|
|
LogFont.lfOrientation = LogFont.lfEscapement = 90*10;
|
|
LogFont.lfWeight = TextMetrics.tmWeight;
|
|
LogFont.lfStrikeOut = TextMetrics.tmStruckOut;
|
|
LogFont.lfUnderline = TextMetrics.tmUnderlined;
|
|
LogFont.lfItalic = TextMetrics.tmItalic;
|
|
LogFont.lfCharSet = TextMetrics.tmCharSet;
|
|
LogFont.lfPitchAndFamily = (BYTE)(TextMetrics.tmPitchAndFamily & 0xF0);
|
|
|
|
GetTextFace(hDC, LF_FACESIZE, LogFont.lfFaceName);
|
|
|
|
// Force a truetype font, because raster fonts can't rotate
|
|
LogFont.lfOutPrecision = OUT_TT_ONLY_PRECIS;
|
|
LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
|
LogFont.lfQuality = DEFAULT_QUALITY;
|
|
|
|
// Release the current font
|
|
if (m_hFontVertical != NULL)
|
|
DeleteObject(m_hFontVertical);
|
|
|
|
// Create the font and save handle locally
|
|
m_hFontVertical = CreateFontIndirect(&LogFont);
|
|
|
|
SelectFont(hDC, m_hFontVertical);
|
|
GetTextMetrics(hDC, &newTextMetrics);
|
|
|
|
SelectFont(hDC, hFontOld);
|
|
}
|
|
|
|
PCGraphItem
|
|
CGraphDisp::GetItemInLineGraph ( SHORT xPos, SHORT yPos )
|
|
{
|
|
PCGraphItem pItem = NULL;
|
|
PCGraphItem pReturn = NULL;
|
|
|
|
INT iPrevStepNum;
|
|
POINT ptPrev;
|
|
POINT ptNext;
|
|
POINTS ptMouse;
|
|
CStepper locStepper;
|
|
|
|
INT iHistIndex;
|
|
BOOL bLog;
|
|
BOOL bLogMultiVal;
|
|
BOOL bFound = FALSE;
|
|
|
|
INT yPosPrev[3];
|
|
INT yPosNext[3];
|
|
|
|
pItem = m_pCtrl->FirstCounter();
|
|
bLog = m_pCtrl->IsLogSource();
|
|
bLogMultiVal = bLog && !DisplaySingleLogSampleValue();
|
|
|
|
// Items exist?
|
|
if (pItem != NULL) {
|
|
|
|
locStepper = m_pGraph->TimeStepper;
|
|
locStepper.Reset();
|
|
|
|
iPrevStepNum = locStepper.PrevStepNum(xPos - m_rectPlot.left);
|
|
locStepper.StepTo(iPrevStepNum);
|
|
|
|
ptPrev.x = m_rectPlot.left + locStepper.Position();
|
|
ptNext.x = m_rectPlot.left + locStepper.NextPosition();
|
|
|
|
ptMouse.x = xPos;
|
|
ptMouse.y = yPos;
|
|
|
|
// Item within rectangle?
|
|
if ( iPrevStepNum > -1 ) {
|
|
|
|
// Determine the history index of the preceding step.
|
|
|
|
if ( iPrevStepNum <= m_pGraph->TimeStepper.StepNum() ) {
|
|
iHistIndex = m_pGraph->TimeStepper.StepNum() - iPrevStepNum;
|
|
} else {
|
|
iHistIndex = m_pGraph->TimeStepper.StepNum()
|
|
+ (m_pGraph->TimeStepper.StepCount() - iPrevStepNum);
|
|
}
|
|
|
|
while ( (pItem != NULL) && !bFound ) {
|
|
|
|
// Calculate y position of this value to compare against
|
|
// y position of hit point.
|
|
if ( CalcYPosition ( pItem, iHistIndex, bLog, yPosPrev ) ) {
|
|
|
|
if ( iPrevStepNum < locStepper.StepCount() ) {
|
|
|
|
if ( CalcYPosition ( pItem, iHistIndex - 1, bLog, yPosNext ) ) {
|
|
|
|
ptPrev.y = yPosPrev[0];
|
|
ptNext.y = yPosNext[0];
|
|
|
|
bFound = HitTestLine( ptPrev, ptNext, ptMouse, eHitRegion );
|
|
|
|
// For log files, also check the vertical line from min to max
|
|
// for the closest step.
|
|
if ( !bFound && bLogMultiVal ) {
|
|
INT iTemp = ptNext.x - ptPrev.x;
|
|
|
|
iTemp = iTemp / 2;
|
|
|
|
if ( ptMouse.x <= ( ptPrev.x + iTemp/2 ) ) {
|
|
|
|
bFound = (( yPosPrev[2] - eHitRegion < yPos )
|
|
&& ( yPos < yPosPrev[1] + eHitRegion ));
|
|
} else {
|
|
|
|
bFound = (( yPosNext[2] - eHitRegion < yPos )
|
|
&& ( yPos < yPosNext[1] + eHitRegion ));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
|
|
// At the end, so just check the final point.
|
|
|
|
if ( !bLogMultiVal ) {
|
|
bFound = (( yPosPrev[0] - eHitRegion < yPos )
|
|
&& ( yPos < yPosPrev[0] + eHitRegion ));
|
|
} else {
|
|
bFound = (( yPosPrev[2] - eHitRegion < yPos )
|
|
&& ( yPos < yPosPrev[1] + eHitRegion ));
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bFound )
|
|
pReturn = pItem;
|
|
else
|
|
pItem = pItem->Next();
|
|
}
|
|
}
|
|
}
|
|
|
|
return pReturn;
|
|
}
|
|
|
|
PCGraphItem
|
|
CGraphDisp::GetItemInBarGraph ( SHORT xPos, SHORT /* yPos */ )
|
|
{
|
|
PCGraphItem pItem = NULL;
|
|
|
|
pItem = m_pCtrl->FirstCounter();
|
|
|
|
// Items exist?
|
|
if (pItem != NULL) {
|
|
|
|
CStepper BarStepper;
|
|
INT iNumCounters = m_pGraph->CounterTree.NumCounters();
|
|
INT iCount;
|
|
INT iHitStep;
|
|
|
|
// Intialize stepper for number of bars in plot
|
|
BarStepper.Init ( ( m_rectPlot.right - m_rectPlot.left), iNumCounters );
|
|
iHitStep = BarStepper.PrevStepNum ( xPos - m_rectPlot.left );
|
|
|
|
assert ( -1 != iHitStep );
|
|
|
|
// Find the counter displayed in the hit step.
|
|
for ( iCount = 0;
|
|
( iCount < iHitStep ) && ( pItem != NULL );
|
|
iCount++ ) {
|
|
|
|
pItem = pItem->Next();
|
|
}
|
|
}
|
|
|
|
return pItem;
|
|
}
|
|
|
|
PCGraphItem
|
|
CGraphDisp::GetItem( INT xPos, INT yPos )
|
|
{
|
|
PCGraphItem pReturn = NULL;
|
|
|
|
if ( ( m_pGraph->Options.iVertMax > m_pGraph->Options.iVertMin)
|
|
&& ( yPos >= m_rectPlot.top ) && ( yPos <= m_rectPlot.bottom )
|
|
&& ( xPos >= m_rectPlot.left ) && ( xPos <= m_rectPlot.right ) ) {
|
|
|
|
m_pCtrl->LockCounterData();
|
|
|
|
if ( LINE_GRAPH == m_pGraph->Options.iDisplayType ) {
|
|
assert ( SHRT_MAX >= xPos );
|
|
assert ( SHRT_MAX >= yPos );
|
|
|
|
pReturn = GetItemInLineGraph( (SHORT)xPos, (SHORT)yPos );
|
|
} else if ( BAR_GRAPH == m_pGraph->Options.iDisplayType ) {
|
|
assert ( SHRT_MAX >= xPos );
|
|
assert ( SHRT_MAX >= yPos );
|
|
|
|
pReturn = GetItemInBarGraph( (SHORT)xPos, (SHORT)yPos );
|
|
}
|
|
|
|
m_pCtrl->UnlockCounterData();
|
|
|
|
}
|
|
|
|
return pReturn;
|
|
}
|