Files
mirror-processhacker/2.x/trunk/phlib/graph.c
T
wj32 b7c46e7b1e fixed lag when showing the System Information window
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@4140 21ef857c-d57f-4fe0-8362-d861dc6d29cd
2011-03-17 02:58:40 +00:00

997 lines
29 KiB
C

/*
* Process Hacker -
* graph control
*
* Copyright (C) 2010-2011 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/>.
*/
#define _PH_GRAPH_PRIVATE
#include <phgui.h>
#include <graph.h>
typedef struct _PHP_GRAPH_CONTEXT
{
HWND Handle;
PH_GRAPH_DRAW_INFO DrawInfo;
HDC BufferedContext;
HBITMAP BufferedOldBitmap;
HBITMAP BufferedBitmap;
RECT BufferedContextRect;
HWND TooltipHandle;
WNDPROC TooltipOldWndProc;
BOOLEAN TooltipVisible;
HMONITOR ValidMonitor;
MONITORINFO MonitorInfo;
} 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()
{
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;
}
/**
* Draws a graph.
*
* \param hdc The DC to draw to.
* \param DrawInfo A structure which contains graphing information.
*/
VOID PhDrawGraph(
__in HDC hdc,
__in PPH_GRAPH_DRAW_INFO DrawInfo
)
{
ULONG width;
ULONG height;
ULONG height1;
ULONG flags;
LONG step;
RECT rect;
POINT points[4];
HPEN nullPen;
HPEN dcPen;
HBRUSH dcBrush;
PPH_LIST lineList1 = NULL;
PPH_LIST lineList2 = NULL;
width = DrawInfo->Width;
height = DrawInfo->Height;
height1 = DrawInfo->Height - 1;
flags = DrawInfo->Flags;
step = DrawInfo->Step;
nullPen = GetStockObject(NULL_PEN);
dcPen = GetStockObject(DC_PEN);
dcBrush = GetStockObject(DC_BRUSH);
// Fill in the background.
rect.left = 0;
rect.top = 0;
rect.right = width;
rect.bottom = height;
SetDCBrushColor(hdc, DrawInfo->BackColor);
FillRect(hdc, &rect, dcBrush);
// Draw the data (this section only fills in the background).
if (DrawInfo->LineData1 && DrawInfo->LineDataCount >= 2)
{
LONG x = width - step;
ULONG index = 0;
BOOLEAN willBreak = FALSE;
SelectObject(hdc, dcBrush);
SelectObject(hdc, nullPen);
lineList1 = PhCreateList(DrawInfo->LineDataCount);
if (DrawInfo->LineData2)
lineList2 = PhCreateList(DrawInfo->LineDataCount);
while (index < DrawInfo->LineDataCount - 1)
{
FLOAT f0; // 0..1
FLOAT f1;
ULONG h0; // height..0
ULONG h1;
if (x < 0 || index == DrawInfo->LineDataCount - 2)
willBreak = TRUE;
// Draw line 1.
f0 = DrawInfo->LineData1[index];
f1 = DrawInfo->LineData1[index + 1];
h0 = (ULONG)((1 - f0) * height);
h1 = (ULONG)((1 - f1) * height);
// top-left, top-right, bottom-right, bottom-left
points[0].x = x;
points[0].y = h1;
points[1].x = x + step;
points[1].y = h0;
points[2].x = x + step;
points[2].y = height;
points[3].x = x;
points[3].y = height;
// Fill in the area below the line.
SetDCBrushColor(hdc, DrawInfo->LineBackColor1);
Polygon(hdc, points, 4);
// Add the line height values to the list for drawing later.
if (h0 > height1) h0 = height1;
if (h1 > height1) h1 = height1;
PhAddItemList(lineList1, (PVOID)h0);
if (willBreak) PhAddItemList(lineList1, (PVOID)h1);
// Draw line 2 (either stacked or overlayed).
if (DrawInfo->LineData2 && (flags & PH_GRAPH_USE_LINE_2))
{
if (flags & PH_GRAPH_OVERLAY_LINE_2)
{
f0 = DrawInfo->LineData2[index];
f1 = DrawInfo->LineData2[index + 1];
}
else
{
f0 += DrawInfo->LineData2[index];
f1 += DrawInfo->LineData2[index + 1];
if (f0 > 1) f0 = 1;
if (f1 > 1) f1 = 1;
}
h0 = (ULONG)((1 - f0) * height);
h1 = (ULONG)((1 - f1) * height);
if (flags & PH_GRAPH_OVERLAY_LINE_2)
{
points[0].y = h1;
points[1].y = h0;
}
else
{
// Fix the bottom points so we don't fill in the line 1 area.
points[2].y = points[1].y; // p2.y = h0(line1)
points[3].y = points[0].y; // p3.y = h1(line1)
points[0].y = h1;
points[1].y = h0;
}
SetDCBrushColor(hdc, DrawInfo->LineBackColor2);
Polygon(hdc, points, 4);
if (h0 > height1) h0 = height1;
if (h1 > height1) h1 = height1;
PhAddItemList(lineList2, (PVOID)h0);
if (willBreak) PhAddItemList(lineList2, (PVOID)h1);
}
if (x < 0)
break;
x -= step;
index++;
}
}
// Draw the grid.
if (flags & PH_GRAPH_USE_GRID)
{
ULONG x = width / DrawInfo->GridWidth;
ULONG y = height / DrawInfo->GridHeight;
ULONG i;
ULONG pos;
ULONG gridStart;
SetDCPenColor(hdc, DrawInfo->GridColor);
SelectObject(hdc, dcPen);
// Draw the vertical lines.
points[0].y = 0;
points[1].y = height;
gridStart = (DrawInfo->GridStart * DrawInfo->Step) % DrawInfo->GridWidth;
for (i = 0; i <= x; i++)
{
pos = width - (i * DrawInfo->GridWidth + gridStart) - 1;
points[0].x = pos;
points[1].x = pos;
Polyline(hdc, points, 2);
}
// Draw the horizontal lines.
points[0].x = 0;
points[1].x = width;
for (i = 0; i <= y; i++)
{
pos = i * DrawInfo->GridHeight - 1;
points[0].y = pos;
points[1].y = pos;
Polyline(hdc, points, 2);
}
}
// Draw the data (this section draws the lines).
// This is done to get a clearer graph.
if (lineList1)
{
LONG x = width - step;
ULONG index;
ULONG previousHeight1;
ULONG previousHeight2;
previousHeight1 = (ULONG)lineList1->Items[0];
index = 1;
if (lineList2)
previousHeight2 = (ULONG)lineList2->Items[0];
while (index < lineList1->Count)
{
points[0].x = x;
points[1].x = x + step;
// Draw line 2 first so it doesn't draw over line 1.
if (lineList2)
{
points[0].y = (ULONG)lineList2->Items[index];
points[1].y = previousHeight2;
SelectObject(hdc, dcPen);
SetDCPenColor(hdc, DrawInfo->LineColor2);
Polyline(hdc, points, 2);
previousHeight2 = points[0].y;
points[0].y = (ULONG)lineList1->Items[index];
points[1].y = previousHeight1;
SelectObject(hdc, dcPen);
SetDCPenColor(hdc, DrawInfo->LineColor1);
Polyline(hdc, points, 2);
previousHeight1 = points[0].y;
}
else
{
points[0].x = x;
points[0].y = (ULONG)lineList1->Items[index];
points[1].x = x + step;
points[1].y = previousHeight1;
SelectObject(hdc, dcPen);
SetDCPenColor(hdc, DrawInfo->LineColor1);
Polyline(hdc, points, 2);
previousHeight1 = points[0].y;
}
if (x < 0)
break;
x -= step;
index++;
}
PhDereferenceObject(lineList1);
if (lineList2) PhDereferenceObject(lineList2);
}
// Draw the text.
if (DrawInfo->Text.Buffer)
{
// 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, DrawInfo->Text.Length / 2, &DrawInfo->TextRect, DT_NOCLIP);
}
}
/**
* 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
)
{
SIZE textSize;
PH_RECTANGLE boxRectangle;
PH_RECTANGLE textRectangle;
DrawInfo->Text = *Text;
GetTextExtentPoint32(hdc, Text->Buffer, Text->Length / 2, &textSize);
// 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);
}
HWND PhCreateGraphControl(
__in HWND ParentHandle,
__in INT_PTR Id
)
{
return CreateWindow(
PH_GRAPH_CLASSNAME,
L"",
WS_CHILD | WS_CLIPSIBLINGS,
0,
0,
3,
3,
ParentHandle,
(HMENU)Id,
PhLibImageBase,
NULL
);
}
VOID PhpCreateGraphContext(
__out PPHP_GRAPH_CONTEXT *Context
)
{
PPHP_GRAPH_CONTEXT context;
context = PhAllocate(sizeof(PHP_GRAPH_CONTEXT));
memset(&context->DrawInfo, 0, sizeof(PH_GRAPH_DRAW_INFO));
context->DrawInfo.Width = 3;
context->DrawInfo.Height = 3;
context->DrawInfo.Flags = PH_GRAPH_USE_GRID;
context->DrawInfo.Step = 2;
context->DrawInfo.BackColor = RGB(0x00, 0x00, 0x00);
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(0x00, 0x99, 0x00);
context->DrawInfo.GridWidth = 12;
context->DrawInfo.GridHeight = 12;
context->DrawInfo.GridStart = 0;
context->DrawInfo.TextColor = RGB(0x00, 0xff, 0x00);
context->DrawInfo.TextBoxColor = RGB(0x00, 0x22, 0x00);
context->BufferedContext = NULL;
context->TooltipHandle = NULL;
context->TooltipOldWndProc = NULL;
context->TooltipVisible = FALSE;
context->ValidMonitor = NULL;
memset(&context->MonitorInfo, 0, sizeof(MONITORINFO));
context->MonitorInfo.cbSize = sizeof(MONITORINFO);
*Context = context;
}
VOID PhpFreeGraphContext(
__inout __post_invalid PPHP_GRAPH_CONTEXT Context
)
{
PhFree(Context);
}
static PWSTR PhpMakeGraphTooltipContextAtom()
{
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;
}
}
static VOID PhpCreateBufferedContext(
__in PPHP_GRAPH_CONTEXT Context
)
{
HDC hdc;
PhpDeleteBufferedContext(Context);
GetClientRect(Context->Handle, &Context->BufferedContextRect);
hdc = GetDC(Context->Handle);
Context->BufferedContext = CreateCompatibleDC(hdc);
Context->BufferedBitmap = CreateCompatibleBitmap(
hdc,
Context->BufferedContextRect.right,
Context->BufferedContextRect.bottom
);
ReleaseDC(Context->Handle, hdc);
Context->BufferedOldBitmap = SelectObject(Context->BufferedContext, Context->BufferedBitmap);
}
static VOID PhpUpdateTooltip(
__in PPHP_GRAPH_CONTEXT Context
)
{
POINT point;
RECT windowRect;
HWND hwnd;
TOOLINFO toolInfo = { sizeof(toolInfo) };
HMONITOR monitor;
GetCursorPos(&point);
GetWindowRect(Context->Handle, &windowRect);
if (
point.x < windowRect.left || point.x >= windowRect.right ||
point.y < windowRect.top || point.y >= windowRect.bottom
)
return;
hwnd = WindowFromPoint(point);
if (hwnd != Context->Handle)
return;
toolInfo.hwnd = Context->Handle;
toolInfo.uId = 1;
if (!Context->TooltipVisible)
{
TRACKMOUSEEVENT trackMouseEvent = { sizeof(trackMouseEvent) };
SendMessage(Context->TooltipHandle, TTM_TRACKACTIVATE, TRUE, (LPARAM)&toolInfo);
trackMouseEvent.dwFlags = TME_LEAVE;
trackMouseEvent.hwndTrack = Context->Handle;
TrackMouseEvent(&trackMouseEvent);
Context->ValidMonitor = NULL; // force a refresh of monitor info
}
// Update monitor information only if the tooltip has moved onto another monitor.
monitor = MonitorFromPoint(point, MONITOR_DEFAULTTONEAREST);
if (Context->ValidMonitor != monitor)
{
GetMonitorInfo(monitor, &Context->MonitorInfo);
Context->ValidMonitor = monitor;
}
// Add an offset to fix the case where the user moves the mouse to the bottom-right.
point.x += 12;
point.y += 12;
SendMessage(Context->TooltipHandle, TTM_TRACKPOSITION, 0, MAKELONG(point.x, point.y));
}
static LRESULT CALLBACK PhpTooltipWndProc(
__in HWND hwnd,
__in UINT uMsg,
__in WPARAM wParam,
__in LPARAM lParam
)
{
PPHP_GRAPH_CONTEXT context;
WNDPROC oldWndProc;
context = (PPHP_GRAPH_CONTEXT)GetProp(hwnd, PhpMakeGraphTooltipContextAtom());
oldWndProc = context->TooltipOldWndProc;
switch (uMsg)
{
case WM_DESTROY:
{
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc);
RemoveProp(hwnd, PhpMakeGraphTooltipContextAtom());
}
break;
case WM_MOVE:
{
TOOLINFO toolInfo = { sizeof(toolInfo) };
RECT windowRect;
BOOLEAN needsMove;
if (CallWindowProc(oldWndProc, hwnd, TTM_GETCURRENTTOOL, 0, (LPARAM)&toolInfo))
{
GetWindowRect(hwnd, &windowRect);
needsMove = FALSE;
// Make sure the tooltip isn't off-screen.
if (windowRect.right > context->MonitorInfo.rcWork.right)
{
windowRect.left = context->MonitorInfo.rcWork.right - (windowRect.right - windowRect.left);
needsMove = TRUE;
}
if (windowRect.bottom >context->MonitorInfo.rcWork.bottom)
{
windowRect.top = context->MonitorInfo.rcWork.bottom - (windowRect.bottom - windowRect.top);
needsMove = TRUE;
}
if (needsMove)
{
SetWindowPos(hwnd, HWND_TOPMOST, windowRect.left, windowRect.top,
windowRect.right - windowRect.left, windowRect.bottom - windowRect.top,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOOWNERZORDER);
}
}
}
break;
}
return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam);
}
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_CREATE:
{
context->Handle = hwnd;
}
break;
case WM_DESTROY:
{
if (context->TooltipHandle)
DestroyWindow(context->TooltipHandle);
PhpDeleteBufferedContext(context);
PhpFreeGraphContext(context);
SetWindowLongPtr(hwnd, 0, (LONG_PTR)NULL);
}
break;
case WM_SIZE:
{
// Force a re-create of the buffered context.
PhpCreateBufferedContext(context);
SendMessage(hwnd, GCM_DRAW, 0, 0);
}
break;
case WM_PAINT:
{
PAINTSTRUCT paintStruct;
RECT clientRect;
HDC hdc;
if (hdc = BeginPaint(hwnd, &paintStruct))
{
GetClientRect(hwnd, &clientRect);
if (context->BufferedContext)
{
BitBlt(hdc, 0, 0, clientRect.right, clientRect.bottom, context->BufferedContext, 0, 0, SRCCOPY);
}
EndPaint(hwnd, &paintStruct);
}
}
break;
case WM_ERASEBKGND:
return 1;
case WM_NOTIFY:
{
LPNMHDR header = (LPNMHDR)lParam;
switch (header->code)
{
case TTN_GETDISPINFO:
{
LPNMTTDISPINFO dispInfo = (LPNMTTDISPINFO)header;
POINT point;
RECT clientRect;
PH_GRAPH_GETTOOLTIPTEXT getTooltipText;
GetCursorPos(&point);
MapWindowPoints(NULL, hwnd, &point, 1);
GetClientRect(hwnd, &clientRect);
getTooltipText.Header.hwndFrom = hwnd;
getTooltipText.Header.code = GCN_GETTOOLTIPTEXT;
getTooltipText.Index = (clientRect.right - point.x) / 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;
case TTN_SHOW:
context->TooltipVisible = TRUE;
break;
case TTN_POP:
context->TooltipVisible = FALSE;
break;
}
}
break;
case WM_MOUSEMOVE:
{
if (context->TooltipHandle)
{
PhpUpdateTooltip(context);
}
}
break;
case WM_MOUSELEAVE:
{
if (context->TooltipHandle)
{
TOOLINFO toolInfo = { sizeof(toolInfo) };
toolInfo.hwnd = hwnd;
toolInfo.uId = 1;
SendMessage(context->TooltipHandle, TTM_TRACKACTIVATE, FALSE, (LPARAM)&toolInfo);
}
}
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.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) / 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:
{
if (!context->BufferedContext)
PhpCreateBufferedContext(context);
context->DrawInfo.Width = context->BufferedContextRect.right;
context->DrawInfo.Height = context->BufferedContextRect.bottom;
{
PH_GRAPH_GETDRAWINFO getDrawInfo;
getDrawInfo.Header.hwndFrom = hwnd;
getDrawInfo.Header.code = GCN_GETDRAWINFO;
getDrawInfo.DrawInfo = &context->DrawInfo;
SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&getDrawInfo);
}
PhDrawGraph(context->BufferedContext, &context->DrawInfo);
}
return TRUE;
case GCM_MOVEGRID:
{
LONG increment = (LONG)wParam;
context->DrawInfo.GridStart += increment;
}
return TRUE;
case GCM_GETBUFFEREDCONTEXT:
return (LRESULT)context->BufferedContext;
case GCM_SETTOOLTIP:
{
if (wParam)
{
TOOLINFO toolInfo = { sizeof(toolInfo) };
context->TooltipHandle = CreateWindow(
TOOLTIPS_CLASS,
L"",
WS_POPUP | TTS_NOPREFIX,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
PhLibImageBase,
NULL
);
context->TooltipOldWndProc = (WNDPROC)GetWindowLongPtr(context->TooltipHandle, GWLP_WNDPROC);
SetProp(context->TooltipHandle, PhpMakeGraphTooltipContextAtom(), (HANDLE)context);
SetWindowLongPtr(context->TooltipHandle, GWLP_WNDPROC, (LONG_PTR)PhpTooltipWndProc);
SetWindowPos(context->TooltipHandle, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
toolInfo.uFlags = TTF_ABSOLUTE | TTF_TRACK;
toolInfo.hwnd = hwnd;
toolInfo.uId = 1;
toolInfo.lpszText = LPSTR_TEXTCALLBACK;
SendMessage(context->TooltipHandle, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
// Allow newlines (-1 doesn't work)
// MAXINT doesn't work either on high DPI configurations for some reason.
SendMessage(context->TooltipHandle, TTM_SETMAXTIPWIDTH, 0, 4096);
}
else
{
DestroyWindow(context->TooltipHandle);
context->TooltipHandle = NULL;
}
}
return TRUE;
case GCM_UPDATETOOLTIP:
{
if (!context->TooltipHandle)
return FALSE;
if (context->TooltipVisible)
{
TOOLINFO toolInfo = { sizeof(toolInfo) };
toolInfo.hwnd = hwnd;
toolInfo.uId = 1;
toolInfo.lpszText = LPSTR_TEXTCALLBACK;
SendMessage(context->TooltipHandle, TTM_UPDATETIPTEXT, 0, (LPARAM)&toolInfo);
}
else
{
PhpUpdateTooltip(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);
}