1333 lines
39 KiB
C
1333 lines
39 KiB
C
/*
|
|
* Process Hacker -
|
|
* graph control
|
|
*
|
|
* Copyright (C) 2010-2016 wj32
|
|
*
|
|
* This file is part of Process Hacker.
|
|
*
|
|
* Process Hacker is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Process Hacker is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <ph.h>
|
|
#include <guisup.h>
|
|
#include <graph.h>
|
|
#include <math.h>
|
|
|
|
#define COLORREF_TO_BITS(Color) (_byteswap_ulong(Color) >> 8)
|
|
|
|
typedef struct _PHP_GRAPH_CONTEXT
|
|
{
|
|
HWND Handle;
|
|
ULONG Style;
|
|
ULONG_PTR Id;
|
|
PH_GRAPH_DRAW_INFO DrawInfo;
|
|
PH_GRAPH_OPTIONS Options;
|
|
BOOLEAN NeedsUpdate;
|
|
BOOLEAN NeedsDraw;
|
|
|
|
HDC BufferedContext;
|
|
HBITMAP BufferedOldBitmap;
|
|
HBITMAP BufferedBitmap;
|
|
PVOID BufferedBits;
|
|
RECT BufferedContextRect;
|
|
|
|
HDC FadeOutContext;
|
|
HBITMAP FadeOutOldBitmap;
|
|
HBITMAP FadeOutBitmap;
|
|
PVOID FadeOutBits;
|
|
RECT FadeOutContextRect;
|
|
|
|
HWND TooltipHandle;
|
|
WNDPROC TooltipOldWndProc;
|
|
POINT LastCursorLocation;
|
|
} PHP_GRAPH_CONTEXT, *PPHP_GRAPH_CONTEXT;
|
|
|
|
LRESULT CALLBACK PhpGraphWndProc(
|
|
_In_ HWND hwnd,
|
|
_In_ UINT uMsg,
|
|
_In_ WPARAM wParam,
|
|
_In_ LPARAM lParam
|
|
);
|
|
|
|
RECT PhNormalGraphTextMargin = { 5, 5, 5, 5 };
|
|
RECT PhNormalGraphTextPadding = { 3, 3, 3, 3 };
|
|
|
|
BOOLEAN PhGraphControlInitialization(
|
|
VOID
|
|
)
|
|
{
|
|
WNDCLASSEX c = { sizeof(c) };
|
|
|
|
c.style = CS_GLOBALCLASS | CS_DBLCLKS;
|
|
c.lpfnWndProc = PhpGraphWndProc;
|
|
c.cbClsExtra = 0;
|
|
c.cbWndExtra = sizeof(PVOID);
|
|
c.hInstance = PhLibImageBase;
|
|
c.hIcon = NULL;
|
|
c.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
c.hbrBackground = NULL;
|
|
c.lpszMenuName = NULL;
|
|
c.lpszClassName = PH_GRAPH_CLASSNAME;
|
|
c.hIconSm = NULL;
|
|
|
|
if (!RegisterClassEx(&c))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
FORCEINLINE VOID PhpGetGraphPoint(
|
|
_In_ PPH_GRAPH_DRAW_INFO DrawInfo,
|
|
_In_ ULONG Index,
|
|
_Out_ PULONG H1,
|
|
_Out_ PULONG H2
|
|
)
|
|
{
|
|
if (Index < DrawInfo->LineDataCount)
|
|
{
|
|
FLOAT f1;
|
|
FLOAT f2;
|
|
|
|
f1 = DrawInfo->LineData1[Index];
|
|
|
|
if (f1 < 0)
|
|
f1 = 0;
|
|
if (f1 > 1)
|
|
f1 = 1;
|
|
|
|
*H1 = (ULONG)(f1 * (DrawInfo->Height - 1));
|
|
|
|
if (DrawInfo->Flags & PH_GRAPH_USE_LINE_2)
|
|
{
|
|
f2 = f1 + DrawInfo->LineData2[Index];
|
|
|
|
if (f2 < 0)
|
|
f2 = 0;
|
|
if (f2 > 1)
|
|
f2 = 1;
|
|
|
|
*H2 = (ULONG)(f2 * (DrawInfo->Height - 1));
|
|
}
|
|
else
|
|
{
|
|
*H2 = *H1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*H1 = 0;
|
|
*H2 = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draws a graph directly to memory.
|
|
*
|
|
* \param hdc The DC to draw to. This is only used when drawing text.
|
|
* \param Bits The bits in a bitmap.
|
|
* \param DrawInfo A structure which contains graphing information.
|
|
*
|
|
* \remarks The following information is fixed:
|
|
* \li The graph is fixed to the origin (0, 0).
|
|
* \li The total size of the bitmap is assumed to be \a Width and \a Height in \a DrawInfo.
|
|
* \li \a Step is fixed at 2.
|
|
* \li If \ref PH_GRAPH_USE_LINE_2 is specified in \a Flags, \ref PH_GRAPH_OVERLAY_LINE_2 is never
|
|
* used.
|
|
*/
|
|
VOID PhDrawGraphDirect(
|
|
_In_ HDC hdc,
|
|
_In_ PVOID Bits,
|
|
_In_ PPH_GRAPH_DRAW_INFO DrawInfo
|
|
)
|
|
{
|
|
PULONG bits = Bits;
|
|
LONG width = DrawInfo->Width;
|
|
LONG height = DrawInfo->Height;
|
|
LONG numberOfPixels = width * height;
|
|
ULONG flags = DrawInfo->Flags;
|
|
LONG i;
|
|
LONG x;
|
|
|
|
BOOLEAN intermediate = FALSE; // whether we are currently between two data positions
|
|
ULONG dataIndex = 0; // the data index of the current position
|
|
ULONG h1_i; // the line 1 height value to the left of the current position
|
|
ULONG h1_o; // the line 1 height value at the current position
|
|
ULONG h2_i; // the line 1 + line 2 height value to the left of the current position
|
|
ULONG h2_o; // the line 1 + line 2 height value at the current position
|
|
ULONG h1; // current pixel
|
|
ULONG h1_left; // current pixel
|
|
ULONG h2; // current pixel
|
|
ULONG h2_left; // current pixel
|
|
|
|
LONG mid;
|
|
LONG h1_low1;
|
|
LONG h1_high1;
|
|
LONG h1_low2;
|
|
LONG h1_high2;
|
|
LONG h2_low1;
|
|
LONG h2_high1;
|
|
LONG h2_low2;
|
|
LONG h2_high2;
|
|
LONG old_low2;
|
|
LONG old_high2;
|
|
|
|
ULONG lineColor1;
|
|
ULONG lineBackColor1;
|
|
ULONG lineColor2;
|
|
ULONG lineBackColor2;
|
|
FLOAT gridHeight;
|
|
LONG gridYThreshold;
|
|
ULONG gridYCounter;
|
|
ULONG gridColor;
|
|
FLOAT gridBase;
|
|
FLOAT gridLevel;
|
|
|
|
ULONG yLabelMax;
|
|
ULONG yLabelDataIndex;
|
|
|
|
lineColor1 = COLORREF_TO_BITS(DrawInfo->LineColor1);
|
|
lineBackColor1 = COLORREF_TO_BITS(DrawInfo->LineBackColor1);
|
|
lineColor2 = COLORREF_TO_BITS(DrawInfo->LineColor2);
|
|
lineBackColor2 = COLORREF_TO_BITS(DrawInfo->LineBackColor2);
|
|
|
|
if (DrawInfo->BackColor == 0)
|
|
{
|
|
memset(bits, 0, numberOfPixels * 4);
|
|
}
|
|
else
|
|
{
|
|
PhFillMemoryUlong(bits, COLORREF_TO_BITS(DrawInfo->BackColor), numberOfPixels);
|
|
}
|
|
|
|
x = width - 1;
|
|
h1_low2 = MAXLONG;
|
|
h1_high2 = 0;
|
|
h2_low2 = MAXLONG;
|
|
h2_high2 = 0;
|
|
|
|
PhpGetGraphPoint(DrawInfo, 0, &h1_i, &h2_i);
|
|
|
|
if (flags & (PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y))
|
|
{
|
|
gridHeight = max(DrawInfo->GridHeight, 0);
|
|
gridLevel = gridHeight;
|
|
gridYThreshold = DrawInfo->GridYThreshold;
|
|
gridYCounter = DrawInfo->GridWidth - (DrawInfo->GridXOffset * DrawInfo->Step) % DrawInfo->GridWidth - 1;
|
|
gridColor = COLORREF_TO_BITS(DrawInfo->GridColor);
|
|
}
|
|
|
|
if ((flags & (PH_GRAPH_USE_GRID_Y | PH_GRAPH_LOGARITHMIC_GRID_Y)) == (PH_GRAPH_USE_GRID_Y | PH_GRAPH_LOGARITHMIC_GRID_Y))
|
|
{
|
|
// Pre-process to find the largest integer n such that GridHeight*GridBase^n < 1.
|
|
|
|
gridBase = DrawInfo->GridBase;
|
|
|
|
if (gridBase > 1)
|
|
{
|
|
DOUBLE logBase;
|
|
DOUBLE exponent;
|
|
DOUBLE high;
|
|
|
|
logBase = log(gridBase);
|
|
exponent = ceil(-log(gridHeight) / logBase) - 1; // Works for both GridHeight > 1 and GridHeight < 1
|
|
high = exp(exponent * logBase);
|
|
gridLevel = (FLOAT)(gridHeight * high);
|
|
|
|
if (gridLevel < 0 || !isfinite(gridLevel))
|
|
gridLevel = 0;
|
|
if (gridLevel > 1)
|
|
gridLevel = 1;
|
|
}
|
|
else
|
|
{
|
|
// This is an error.
|
|
gridLevel = 0;
|
|
}
|
|
}
|
|
|
|
if (flags & PH_GRAPH_LABEL_MAX_Y)
|
|
{
|
|
yLabelMax = h2_i;
|
|
yLabelDataIndex = 0;
|
|
}
|
|
|
|
while (x >= 0)
|
|
{
|
|
// Calculate the height of the graph at this point.
|
|
|
|
if (!intermediate)
|
|
{
|
|
h1_o = h1_i;
|
|
h2_o = h2_i;
|
|
|
|
// Pull in new data.
|
|
dataIndex++;
|
|
PhpGetGraphPoint(DrawInfo, dataIndex, &h1_i, &h2_i);
|
|
|
|
h1 = h1_o;
|
|
h1_left = (h1_i + h1_o) / 2;
|
|
h2 = h2_o;
|
|
h2_left = (h2_i + h2_o) / 2;
|
|
|
|
if ((flags & PH_GRAPH_LABEL_MAX_Y) && dataIndex < DrawInfo->LabelMaxYIndexLimit)
|
|
{
|
|
if (yLabelMax <= h2_i)
|
|
{
|
|
yLabelMax = h2_i;
|
|
yLabelDataIndex = dataIndex;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
h1 = h1_left;
|
|
h1_left = h1_i;
|
|
h2 = h2_left;
|
|
h2_left = h2_i;
|
|
}
|
|
|
|
// The graph is drawn right-to-left. There is one iteration of the loop per horizontal pixel.
|
|
// There is a fixed step value of 2, so every other iteration is a mid-point (intermediate)
|
|
// iteration with a height value of (left + right) / 2. In order to rasterize the outline,
|
|
// effectively in each iteration half of the line is drawn at the current column and the other
|
|
// half is drawn in the column to the left.
|
|
|
|
// Rasterize the data outline.
|
|
// h?_low2 to h?_high2 is the vertical line to the left of the current pixel.
|
|
// h?_low1 to h?_high1 is the vertical line at the current pixel.
|
|
// We merge (union) the old h?_low2 to h?_high2 line with the current line in each iteration.
|
|
//
|
|
// For example:
|
|
//
|
|
// X represents a data point. M represents the mid-point between two data points ("intermediate").
|
|
// X, M and x are all part of the outline. # represents the background filled in during
|
|
// the current loop iteration.
|
|
//
|
|
// slope > 0: slope < 0:
|
|
//
|
|
// X < high1 X < high2 (of next loop iteration)
|
|
// x x
|
|
// x < low1 x < low2 (of next loop iteration)
|
|
// x# < high2 x < high1 (of next loop iteration)
|
|
// M# < low2 M < low1 (of next loop iteration)
|
|
// x# < high1 (of next loop iteration) x < high2
|
|
// x# < low1 (of next loop iteration) x < low2
|
|
// x # < high2 (of next loop iteration) x < high1
|
|
// x # x
|
|
// X # < low2 (of next loop iteration) X < low1
|
|
// # #
|
|
// ^ ^
|
|
// ^| current pixel ^| current pixel
|
|
// | |
|
|
// | left of current pixel | left of current pixel
|
|
//
|
|
// In both examples above, the line low2-high2 will be merged with the line low1-high1 of
|
|
// the next iteration.
|
|
|
|
mid = ((h1_left + h1) / 2) * width;
|
|
old_low2 = h1_low2;
|
|
old_high2 = h1_high2;
|
|
|
|
if (h1_left < h1) // slope > 0
|
|
{
|
|
h1_low2 = h1_left * width;
|
|
h1_high2 = mid;
|
|
h1_low1 = mid + width;
|
|
h1_high1 = h1 * width;
|
|
}
|
|
else // slope < 0
|
|
{
|
|
h1_high2 = h1_left * width;
|
|
h1_low2 = mid + width;
|
|
h1_high1 = mid;
|
|
h1_low1 = h1 * width;
|
|
}
|
|
|
|
// Merge the lines.
|
|
if (h1_low1 > old_low2)
|
|
h1_low1 = old_low2;
|
|
if (h1_high1 < old_high2)
|
|
h1_high1 = old_high2;
|
|
|
|
// Fix up values for the current horizontal offset.
|
|
h1_low1 += x;
|
|
h1_high1 += x;
|
|
|
|
if (flags & PH_GRAPH_USE_LINE_2)
|
|
{
|
|
mid = ((h2_left + h2) / 2) * width;
|
|
old_low2 = h2_low2;
|
|
old_high2 = h2_high2;
|
|
|
|
if (h2_left < h2) // slope > 0
|
|
{
|
|
h2_low2 = h2_left * width;
|
|
h2_high2 = mid;
|
|
h2_low1 = mid + width;
|
|
h2_high1 = h2 * width;
|
|
}
|
|
else // slope < 0
|
|
{
|
|
h2_high2 = h2_left * width;
|
|
h2_low2 = mid + width;
|
|
h2_high1 = mid;
|
|
h2_low1 = h2 * width;
|
|
}
|
|
|
|
// Merge the lines.
|
|
if (h2_low1 > old_low2)
|
|
h2_low1 = old_low2;
|
|
if (h2_high1 < old_high2)
|
|
h2_high1 = old_high2;
|
|
|
|
// Fix up values for the current horizontal offset.
|
|
h2_low1 += x;
|
|
h2_high1 += x;
|
|
}
|
|
|
|
// Fill in the background.
|
|
|
|
if (flags & PH_GRAPH_USE_LINE_2)
|
|
{
|
|
for (i = h1_high1 + width; i < h2_low1; i += width)
|
|
{
|
|
bits[i] = lineBackColor2;
|
|
}
|
|
}
|
|
|
|
for (i = x; i < h1_low1; i += width)
|
|
{
|
|
bits[i] = lineBackColor1;
|
|
}
|
|
|
|
// Draw the grid.
|
|
|
|
if (flags & PH_GRAPH_USE_GRID_X)
|
|
{
|
|
// Draw the vertical grid line.
|
|
if (gridYCounter == 0)
|
|
{
|
|
for (i = x; i < numberOfPixels; i += width)
|
|
{
|
|
bits[i] = gridColor;
|
|
}
|
|
}
|
|
|
|
gridYCounter++;
|
|
|
|
if (gridYCounter == DrawInfo->GridWidth)
|
|
gridYCounter = 0;
|
|
}
|
|
|
|
if (flags & PH_GRAPH_USE_GRID_Y)
|
|
{
|
|
FLOAT level;
|
|
LONG h;
|
|
LONG h_last;
|
|
|
|
// Draw the horizontal grid line.
|
|
if (flags & PH_GRAPH_LOGARITHMIC_GRID_Y)
|
|
{
|
|
level = gridLevel;
|
|
h = (LONG)(level * (height - 1));
|
|
h_last = height + gridYThreshold - 1;
|
|
|
|
while (TRUE)
|
|
{
|
|
if (h <= h_last - gridYThreshold)
|
|
{
|
|
bits[x + h * width] = gridColor;
|
|
h_last = h;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
level /= gridBase;
|
|
h = (LONG)(level * (height - 1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
level = gridHeight;
|
|
h = (LONG)(level * (height - 1));
|
|
h_last = 0;
|
|
|
|
while (h < height - 1)
|
|
{
|
|
if (h >= h_last + gridYThreshold)
|
|
{
|
|
bits[x + h * width] = gridColor;
|
|
h_last = h;
|
|
}
|
|
|
|
level += gridHeight;
|
|
h = (LONG)(level * (height - 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw the outline (line 1 is allowed to paint over line 2).
|
|
|
|
if (flags & PH_GRAPH_USE_LINE_2)
|
|
{
|
|
for (i = h2_low1; i <= h2_high1; i += width) // exclude pixel in the middle
|
|
{
|
|
bits[i] = lineColor2;
|
|
}
|
|
}
|
|
|
|
for (i = h1_low1; i <= h1_high1; i += width)
|
|
{
|
|
bits[i] = lineColor1;
|
|
}
|
|
|
|
intermediate = !intermediate;
|
|
x--;
|
|
}
|
|
|
|
if ((flags & PH_GRAPH_LABEL_MAX_Y) && yLabelDataIndex < DrawInfo->LineDataCount)
|
|
{
|
|
FLOAT value;
|
|
PPH_STRING label;
|
|
|
|
value = DrawInfo->LineData1[yLabelDataIndex];
|
|
|
|
if (flags & PH_GRAPH_USE_LINE_2)
|
|
value += DrawInfo->LineData2[yLabelDataIndex];
|
|
|
|
if (label = DrawInfo->LabelYFunction(DrawInfo, yLabelDataIndex, value, DrawInfo->LabelYFunctionParameter))
|
|
{
|
|
HFONT oldFont = NULL;
|
|
SIZE textSize;
|
|
RECT rect;
|
|
|
|
if (DrawInfo->LabelYFont)
|
|
oldFont = SelectObject(hdc, DrawInfo->LabelYFont);
|
|
|
|
SetTextColor(hdc, DrawInfo->LabelYColor);
|
|
SetBkMode(hdc, TRANSPARENT);
|
|
|
|
GetTextExtentPoint32(hdc, label->Buffer, (ULONG)label->Length / 2, &textSize);
|
|
|
|
rect.bottom = height - yLabelMax - PhNormalGraphTextPadding.top;
|
|
rect.top = rect.bottom - textSize.cy;
|
|
|
|
if (rect.top < PhNormalGraphTextPadding.top)
|
|
{
|
|
rect.top = PhNormalGraphTextPadding.top;
|
|
rect.bottom = rect.top + textSize.cy;
|
|
}
|
|
|
|
rect.left = 0;
|
|
rect.right = width - min((LONG)yLabelDataIndex * 2, width) - PhNormalGraphTextPadding.right;
|
|
DrawText(hdc, label->Buffer, (ULONG)label->Length / 2, &rect, DT_NOCLIP | DT_RIGHT);
|
|
|
|
if (oldFont)
|
|
SelectObject(hdc, oldFont);
|
|
|
|
PhDereferenceObject(label);
|
|
}
|
|
}
|
|
|
|
if (DrawInfo->Text.Buffer)
|
|
{
|
|
HFONT oldFont = NULL;
|
|
|
|
if (DrawInfo->TextFont)
|
|
oldFont = SelectObject(hdc, DrawInfo->TextFont);
|
|
|
|
// Fill in the text box.
|
|
SetDCBrushColor(hdc, DrawInfo->TextBoxColor);
|
|
FillRect(hdc, &DrawInfo->TextBoxRect, GetStockObject(DC_BRUSH));
|
|
|
|
// Draw the text.
|
|
SetTextColor(hdc, DrawInfo->TextColor);
|
|
SetBkMode(hdc, TRANSPARENT);
|
|
DrawText(hdc, DrawInfo->Text.Buffer, (ULONG)DrawInfo->Text.Length / 2, &DrawInfo->TextRect, DT_NOCLIP);
|
|
|
|
if (oldFont)
|
|
SelectObject(hdc, oldFont);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the text in a graphing information structure.
|
|
*
|
|
* \param hdc The DC to perform calculations from.
|
|
* \param DrawInfo A structure which contains graphing information. The structure is modified to
|
|
* contain the new text information.
|
|
* \param Text The text.
|
|
* \param Margin The margins of the text box from the edges of the graph.
|
|
* \param Padding The padding within the text box.
|
|
* \param Align The alignment of the text box.
|
|
*/
|
|
VOID PhSetGraphText(
|
|
_In_ HDC hdc,
|
|
_Inout_ PPH_GRAPH_DRAW_INFO DrawInfo,
|
|
_In_ PPH_STRINGREF Text,
|
|
_In_ PRECT Margin,
|
|
_In_ PRECT Padding,
|
|
_In_ ULONG Align
|
|
)
|
|
{
|
|
HFONT oldFont = NULL;
|
|
SIZE textSize;
|
|
PH_RECTANGLE boxRectangle;
|
|
PH_RECTANGLE textRectangle;
|
|
|
|
if (DrawInfo->TextFont)
|
|
oldFont = SelectObject(hdc, DrawInfo->TextFont);
|
|
|
|
DrawInfo->Text = *Text;
|
|
GetTextExtentPoint32(hdc, Text->Buffer, (ULONG)Text->Length / 2, &textSize);
|
|
|
|
if (oldFont)
|
|
SelectObject(hdc, oldFont);
|
|
|
|
// Calculate the box rectangle.
|
|
|
|
boxRectangle.Width = textSize.cx + Padding->left + Padding->right;
|
|
boxRectangle.Height = textSize.cy + Padding->top + Padding->bottom;
|
|
|
|
if (Align & PH_ALIGN_LEFT)
|
|
boxRectangle.Left = Margin->left;
|
|
else if (Align & PH_ALIGN_RIGHT)
|
|
boxRectangle.Left = DrawInfo->Width - boxRectangle.Width - Margin->right;
|
|
else
|
|
boxRectangle.Left = (DrawInfo->Width - boxRectangle.Width) / 2;
|
|
|
|
if (Align & PH_ALIGN_TOP)
|
|
boxRectangle.Top = Margin->top;
|
|
else if (Align & PH_ALIGN_BOTTOM)
|
|
boxRectangle.Top = DrawInfo->Height - boxRectangle.Height - Margin->bottom;
|
|
else
|
|
boxRectangle.Top = (DrawInfo->Height - boxRectangle.Height) / 2;
|
|
|
|
// Calculate the text rectangle.
|
|
|
|
textRectangle.Left = boxRectangle.Left + Padding->left;
|
|
textRectangle.Top = boxRectangle.Top + Padding->top;
|
|
textRectangle.Width = textSize.cx;
|
|
textRectangle.Height = textSize.cy;
|
|
|
|
// Save the rectangles.
|
|
DrawInfo->TextRect = PhRectangleToRect(textRectangle);
|
|
DrawInfo->TextBoxRect = PhRectangleToRect(boxRectangle);
|
|
}
|
|
|
|
VOID PhpCreateGraphContext(
|
|
_Out_ PPHP_GRAPH_CONTEXT *Context
|
|
)
|
|
{
|
|
PPHP_GRAPH_CONTEXT context;
|
|
|
|
context = PhAllocate(sizeof(PHP_GRAPH_CONTEXT));
|
|
memset(context, 0, sizeof(PHP_GRAPH_CONTEXT));
|
|
|
|
context->DrawInfo.Width = 3;
|
|
context->DrawInfo.Height = 3;
|
|
context->DrawInfo.Flags = PH_GRAPH_USE_GRID_X;
|
|
context->DrawInfo.Step = 2;
|
|
context->DrawInfo.BackColor = RGB(0xef, 0xef, 0xef);
|
|
context->DrawInfo.LineDataCount = 0;
|
|
context->DrawInfo.LineData1 = NULL;
|
|
context->DrawInfo.LineData2 = NULL;
|
|
context->DrawInfo.LineColor1 = RGB(0x00, 0xff, 0x00);
|
|
context->DrawInfo.LineColor2 = RGB(0xff, 0x00, 0x00);
|
|
context->DrawInfo.LineBackColor1 = RGB(0x00, 0x77, 0x00);
|
|
context->DrawInfo.LineBackColor2 = RGB(0x77, 0x00, 0x00);
|
|
context->DrawInfo.GridColor = RGB(0xc7, 0xc7, 0xc7);
|
|
context->DrawInfo.GridWidth = 20;
|
|
context->DrawInfo.GridHeight = 0.25f;
|
|
context->DrawInfo.GridXOffset = 0;
|
|
context->DrawInfo.GridYThreshold = 10;
|
|
context->DrawInfo.GridBase = 2.0f;
|
|
context->DrawInfo.LabelYColor = RGB(0x77, 0x77, 0x77);
|
|
context->DrawInfo.LabelMaxYIndexLimit = -1;
|
|
context->DrawInfo.TextColor = RGB(0x00, 0xff, 0x00);
|
|
context->DrawInfo.TextBoxColor = RGB(0x00, 0x22, 0x00);
|
|
|
|
context->Options.FadeOutBackColor = RGB(0xef, 0xef, 0xef);
|
|
context->Options.FadeOutWidth = 100;
|
|
|
|
*Context = context;
|
|
}
|
|
|
|
VOID PhpFreeGraphContext(
|
|
_Inout_ _Post_invalid_ PPHP_GRAPH_CONTEXT Context
|
|
)
|
|
{
|
|
PhFree(Context);
|
|
}
|
|
|
|
static PWSTR PhpMakeGraphTooltipContextAtom(
|
|
VOID
|
|
)
|
|
{
|
|
PH_DEFINE_MAKE_ATOM(L"PhLib_GraphTooltipContext");
|
|
}
|
|
|
|
static VOID PhpDeleteBufferedContext(
|
|
_In_ PPHP_GRAPH_CONTEXT Context
|
|
)
|
|
{
|
|
if (Context->BufferedContext)
|
|
{
|
|
// The original bitmap must be selected back into the context, otherwise the bitmap can't be
|
|
// deleted.
|
|
SelectObject(Context->BufferedContext, Context->BufferedOldBitmap);
|
|
DeleteObject(Context->BufferedBitmap);
|
|
DeleteDC(Context->BufferedContext);
|
|
|
|
Context->BufferedContext = NULL;
|
|
Context->BufferedBitmap = NULL;
|
|
Context->BufferedBits = NULL;
|
|
}
|
|
}
|
|
|
|
static VOID PhpCreateBufferedContext(
|
|
_In_ PPHP_GRAPH_CONTEXT Context
|
|
)
|
|
{
|
|
HDC hdc;
|
|
BITMAPINFOHEADER header;
|
|
|
|
PhpDeleteBufferedContext(Context);
|
|
|
|
GetClientRect(Context->Handle, &Context->BufferedContextRect);
|
|
|
|
hdc = GetDC(Context->Handle);
|
|
Context->BufferedContext = CreateCompatibleDC(hdc);
|
|
|
|
memset(&header, 0, sizeof(BITMAPINFOHEADER));
|
|
header.biSize = sizeof(BITMAPINFOHEADER);
|
|
header.biWidth = Context->BufferedContextRect.right;
|
|
header.biHeight = Context->BufferedContextRect.bottom;
|
|
header.biPlanes = 1;
|
|
header.biBitCount = 32;
|
|
|
|
Context->BufferedBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&header, DIB_RGB_COLORS, &Context->BufferedBits, NULL, 0);
|
|
|
|
ReleaseDC(Context->Handle, hdc);
|
|
Context->BufferedOldBitmap = SelectObject(Context->BufferedContext, Context->BufferedBitmap);
|
|
}
|
|
|
|
static VOID PhpDeleteFadeOutContext(
|
|
_In_ PPHP_GRAPH_CONTEXT Context
|
|
)
|
|
{
|
|
if (Context->FadeOutContext)
|
|
{
|
|
SelectObject(Context->FadeOutContext, Context->FadeOutOldBitmap);
|
|
DeleteObject(Context->FadeOutBitmap);
|
|
DeleteDC(Context->FadeOutContext);
|
|
|
|
Context->FadeOutContext = NULL;
|
|
Context->FadeOutBitmap = NULL;
|
|
Context->FadeOutBits = NULL;
|
|
}
|
|
}
|
|
|
|
static VOID PhpCreateFadeOutContext(
|
|
_In_ PPHP_GRAPH_CONTEXT Context
|
|
)
|
|
{
|
|
HDC hdc;
|
|
BITMAPINFOHEADER header;
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG height;
|
|
COLORREF backColor;
|
|
ULONG fadeOutWidth;
|
|
FLOAT fadeOutWidthSquared;
|
|
ULONG currentAlpha;
|
|
ULONG currentColor;
|
|
|
|
PhpDeleteFadeOutContext(Context);
|
|
|
|
GetClientRect(Context->Handle, &Context->FadeOutContextRect);
|
|
Context->FadeOutContextRect.right = Context->Options.FadeOutWidth;
|
|
|
|
hdc = GetDC(Context->Handle);
|
|
Context->FadeOutContext = CreateCompatibleDC(hdc);
|
|
|
|
memset(&header, 0, sizeof(BITMAPINFOHEADER));
|
|
header.biSize = sizeof(BITMAPINFOHEADER);
|
|
header.biWidth = Context->FadeOutContextRect.right;
|
|
header.biHeight = Context->FadeOutContextRect.bottom;
|
|
header.biPlanes = 1;
|
|
header.biBitCount = 32;
|
|
|
|
Context->FadeOutBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&header, DIB_RGB_COLORS, &Context->FadeOutBits, NULL, 0);
|
|
|
|
ReleaseDC(Context->Handle, hdc);
|
|
Context->FadeOutOldBitmap = SelectObject(Context->FadeOutContext, Context->FadeOutBitmap);
|
|
|
|
if (!Context->FadeOutBits)
|
|
return;
|
|
|
|
height = Context->FadeOutContextRect.bottom;
|
|
backColor = Context->Options.FadeOutBackColor;
|
|
fadeOutWidth = Context->Options.FadeOutWidth;
|
|
fadeOutWidthSquared = (FLOAT)fadeOutWidth * fadeOutWidth;
|
|
|
|
for (i = 0; i < fadeOutWidth; i++)
|
|
{
|
|
currentAlpha = 255 - (ULONG)((FLOAT)(i * i) / fadeOutWidthSquared * 255);
|
|
currentColor =
|
|
((backColor & 0xff) * currentAlpha / 255) +
|
|
((((backColor >> 8) & 0xff) * currentAlpha / 255) << 8) +
|
|
((((backColor >> 16) & 0xff) * currentAlpha / 255) << 16) +
|
|
(currentAlpha << 24);
|
|
|
|
for (j = i; j < height * fadeOutWidth; j += fadeOutWidth)
|
|
{
|
|
((PULONG)Context->FadeOutBits)[j] = currentColor;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID PhpUpdateDrawInfo(
|
|
_In_ HWND hwnd,
|
|
_In_ PPHP_GRAPH_CONTEXT Context
|
|
)
|
|
{
|
|
PH_GRAPH_GETDRAWINFO getDrawInfo;
|
|
|
|
Context->DrawInfo.Width = Context->BufferedContextRect.right;
|
|
Context->DrawInfo.Height = Context->BufferedContextRect.bottom;
|
|
|
|
getDrawInfo.Header.hwndFrom = hwnd;
|
|
getDrawInfo.Header.idFrom = Context->Id;
|
|
getDrawInfo.Header.code = GCN_GETDRAWINFO;
|
|
getDrawInfo.DrawInfo = &Context->DrawInfo;
|
|
|
|
SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&getDrawInfo);
|
|
}
|
|
|
|
VOID PhpDrawGraphControl(
|
|
_In_ HWND hwnd,
|
|
_In_ PPHP_GRAPH_CONTEXT Context
|
|
)
|
|
{
|
|
if (Context->BufferedBits)
|
|
PhDrawGraphDirect(Context->BufferedContext, Context->BufferedBits, &Context->DrawInfo);
|
|
|
|
if (Context->Style & GC_STYLE_FADEOUT)
|
|
{
|
|
BLENDFUNCTION blendFunction;
|
|
|
|
if (!Context->FadeOutContext)
|
|
PhpCreateFadeOutContext(Context);
|
|
|
|
blendFunction.BlendOp = AC_SRC_OVER;
|
|
blendFunction.BlendFlags = 0;
|
|
blendFunction.SourceConstantAlpha = 255;
|
|
blendFunction.AlphaFormat = AC_SRC_ALPHA;
|
|
GdiAlphaBlend(
|
|
Context->BufferedContext,
|
|
0,
|
|
0,
|
|
Context->Options.FadeOutWidth,
|
|
Context->FadeOutContextRect.bottom,
|
|
Context->FadeOutContext,
|
|
0,
|
|
0,
|
|
Context->Options.FadeOutWidth,
|
|
Context->FadeOutContextRect.bottom,
|
|
blendFunction
|
|
);
|
|
}
|
|
|
|
if (Context->Style & GC_STYLE_DRAW_PANEL)
|
|
{
|
|
PH_GRAPH_DRAWPANEL drawPanel;
|
|
|
|
drawPanel.Header.hwndFrom = hwnd;
|
|
drawPanel.Header.idFrom = Context->Id;
|
|
drawPanel.Header.code = GCN_DRAWPANEL;
|
|
drawPanel.hdc = Context->BufferedContext;
|
|
drawPanel.Rect = Context->BufferedContextRect;
|
|
|
|
SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&drawPanel);
|
|
}
|
|
}
|
|
|
|
LRESULT CALLBACK PhpGraphWndProc(
|
|
_In_ HWND hwnd,
|
|
_In_ UINT uMsg,
|
|
_In_ WPARAM wParam,
|
|
_In_ LPARAM lParam
|
|
)
|
|
{
|
|
PPHP_GRAPH_CONTEXT context;
|
|
|
|
context = (PPHP_GRAPH_CONTEXT)GetWindowLongPtr(hwnd, 0);
|
|
|
|
if (uMsg == WM_CREATE)
|
|
{
|
|
PhpCreateGraphContext(&context);
|
|
SetWindowLongPtr(hwnd, 0, (LONG_PTR)context);
|
|
}
|
|
|
|
if (!context)
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_MOUSEMOVE:
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONUP:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_RBUTTONUP:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_MBUTTONUP:
|
|
{
|
|
if (context->TooltipHandle)
|
|
{
|
|
MSG message;
|
|
|
|
message.hwnd = hwnd;
|
|
message.message = uMsg;
|
|
message.wParam = wParam;
|
|
message.lParam = lParam;
|
|
SendMessage(context->TooltipHandle, TTM_RELAYEVENT, 0, (LPARAM)&message);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_CREATE:
|
|
{
|
|
CREATESTRUCT *createStruct = (CREATESTRUCT *)lParam;
|
|
|
|
context->Handle = hwnd;
|
|
context->Style = createStruct->style;
|
|
context->Id = (ULONG_PTR)createStruct->hMenu;
|
|
}
|
|
break;
|
|
case WM_DESTROY:
|
|
{
|
|
if (context->TooltipHandle)
|
|
DestroyWindow(context->TooltipHandle);
|
|
|
|
PhpDeleteFadeOutContext(context);
|
|
PhpDeleteBufferedContext(context);
|
|
PhpFreeGraphContext(context);
|
|
SetWindowLongPtr(hwnd, 0, (LONG_PTR)NULL);
|
|
}
|
|
break;
|
|
case WM_STYLECHANGED:
|
|
{
|
|
STYLESTRUCT *styleStruct = (STYLESTRUCT *)lParam;
|
|
|
|
if (wParam == GWL_STYLE)
|
|
{
|
|
context->Style = styleStruct->styleNew;
|
|
context->NeedsDraw = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case WM_SIZE:
|
|
{
|
|
// Force a re-create of the buffered context.
|
|
PhpCreateBufferedContext(context);
|
|
PhpDeleteFadeOutContext(context);
|
|
|
|
PhpUpdateDrawInfo(hwnd, context);
|
|
context->NeedsDraw = TRUE;
|
|
InvalidateRect(hwnd, NULL, FALSE);
|
|
|
|
if (context->TooltipHandle)
|
|
{
|
|
TOOLINFO toolInfo;
|
|
|
|
memset(&toolInfo, 0, sizeof(TOOLINFO));
|
|
toolInfo.cbSize = sizeof(TOOLINFO);
|
|
toolInfo.hwnd = hwnd;
|
|
toolInfo.uId = 1;
|
|
GetClientRect(hwnd, &toolInfo.rect);
|
|
SendMessage(context->TooltipHandle, TTM_NEWTOOLRECT, 0, (LPARAM)&toolInfo);
|
|
}
|
|
}
|
|
break;
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT paintStruct;
|
|
HDC hdc;
|
|
|
|
if (hdc = BeginPaint(hwnd, &paintStruct))
|
|
{
|
|
if (!context->BufferedContext)
|
|
PhpCreateBufferedContext(context);
|
|
|
|
if (context->NeedsUpdate)
|
|
{
|
|
PhpUpdateDrawInfo(hwnd, context);
|
|
context->NeedsUpdate = FALSE;
|
|
}
|
|
|
|
if (context->NeedsDraw)
|
|
{
|
|
PhpDrawGraphControl(hwnd, context);
|
|
context->NeedsDraw = FALSE;
|
|
}
|
|
|
|
BitBlt(
|
|
hdc,
|
|
paintStruct.rcPaint.left,
|
|
paintStruct.rcPaint.top,
|
|
paintStruct.rcPaint.right - paintStruct.rcPaint.left,
|
|
paintStruct.rcPaint.bottom - paintStruct.rcPaint.top,
|
|
context->BufferedContext,
|
|
paintStruct.rcPaint.left,
|
|
paintStruct.rcPaint.top,
|
|
SRCCOPY
|
|
);
|
|
|
|
EndPaint(hwnd, &paintStruct);
|
|
}
|
|
}
|
|
return 0;
|
|
case WM_ERASEBKGND:
|
|
return 1;
|
|
case WM_NCPAINT:
|
|
{
|
|
HRGN updateRegion;
|
|
|
|
updateRegion = (HRGN)wParam;
|
|
|
|
if (updateRegion == (HRGN)1) // HRGN_FULL
|
|
updateRegion = NULL;
|
|
|
|
// Themed border
|
|
if (context->Style & WS_BORDER)
|
|
{
|
|
HDC hdc;
|
|
ULONG flags;
|
|
RECT rect;
|
|
|
|
// Note the use of undocumented flags below. GetDCEx doesn't work without these.
|
|
|
|
flags = DCX_WINDOW | DCX_LOCKWINDOWUPDATE | 0x10000;
|
|
|
|
if (updateRegion)
|
|
flags |= DCX_INTERSECTRGN | 0x40000;
|
|
|
|
if (hdc = GetDCEx(hwnd, updateRegion, flags))
|
|
{
|
|
GetClientRect(hwnd, &rect);
|
|
rect.right += 2;
|
|
rect.bottom += 2;
|
|
SetDCBrushColor(hdc, RGB(0x8f, 0x8f, 0x8f));
|
|
FrameRect(hdc, &rect, GetStockObject(DC_BRUSH));
|
|
|
|
ReleaseDC(hwnd, hdc);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case WM_NOTIFY:
|
|
{
|
|
LPNMHDR header = (LPNMHDR)lParam;
|
|
|
|
if (header->hwndFrom == context->TooltipHandle)
|
|
{
|
|
switch (header->code)
|
|
{
|
|
case TTN_GETDISPINFO:
|
|
{
|
|
LPNMTTDISPINFO dispInfo = (LPNMTTDISPINFO)header;
|
|
POINT point;
|
|
RECT clientRect;
|
|
PH_GRAPH_GETTOOLTIPTEXT getTooltipText;
|
|
|
|
GetCursorPos(&point);
|
|
ScreenToClient(hwnd, &point);
|
|
GetClientRect(hwnd, &clientRect);
|
|
|
|
getTooltipText.Header.hwndFrom = hwnd;
|
|
getTooltipText.Header.idFrom = context->Id;
|
|
getTooltipText.Header.code = GCN_GETTOOLTIPTEXT;
|
|
getTooltipText.Index = (clientRect.right - point.x - 1) / context->DrawInfo.Step;
|
|
getTooltipText.TotalCount = context->DrawInfo.LineDataCount;
|
|
getTooltipText.Text.Buffer = NULL;
|
|
getTooltipText.Text.Length = 0;
|
|
|
|
SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&getTooltipText);
|
|
|
|
if (getTooltipText.Text.Buffer)
|
|
{
|
|
dispInfo->lpszText = getTooltipText.Text.Buffer;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case WM_SETCURSOR:
|
|
{
|
|
if (context->Options.DefaultCursor)
|
|
{
|
|
SetCursor(context->Options.DefaultCursor);
|
|
return TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
if (context->TooltipHandle)
|
|
{
|
|
POINT point;
|
|
|
|
GetCursorPos(&point);
|
|
ScreenToClient(hwnd, &point);
|
|
|
|
if (context->LastCursorLocation.x != point.x || context->LastCursorLocation.y != point.y)
|
|
{
|
|
SendMessage(context->TooltipHandle, TTM_UPDATE, 0, 0);
|
|
context->LastCursorLocation = point;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONUP:
|
|
case WM_LBUTTONDBLCLK:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_RBUTTONUP:
|
|
case WM_RBUTTONDBLCLK:
|
|
{
|
|
PH_GRAPH_MOUSEEVENT mouseEvent;
|
|
RECT clientRect;
|
|
|
|
GetClientRect(hwnd, &clientRect);
|
|
|
|
mouseEvent.Header.hwndFrom = hwnd;
|
|
mouseEvent.Header.idFrom = context->Id;
|
|
mouseEvent.Header.code = GCN_MOUSEEVENT;
|
|
mouseEvent.Message = uMsg;
|
|
mouseEvent.Keys = (ULONG)wParam;
|
|
mouseEvent.Point.x = LOWORD(lParam);
|
|
mouseEvent.Point.y = HIWORD(lParam);
|
|
|
|
mouseEvent.Index = (clientRect.right - mouseEvent.Point.x - 1) / context->DrawInfo.Step;
|
|
mouseEvent.TotalCount = context->DrawInfo.LineDataCount;
|
|
|
|
SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&mouseEvent);
|
|
}
|
|
break;
|
|
case GCM_GETDRAWINFO:
|
|
{
|
|
PPH_GRAPH_DRAW_INFO drawInfo = (PPH_GRAPH_DRAW_INFO)lParam;
|
|
|
|
memcpy(drawInfo, &context->DrawInfo, sizeof(PH_GRAPH_DRAW_INFO));
|
|
}
|
|
return TRUE;
|
|
case GCM_SETDRAWINFO:
|
|
{
|
|
PPH_GRAPH_DRAW_INFO drawInfo = (PPH_GRAPH_DRAW_INFO)lParam;
|
|
ULONG width;
|
|
ULONG height;
|
|
|
|
width = context->DrawInfo.Width;
|
|
height = context->DrawInfo.Height;
|
|
memcpy(&context->DrawInfo, drawInfo, sizeof(PH_GRAPH_DRAW_INFO));
|
|
context->DrawInfo.Width = width;
|
|
context->DrawInfo.Height = height;
|
|
}
|
|
return TRUE;
|
|
case GCM_DRAW:
|
|
{
|
|
PhpUpdateDrawInfo(hwnd, context);
|
|
context->NeedsDraw = TRUE;
|
|
}
|
|
return TRUE;
|
|
case GCM_MOVEGRID:
|
|
{
|
|
LONG increment = (LONG)wParam;
|
|
|
|
context->DrawInfo.GridXOffset += increment;
|
|
}
|
|
return TRUE;
|
|
case GCM_GETBUFFEREDCONTEXT:
|
|
return (LRESULT)context->BufferedContext;
|
|
case GCM_SETTOOLTIP:
|
|
{
|
|
if (wParam)
|
|
{
|
|
TOOLINFO toolInfo = { sizeof(toolInfo) };
|
|
|
|
context->TooltipHandle = CreateWindow(
|
|
TOOLTIPS_CLASS,
|
|
NULL,
|
|
WS_POPUP | WS_EX_TRANSPARENT | TTS_NOPREFIX,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
NULL,
|
|
NULL,
|
|
PhLibImageBase,
|
|
NULL
|
|
);
|
|
|
|
SetWindowPos(context->TooltipHandle, HWND_TOPMOST, 0, 0, 0, 0,
|
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
|
|
toolInfo.uFlags = 0;
|
|
toolInfo.hwnd = hwnd;
|
|
toolInfo.uId = 1;
|
|
toolInfo.lpszText = LPSTR_TEXTCALLBACK;
|
|
GetClientRect(hwnd, &toolInfo.rect);
|
|
SendMessage(context->TooltipHandle, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
|
|
|
|
SendMessage(context->TooltipHandle, TTM_SETDELAYTIME, TTDT_INITIAL, 0);
|
|
SendMessage(context->TooltipHandle, TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT);
|
|
// Allow newlines (-1 doesn't work)
|
|
SendMessage(context->TooltipHandle, TTM_SETMAXTIPWIDTH, 0, MAXSHORT);
|
|
}
|
|
else
|
|
{
|
|
DestroyWindow(context->TooltipHandle);
|
|
context->TooltipHandle = NULL;
|
|
}
|
|
}
|
|
return TRUE;
|
|
case GCM_UPDATETOOLTIP:
|
|
{
|
|
if (!context->TooltipHandle)
|
|
return FALSE;
|
|
|
|
SendMessage(context->TooltipHandle, TTM_UPDATE, 0, 0);
|
|
}
|
|
return TRUE;
|
|
case GCM_GETOPTIONS:
|
|
memcpy((PPH_GRAPH_OPTIONS)lParam, &context->Options, sizeof(PH_GRAPH_OPTIONS));
|
|
return TRUE;
|
|
case GCM_SETOPTIONS:
|
|
memcpy(&context->Options, (PPH_GRAPH_OPTIONS)lParam, sizeof(PH_GRAPH_OPTIONS));
|
|
PhpDeleteFadeOutContext(context);
|
|
return TRUE;
|
|
}
|
|
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
/**
|
|
* Initializes a graph buffer management structure.
|
|
*
|
|
* \param Buffers The buffer management structure.
|
|
*/
|
|
VOID PhInitializeGraphBuffers(
|
|
_Out_ PPH_GRAPH_BUFFERS Buffers
|
|
)
|
|
{
|
|
Buffers->AllocatedCount = 0;
|
|
Buffers->Data1 = NULL;
|
|
Buffers->Data2 = NULL;
|
|
Buffers->Valid = FALSE;
|
|
}
|
|
|
|
/**
|
|
* Frees resources used by a graph buffer management structure.
|
|
*
|
|
* \param Buffers The buffer management structure.
|
|
*/
|
|
VOID PhDeleteGraphBuffers(
|
|
_Inout_ PPH_GRAPH_BUFFERS Buffers
|
|
)
|
|
{
|
|
if (Buffers->Data1) PhFree(Buffers->Data1);
|
|
if (Buffers->Data2) PhFree(Buffers->Data2);
|
|
}
|
|
|
|
/**
|
|
* Sets up a graphing information structure with information from a graph buffer management
|
|
* structure.
|
|
*
|
|
* \param Buffers The buffer management structure.
|
|
* \param DrawInfo The graphing information structure.
|
|
* \param DataCount The number of data points currently required. The buffers are resized if needed.
|
|
*/
|
|
VOID PhGetDrawInfoGraphBuffers(
|
|
_Inout_ PPH_GRAPH_BUFFERS Buffers,
|
|
_Inout_ PPH_GRAPH_DRAW_INFO DrawInfo,
|
|
_In_ ULONG DataCount
|
|
)
|
|
{
|
|
DrawInfo->LineDataCount = min(DataCount, PH_GRAPH_DATA_COUNT(DrawInfo->Width, DrawInfo->Step));
|
|
|
|
// Do we need to allocate or re-allocate the data buffers?
|
|
if (Buffers->AllocatedCount < DrawInfo->LineDataCount)
|
|
{
|
|
if (Buffers->Data1)
|
|
PhFree(Buffers->Data1);
|
|
if ((DrawInfo->Flags & PH_GRAPH_USE_LINE_2) && Buffers->Data2)
|
|
PhFree(Buffers->Data2);
|
|
|
|
Buffers->AllocatedCount *= 2;
|
|
|
|
if (Buffers->AllocatedCount < DrawInfo->LineDataCount)
|
|
Buffers->AllocatedCount = DrawInfo->LineDataCount;
|
|
|
|
Buffers->Data1 = PhAllocate(Buffers->AllocatedCount * sizeof(FLOAT));
|
|
|
|
if (DrawInfo->Flags & PH_GRAPH_USE_LINE_2)
|
|
{
|
|
Buffers->Data2 = PhAllocate(Buffers->AllocatedCount * sizeof(FLOAT));
|
|
}
|
|
|
|
Buffers->Valid = FALSE;
|
|
}
|
|
|
|
DrawInfo->LineData1 = Buffers->Data1;
|
|
DrawInfo->LineData2 = Buffers->Data2;
|
|
}
|
|
|
|
VOID PhInitializeGraphState(
|
|
_Out_ PPH_GRAPH_STATE State
|
|
)
|
|
{
|
|
PhInitializeGraphBuffers(&State->Buffers);
|
|
State->Text = NULL;
|
|
State->TooltipText = NULL;
|
|
State->TooltipIndex = -1;
|
|
}
|
|
|
|
VOID PhDeleteGraphState(
|
|
_Inout_ PPH_GRAPH_STATE State
|
|
)
|
|
{
|
|
PhDeleteGraphBuffers(&State->Buffers);
|
|
if (State->Text) PhDereferenceObject(State->Text);
|
|
if (State->TooltipText) PhDereferenceObject(State->TooltipText);
|
|
}
|
|
|
|
VOID PhGraphStateGetDrawInfo(
|
|
_Inout_ PPH_GRAPH_STATE State,
|
|
_In_ PPH_GRAPH_GETDRAWINFO GetDrawInfo,
|
|
_In_ ULONG DataCount
|
|
)
|
|
{
|
|
PhGetDrawInfoGraphBuffers(&State->Buffers, GetDrawInfo->DrawInfo, DataCount);
|
|
}
|