Windows2003-3790/windows/advcore/duser/engine/lava/spy.cpp
2020-09-30 16:53:55 +02:00

934 lines
25 KiB
C++
Raw Blame History

#include "stdafx.h"
#include "Lava.h"
#include "Spy.h"
#include "..\DUser\resource.h"
#if DBG
const UINT IDC_GADGETTREE = 1;
const int cxValue = 80;
const int cxBorder = 5;
const int cyBorder = 5;
/***************************************************************************\
*****************************************************************************
*
* class Spy
*
*****************************************************************************
\***************************************************************************/
PRID Spy::s_pridLink = 0;
ATOM Spy::s_atom = NULL;
HFONT Spy::s_hfntDesc = NULL;
HFONT Spy::s_hfntDescBold = NULL;
HBRUSH Spy::s_hbrOutline = NULL;
int Spy::s_cyLinePxl = 0;
DWORD Spy::g_tlsSpy = (DWORD) -1;
CritLock Spy::s_lockList;
GList<Spy> Spy::s_lstSpys;
static const GUID guidLink = { 0xd5818900, 0xaf18, 0x4c98, { 0x87, 0x20, 0x5a, 0x32, 0x47, 0xa3, 0x1, 0x78 } }; // {D5818900-AF18-4c98-8720-5A3247A30178}
//------------------------------------------------------------------------------
Spy::Spy()
{
}
//------------------------------------------------------------------------------
Spy::~Spy()
{
s_lockList.Enter();
s_lstSpys.Unlink(this);
s_lockList.Leave();
}
//------------------------------------------------------------------------------
BOOL
Spy::BuildSpy(HWND hwndParent, HGADGET hgadRoot, HGADGET hgadSelect)
{
BOOL fSuccess = FALSE;
Spy * pSpy, * pSpyCur;
s_lockList.Enter();
//
// Perform first-time initialization for Spy
//
if (g_tlsSpy == -1) {
//
// Allocate a TLS slot for Spy. This is DEBUG only, so we don't worry about
// the extra cost. However, if this ever becomes on in RETAIL, we need to
// create a SubTread for Lava and add a Spy slot.
//
g_tlsSpy = TlsAlloc();
if (g_tlsSpy == -1) {
goto Exit;
}
//
// Initialize CommCtrl.
//
INITCOMMONCONTROLSEX icc;
icc.dwSize = sizeof(icc);
icc.dwICC = ICC_TREEVIEW_CLASSES;
if (!InitCommonControlsEx(&icc)) {
goto Exit;
}
}
AssertMsg(::GetGadget(hgadRoot, GG_PARENT) == NULL, "Ensure Root Gadget");
//
// Each Gadget subtree can only be spied on once because there are
// back-pointers from each Gadget to the corresponding HTREEITEM's. Need to
// check if this Gadget subtree is already is being spied on.
//
pSpyCur = s_lstSpys.GetHead();
while (pSpyCur != NULL) {
if (pSpyCur->m_hgadRoot == hgadRoot) {
//
// Already exists, so don't open another Spy.
//
SetForegroundWindow(pSpyCur->m_hwnd);
goto Exit;
}
pSpyCur = pSpyCur->GetNext();
}
//
// Register a WNDCLASS to use
//
if (s_atom == NULL) {
WNDCLASSEX wcex;
ZeroMemory(&wcex, sizeof(wcex));
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = RawSpyWndProc;
wcex.hInstance = g_hDll;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
wcex.lpszClassName = "GadgetSpy (Inside)";
s_atom = RegisterClassEx(&wcex);
if (s_atom == NULL) {
goto Exit;
}
}
//
// Create GDI objects used in painting
//
if (s_hfntDesc == NULL) {
s_hfntDesc = UtilBuildFont(L"Tahoma", 85, FS_NORMAL, NULL);
HDC hdc = GetGdiCache()->GetCompatibleDC();
HFONT hfntOld = (HFONT) SelectObject(hdc, s_hfntDesc);
TEXTMETRIC tm;
GetTextMetrics(hdc, &tm);
s_cyLinePxl = tm.tmHeight;
SelectObject(hdc, hfntOld);
GetGdiCache()->ReleaseCompatibleDC(hdc);
}
if (s_hfntDescBold == NULL) {
s_hfntDescBold = UtilBuildFont(L"Tahoma", 85, FS_BOLD, NULL);
}
if (s_hbrOutline == NULL) {
s_hbrOutline = CreateSolidBrush(GetSysColor(COLOR_3DSHADOW));
}
if (s_pridLink == 0) {
s_pridLink = RegisterGadgetProperty(&guidLink);
}
//
// Create a new Spy instance and HWND
//
pSpy = ProcessNew(Spy);
if (pSpy == NULL) {
goto Exit;
}
pSpy->m_hgadMsg = CreateGadget(NULL, GC_MESSAGE, RawEventProc, pSpy);
pSpy->m_hgadRoot = hgadRoot;
Verify(TlsSetValue(g_tlsSpy, pSpy));
{
RECT rcParentPxl;
GetWindowRect(hwndParent, &rcParentPxl);
HWND hwndSpy = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE,
(LPCTSTR) s_atom, NULL,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, rcParentPxl.left + 20, rcParentPxl.top + 20,
300, 500, hwndParent, NULL, g_hDll, NULL);
if (hwndSpy == NULL) {
ProcessDelete(Spy, pSpy);
goto Exit;
}
pSpy->UpdateTitle();
ShowWindow(hwndSpy, SW_SHOW);
UpdateWindow(hwndSpy);
}
//
// Select the specified Gadget as a starting point. We want to check
// if this HGADGET is actually a valid child since it may have been
// "grabbed" at an earlier time and no longer be valid.
//
if (hgadSelect) {
CheckIsChildData cicd;
cicd.hgadCheck = hgadSelect;
cicd.fChild = FALSE;
Verify(EnumGadgets(hgadRoot, EnumCheckIsChild, &cicd, GENUM_DEEPCHILD));
if (cicd.fChild) {
HTREEITEM htiSelect;
if (GetGadgetProperty(hgadSelect, s_pridLink, (void **) &htiSelect)) {
AssertMsg(htiSelect != NULL, "Must have valid HTREEITEM");
if (!TreeView_SelectItem(pSpy->m_hwndTree, htiSelect)) {
Trace("SPY: Unable to select default Gadget\n");
}
}
}
}
s_lstSpys.Add(pSpy);
fSuccess = TRUE;
Exit:
s_lockList.Leave();
return fSuccess;
}
//------------------------------------------------------------------------------
BOOL CALLBACK
Spy::EnumCheckIsChild(HGADGET hgad, void * pvData)
{
CheckIsChildData * pcicd = (CheckIsChildData *) pvData;
if (hgad == pcicd->hgadCheck) {
pcicd->fChild = TRUE;
return FALSE; // No longer need to enumerate
}
return TRUE;
}
//------------------------------------------------------------------------------
void
Spy::UpdateTitle()
{
TCHAR szTitle[256];
wsprintf(szTitle, "Root HGADGET = 0x%p, %d Gadgets", m_hgadRoot, m_cItems);
SetWindowText(m_hwnd, szTitle);
}
//------------------------------------------------------------------------------
LRESULT CALLBACK
Spy::RawSpyWndProc(HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
Spy * pSpy = (Spy *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (pSpy == NULL) {
//
// Creating a new Spy HWND, so hook up to the Spy object that was
// previously created.
//
pSpy = reinterpret_cast<Spy *> (TlsGetValue(g_tlsSpy));
AssertMsg(pSpy != NULL, "Ensure already created new Spy instance");
TlsSetValue(g_tlsSpy, NULL);
pSpy->m_hwnd = hwnd;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) pSpy);
}
AssertMsg(pSpy->m_hwnd == hwnd, "Ensure HWND's match");
return pSpy->SpyWndProc(nMsg, wParam, lParam);
}
//------------------------------------------------------------------------------
LRESULT
Spy::SpyWndProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
switch (nMsg)
{
case WM_CREATE:
if (DefWindowProc(m_hwnd, nMsg, wParam, lParam) == -1) {
return -1;
}
//
// Setup the window
//
AssertMsg(m_hgadRoot != NULL, "Must already have specified Gadget");
RECT rcClient;
GetClientRect(m_hwnd, &rcClient);
m_hwndTree = CreateWindowEx(0, _T("SysTreeView32"),
NULL, WS_CHILD | WS_VISIBLE | TVS_HASBUTTONS | TVS_HASLINES | TVS_SHOWSELALWAYS,
0, 0, rcClient.right, rcClient.bottom, m_hwnd, (HMENU)((UINT_PTR)IDC_GADGETTREE), g_hDll, NULL);
m_hilc = ImageList_LoadImage(g_hDll, MAKEINTRESOURCE(IDB_SPYICON),
16, 1, RGB(128, 0, 128), IMAGE_BITMAP, LR_SHARED);
if ((m_hwndTree == NULL) || (m_hilc == NULL)) {
return -1;
}
TreeView_SetImageList(m_hwndTree, m_hilc, TVSIL_NORMAL);
EnumData ed;
ed.pspy = this;
ed.htiParent = InsertTreeItem(TVI_ROOT, m_hgadRoot);
ed.nLevel = 1;
Verify(EnumGadgets(m_hgadRoot, EnumAddList, &ed, GENUM_SHALLOWCHILD));
m_fValid = TRUE;
TreeView_Expand(m_hwndTree, ed.htiParent, TVE_EXPAND);
m_hgadDetails = m_hgadRoot;
UpdateDetails();
break;
case WM_DESTROY:
SelectGadget(NULL);
::DeleteHandle(m_hgadMsg);
break;
case WM_NCDESTROY:
ProcessDelete(Spy, this);
goto CallDWP;
case WM_SIZE:
if ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED)) {
UpdateLayout();
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(m_hwnd, &ps);
OnPaint(hdc);
EndPaint(m_hwnd, &ps);
}
break;
case WM_NOTIFY:
if (wParam == IDC_GADGETTREE) {
NMHDR * pnm = (NMHDR *) lParam;
if (pnm->code == TVN_SELCHANGED) {
NMTREEVIEW * pnmtv = (NMTREEVIEW *) lParam;
if (m_fValid) {
SelectGadget(GetGadget(pnmtv->itemNew.hItem));
}
break;
} else if (pnm->code == TVN_KEYDOWN) {
NMTVKEYDOWN * pnmtvkd = (NMTVKEYDOWN *) lParam;
if (pnmtvkd->wVKey == VK_APPS) {
DisplayContextMenu(TRUE);
}
} else if (pnm->code == NM_RCLICK) {
DisplayContextMenu(FALSE);
}
}
goto CallDWP;
default:
CallDWP:
return DefWindowProc(m_hwnd, nMsg, wParam, lParam);
}
return 0;
}
//------------------------------------------------------------------------------
HRESULT CALLBACK
Spy::RawEventProc(HGADGET hgadCur, void * pvCur, EventMsg * pmsg)
{
UNREFERENCED_PARAMETER(hgadCur);
Spy * pSpy = (Spy *) pvCur;
return pSpy->EventProc(pmsg);
}
//------------------------------------------------------------------------------
BOOL
IsDescendant(
HGADGET hgadParent,
HGADGET hgadChild)
{
AssertMsg(hgadParent != NULL, "Must have valid parent");
if (hgadChild == hgadParent) {
return TRUE;
} if (hgadChild == NULL) {
return FALSE;
} else {
return IsDescendant(hgadParent, ::GetGadget(hgadChild, GG_PARENT));
}
}
//------------------------------------------------------------------------------
HRESULT
Spy::EventProc(EventMsg * pmsg)
{
switch (GET_EVENT_DEST(pmsg))
{
case GMF_DIRECT:
//
// Our Listener is being destroyed. We need to detach from everything.
//
if (m_hgadRoot != NULL) {
Trace("SPY: Destroying Spy MsgGadget\n");
Verify(EnumGadgets(m_hgadRoot, EnumRemoveLink, NULL, GENUM_DEEPCHILD));
m_hgadRoot = NULL;
}
break;
case GMF_EVENT:
switch (pmsg->nMsg)
{
case GM_DESTROY:
{
GMSG_DESTROY * pmsgD = (GMSG_DESTROY *) pmsg;
if (pmsgD->nCode == GDESTROY_START) {
//
// Gadget is being destroyed
//
Trace("SPY: Destroying Gadget 0x%p\n", pmsg->hgadMsg);
HTREEITEM hti;
if (GetGadgetProperty(pmsg->hgadMsg, s_pridLink, (void **) &hti)) {
AssertMsg(hti != NULL, "Must have valid HTREEITEM");
Verify(EnumGadgets(pmsg->hgadMsg, EnumRemoveLink, NULL, GENUM_DEEPCHILD));
TreeView_DeleteItem(m_hwndTree, hti);
}
}
}
break;
case GM_CHANGERECT:
if (IsDescendant(pmsg->hgadMsg, m_hgadDetails)) {
UpdateDetails();
}
break;
}
}
return DU_S_NOTHANDLED;
}
//------------------------------------------------------------------------------
BOOL CALLBACK
Spy::EnumAddList(HGADGET hgad, void * pvData)
{
EnumData * ped = (EnumData *) pvData;
Spy * pSpy = ped->pspy;
HTREEITEM htiNew = pSpy->InsertTreeItem(ped->htiParent, hgad);
pSpy->m_cItems++;
if (::GetGadget(hgad, GG_TOPCHILD) != NULL) {
EnumData ed;
ed.pspy = pSpy;
ed.htiParent = htiNew;
ed.nLevel = ped->nLevel + 1;
Verify(EnumGadgets(hgad, EnumAddList, &ed, GENUM_SHALLOWCHILD));
if (ped->nLevel <= 2) {
TreeView_Expand(pSpy->m_hwndTree, htiNew, TVE_EXPAND);
}
}
return TRUE;
}
//------------------------------------------------------------------------------
BOOL CALLBACK
Spy::EnumRemoveLink(HGADGET hgad, void * pvData)
{
UNREFERENCED_PARAMETER(pvData);
RemoveGadgetProperty(hgad, s_pridLink);
return TRUE;
}
//------------------------------------------------------------------------------
void
Spy::SelectGadget(HGADGET hgad)
{
m_hgadDetails = hgad;
{
//
// We are bypassinging the normal API's to directly call a
// DEBUG-only function. Need to lock the Context and do FULL handle
// validation.
//
ContextLock cl;
if (!cl.LockNL(ContextLock::edDefer)) {
return;
}
DuVisual * pgadTree = ValidateVisual(hgad);
DuVisual::DEBUG_SetOutline(pgadTree);
}
UpdateDetails();
}
//------------------------------------------------------------------------------
HTREEITEM
Spy::InsertTreeItem(HTREEITEM htiParent, HGADGET hgad)
{
TCHAR szName[1024];
GMSG_QUERYDESC msg;
msg.cbSize = sizeof(msg);
msg.hgadMsg = hgad;
msg.nMsg = GM_QUERY;
msg.nCode = GQUERY_DESCRIPTION;
msg.szName[0] = '\0';
msg.szType[0] = '\0';
if (DUserSendEvent(&msg, 0) == DU_S_COMPLETE) {
if (msg.szName[0] != '\0') {
wsprintf(szName, "0x%p %S: \"%S\"", hgad, msg.szType, msg.szName);
} else {
wsprintf(szName, "0x%p %S", hgad, msg.szType);
}
} else {
wsprintf(szName, "0x%p", hgad);
}
TVINSERTSTRUCT tvis;
tvis.hParent = htiParent;
tvis.hInsertAfter = TVI_LAST;
tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE;
tvis.item.pszText = szName;
tvis.item.iImage = iGadget;
tvis.item.lParam = (LPARAM) hgad;
HTREEITEM htiNew = TreeView_InsertItem(m_hwndTree, &tvis);
if (htiNew != NULL) {
if (AddGadgetMessageHandler(hgad, 0, m_hgadMsg)) {
Verify(SetGadgetProperty(hgad, s_pridLink, htiNew));
} else {
Trace("WARNING: Spy unable to attach handler on 0x%p\n", hgad);
}
}
return htiNew;
}
//------------------------------------------------------------------------------
HGADGET
Spy::GetGadget(HTREEITEM hti)
{
TVITEM tvi;
tvi.hItem = hti;
tvi.mask = TVIF_PARAM | TVIF_HANDLE;
if (TreeView_GetItem(m_hwndTree, &tvi)) {
HGADGET hgadItem = (HGADGET) tvi.lParam;
AssertMsg(hgadItem != NULL, "All items in the tree should have a Gadget");
return hgadItem;
}
return NULL;
}
//------------------------------------------------------------------------------
void
Spy::DisplayContextMenu(BOOL fViaKbd)
{
//
// Locate TreeView item
//
POINT ptPopup;
ZeroMemory(&ptPopup, sizeof(ptPopup));
HTREEITEM hti;
if (fViaKbd) {
//
// Keyboard driven
//
hti = TreeView_GetSelection(m_hwndTree);
if (hti != NULL) {
RECT rc;
TreeView_GetItemRect(m_hwndTree, hti, &rc, TRUE);
ptPopup.x = rc.left;
ptPopup.y = rc.bottom;
ClientToScreen(m_hwndTree, &ptPopup);
}
} else {
//
// Mouse driven
//
TVHITTESTINFO tvht;
DWORD dwPos = GetMessagePos();
ptPopup.x = GET_X_LPARAM(dwPos);
ptPopup.y = GET_Y_LPARAM(dwPos);
tvht.pt = ptPopup;
ScreenToClient(m_hwndTree, &tvht.pt);
hti = TreeView_HitTest(m_hwndTree, &tvht);
}
//
// Now have tree item and popup position
//
if (hti != NULL) {
//
// Get Gadget associated with this item
//
HGADGET hgad = GetGadget(hti);
//
// Create popup menu template
//
HMENU hMenu = CreatePopupMenu();
if (hMenu != NULL) {
BOOL fRes;
const int cmdDetails = 10;
fRes = AppendMenu(hMenu, MF_STRING, cmdDetails, "Details...");
if (fRes) {
UINT nCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_RIGHTBUTTON, ptPopup.x, ptPopup.y, 0, m_hwndTree, NULL);
DestroyMenu(hMenu);
//
// Invoke commands
//
switch (nCmd)
{
case cmdDetails:
{
GMSG_QUERYDETAILS msg;
msg.cbSize = sizeof(msg);
msg.hgadMsg = hgad;
msg.nMsg = GM_QUERY;
msg.nCode = GQUERY_DETAILS;
msg.nType = GQDT_HWND;
msg.hOwner = m_hwndTree;
DUserSendEvent(&msg, 0);
}
break;
}
}
}
}
}
//------------------------------------------------------------------------------
int
Spy::NumLines(int cyPxl) const
{
return (cyPxl - 1) / s_cyLinePxl + 1;
}
//------------------------------------------------------------------------------
void
Spy::UpdateDetails()
{
if (m_hgadDetails == NULL) {
return;
}
RECT rcPxl;
GetGadgetRect(m_hgadDetails, &rcPxl, SGR_CONTAINER);
wsprintf(m_szRect, "(%d, %d)-(%d, %d) %d<> %d",
rcPxl.left, rcPxl.top, rcPxl.right, rcPxl.bottom,
rcPxl.right - rcPxl.left, rcPxl.bottom - rcPxl.top);
GMSG_QUERYDESC msg;
msg.cbSize = sizeof(msg);
msg.hgadMsg = m_hgadDetails;
msg.nMsg = GM_QUERY;
msg.nCode = GQUERY_DESCRIPTION;
msg.szName[0] = '\0';
msg.szType[0] = '\0';
if (DUserSendEvent(&msg, 0) == DU_S_COMPLETE) {
CopyString(m_szName, msg.szName, _countof(m_szName));
CopyString(m_szType, msg.szType, _countof(m_szType));
} else {
m_szName[0] = '\0';
m_szType[0] = '\0';
}
//
// We are bypassinging the normal API's to directly call a
// DEBUG-only function. Need to lock the Context and do FULL handle
// validation.
//
ContextLock cl;
if (cl.LockNL(ContextLock::edNone)) {
DuVisual * pgadTree = ValidateVisual(m_hgadDetails);
AssertMsg(pgadTree != NULL, "Should be a valid DuVisual for Spy");
pgadTree->DEBUG_GetStyleDesc(m_szStyle, _countof(m_szStyle));
UpdateLayoutDesc(FALSE);
InvalidateRect(m_hwnd, NULL, TRUE);
}
}
//------------------------------------------------------------------------------
void
Spy::UpdateLayout()
{
RECT rcClient;
GetClientRect(m_hwnd, &rcClient);
m_sizeWndPxl.cx = rcClient.right;
m_sizeWndPxl.cy = rcClient.bottom;
UpdateLayoutDesc(TRUE);
}
//------------------------------------------------------------------------------
void
Spy::UpdateLayoutDesc(BOOL fForceLayoutDesc)
{
//
// Compute the number of needed lines
//
int cOldLines = m_cLines;
m_cLines = 4;
RECT rcStyle;
rcStyle.left = cxBorder + cxValue;
rcStyle.top = 0;
rcStyle.right = m_sizeWndPxl.cx - cxBorder;
rcStyle.bottom = 10000;
HDC hdc = GetGdiCache()->GetTempDC();
HFONT hfntOld = (HFONT) SelectObject(hdc, s_hfntDesc);
int nHeight = OS()->DrawText(hdc, m_szStyle, (int) wcslen(m_szStyle), &rcStyle,
DT_CALCRECT | DT_LEFT | DT_TOP | DT_WORDBREAK);
SelectObject(hdc, hfntOld);
GetGdiCache()->ReleaseTempDC(hdc);
m_cLines += NumLines(nHeight);
//
// Move the Tree to provide space for the description
//
if ((cOldLines != m_cLines) || fForceLayoutDesc) {
m_cyDescPxl = s_cyLinePxl * m_cLines + 10;
m_fShowDesc = m_sizeWndPxl.cy > m_cyDescPxl;
SIZE sizeTree;
sizeTree.cx = m_sizeWndPxl.cx;
sizeTree.cy = m_fShowDesc ? (m_sizeWndPxl.cy - m_cyDescPxl) : m_sizeWndPxl.cy;
MoveWindow(m_hwndTree, 0, 0, sizeTree.cx, sizeTree.cy, TRUE);
}
}
//------------------------------------------------------------------------------
void
Spy::OnPaint(HDC hdc)
{
HFONT hfntOld = (HFONT) SelectObject(hdc, s_hfntDesc);
int nOldMode = SetBkMode(hdc, TRANSPARENT);
RECT rcOutline;
rcOutline.left = 2;
rcOutline.top = m_sizeWndPxl.cy - m_cyDescPxl + 2;
rcOutline.right = m_sizeWndPxl.cx - 1;
rcOutline.bottom = m_sizeWndPxl.cy - 1;
GdDrawOutlineRect(hdc, &rcOutline, s_hbrOutline);
POINT pt;
pt.x = cxBorder;
pt.y = m_sizeWndPxl.cy - m_cyDescPxl + cyBorder;
// NOTE: m_cLines should equal the number of lines displayed here
PaintLine(hdc, &pt, "HGADGET: ", m_hgadDetails);
PaintLine(hdc, &pt, "Name: ", m_szName, FALSE, s_hfntDescBold);
PaintLine(hdc, &pt, "Type: ", m_szType);
PaintLine(hdc, &pt, "Rectangle: ", m_szRect);
PaintLine(hdc, &pt, "Style: ", m_szStyle, TRUE);
SetBkMode(hdc, nOldMode);
SelectObject(hdc, hfntOld);
}
class CTempSelectFont
{
public:
CTempSelectFont(HDC hdc, HFONT hfnt)
{
m_hdc = hdc;
m_fSelect = (hfnt != NULL);
if (m_fSelect) {
m_hfntOld = (HFONT) SelectObject(m_hdc, hfnt);
}
}
~CTempSelectFont()
{
if (m_fSelect) {
SelectObject(m_hdc, m_hfntOld);
}
}
BOOL m_fSelect;
HDC m_hdc;
HFONT m_hfntOld;
};
//------------------------------------------------------------------------------
void
Spy::PaintLine(HDC hdc, POINT * pptOffset, LPCTSTR pszName, LPCTSTR pszText, HFONT hfnt)
{
TextOut(hdc, pptOffset->x, pptOffset->y, pszName, (int) _tcslen(pszName));
CTempSelectFont tsf(hdc, hfnt);
TextOut(hdc, pptOffset->x + cxValue, pptOffset->y, pszText, (int) _tcslen(pszText));
pptOffset->y += s_cyLinePxl;
}
//------------------------------------------------------------------------------
void
Spy::PaintLine(HDC hdc, POINT * pptOffset, LPCTSTR pszName, LPCWSTR pszText, BOOL fMultiline, HFONT hfnt)
{
TextOut(hdc, pptOffset->x, pptOffset->y, pszName, (int) _tcslen(pszName));
CTempSelectFont tsf(hdc, hfnt);
if (fMultiline) {
RECT rcStyle;
rcStyle.left = pptOffset->x + cxValue;
rcStyle.top = pptOffset->y;
rcStyle.right = m_sizeWndPxl.cx - cxBorder;
rcStyle.bottom = 10000;
int nHeight = OS()->DrawText(hdc, pszText, (int) wcslen(pszText), &rcStyle,
DT_LEFT | DT_TOP | DT_WORDBREAK);
pptOffset->y += NumLines(nHeight) * s_cyLinePxl;
} else {
OS()->TextOut(hdc, pptOffset->x + cxValue, pptOffset->y, pszText, (int) wcslen(pszText));
pptOffset->y += s_cyLinePxl;
}
}
//------------------------------------------------------------------------------
void
Spy::PaintLine(HDC hdc, POINT * pptOffset, LPCTSTR pszName, int nValue, HFONT hfnt)
{
TextOut(hdc, pptOffset->x, pptOffset->y, pszName, (int) _tcslen(pszName));
CTempSelectFont tsf(hdc, hfnt);
TCHAR szValue[20];
_itot(nValue, szValue, 10);
TextOut(hdc, pptOffset->x + cxValue, pptOffset->y, szValue, (int) _tcslen(szValue));
pptOffset->y += s_cyLinePxl;
}
//------------------------------------------------------------------------------
void
Spy::PaintLine(HDC hdc, POINT * pptOffset, LPCTSTR pszName, void * pvValue, HFONT hfnt)
{
TextOut(hdc, pptOffset->x, pptOffset->y, pszName, (int) _tcslen(pszName));
CTempSelectFont tsf(hdc, hfnt);
TCHAR szValue[20];
wsprintf(szValue, "0x%p", pvValue);
TextOut(hdc, pptOffset->x + cxValue, pptOffset->y, szValue, (int) _tcslen(szValue));
pptOffset->y += s_cyLinePxl;
}
#endif // DBG