833 lines
19 KiB
C++
833 lines
19 KiB
C++
|
//-----------------------------------------------------------------------------
|
||
|
// File: cdevicecontrol.cpp
|
||
|
//
|
||
|
// Desc: CDeviceControl is a class that encapsulate the functionality of a
|
||
|
// device control (or a callout). CDeviceView accesses it to retrieve/
|
||
|
// save information about the control.
|
||
|
//
|
||
|
// Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
#include "common.hpp"
|
||
|
|
||
|
|
||
|
CDeviceControl::CDeviceControl(CDeviceUI &ui, CDeviceView &view) :
|
||
|
m_ui(ui),
|
||
|
m_view(view),
|
||
|
m_bHighlight(FALSE),
|
||
|
m_ptszCaption(NULL),
|
||
|
m_dwDrawTextFlags(0),
|
||
|
m_FontHeight(-1),
|
||
|
m_bCalledCalcCallout(FALSE),
|
||
|
m_bPlacedOnlyFirstCorner(FALSE),
|
||
|
m_bInit(FALSE),
|
||
|
m_dwCalloutAlign(CAF_TOPLEFT),
|
||
|
m_nLinePoints(0),
|
||
|
m_dwDeviceControlOffset((DWORD)-1),
|
||
|
m_bOffsetAssigned(FALSE),
|
||
|
m_pbmOverlay(NULL),
|
||
|
m_pbmHitMask(NULL),
|
||
|
m_ptszOverlayPath(NULL),
|
||
|
m_bCaptionClipped(FALSE)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CDeviceControl::~CDeviceControl()
|
||
|
{
|
||
|
DEVICEUINOTIFY uin;
|
||
|
uin.from = DEVUINFROM_CONTROL;
|
||
|
uin.control.pControl = (CDeviceControl *)this;
|
||
|
uin.msg = DEVUINM_ONCONTROLDESTROY;
|
||
|
m_ui.Notify(uin);
|
||
|
if (m_ptszCaption)
|
||
|
free(m_ptszCaption);
|
||
|
delete m_pbmOverlay;
|
||
|
delete m_ptszOverlayPath;
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::SetCaption(LPCTSTR tszCaption, BOOL bFixed)
|
||
|
{
|
||
|
LPTSTR tszNewCaption = NULL;
|
||
|
|
||
|
m_bFixed = bFixed;
|
||
|
|
||
|
if (tszCaption != NULL)
|
||
|
{
|
||
|
tszNewCaption = _tcsdup(tszCaption);
|
||
|
|
||
|
if (tszNewCaption == NULL)
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
free(m_ptszCaption);
|
||
|
m_ptszCaption = tszNewCaption;
|
||
|
tszNewCaption = NULL;
|
||
|
|
||
|
CalcCallout();
|
||
|
Invalidate();
|
||
|
}
|
||
|
|
||
|
LPCTSTR CDeviceControl::GetCaption()
|
||
|
{
|
||
|
return (LPCTSTR)m_ptszCaption;
|
||
|
}
|
||
|
|
||
|
BOOL CDeviceControl::HitControl(POINT point)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
DEVCTRLHITRESULT CDeviceControl::HitTest(POINT test)
|
||
|
{
|
||
|
if (!m_bInit)
|
||
|
return DCHT_NOHIT;
|
||
|
|
||
|
if (m_ui.InEditMode() &&
|
||
|
PtInRect(&m_rectCalloutMax, test))
|
||
|
return DCHT_MAXRECT;
|
||
|
|
||
|
PrepCallout();
|
||
|
|
||
|
if (PtInRect(&m_rectCallout, test))
|
||
|
return DCHT_CAPTION;
|
||
|
|
||
|
if (HitControl(test))
|
||
|
return DCHT_CONTROL;
|
||
|
|
||
|
return DCHT_NOHIT;
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::Init()
|
||
|
{
|
||
|
m_uin.from = DEVUINFROM_CONTROL;
|
||
|
m_uin.control.pControl = this;
|
||
|
|
||
|
CalcCallout();
|
||
|
|
||
|
m_bInit = TRUE;
|
||
|
}
|
||
|
|
||
|
// We will have to know the view's scrolling offset to adjust the tooltip's position.
|
||
|
void CDeviceControl::OnMouseOver(POINT point)
|
||
|
{
|
||
|
// Tooltip only if the callout text is clipped.
|
||
|
if (m_bCaptionClipped)
|
||
|
{
|
||
|
TOOLTIPINITPARAM ttip;
|
||
|
ttip.hWndParent = GetParent(m_view.m_hWnd); // Parent is the page window.
|
||
|
ttip.iSBWidth = 0;
|
||
|
ttip.dwID = m_dwDeviceControlOffset;
|
||
|
ttip.hWndNotify = m_view.m_hWnd;
|
||
|
ttip.tszCaption = GetCaption();
|
||
|
CFlexToolTip::UpdateToolTipParam(ttip);
|
||
|
} else
|
||
|
CFlexWnd::s_ToolTip.SetToolTipParent(NULL);
|
||
|
|
||
|
m_uin.msg = DEVUINM_MOUSEOVER;
|
||
|
m_ui.Notify(m_uin);
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::OnClick(POINT point, BOOL bLeft, BOOL bDoubleClick)
|
||
|
{
|
||
|
//@@BEGIN_MSINTERNAL
|
||
|
#ifdef DDKBUILD
|
||
|
if (!bLeft && m_ui.InEditMode())
|
||
|
{
|
||
|
// If right click in edit mode, pop up the edit menu.
|
||
|
m_view.EditMenu(point, this);
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
//@@END_MSINTERNAL
|
||
|
|
||
|
// If this control is not assigned, and we are in view mode, we should not do anything (highlight).
|
||
|
if (!lstrcmp(m_ptszCaption, g_tszUnassignedControlCaption) && !m_ui.m_uig.InEditMode())
|
||
|
return;
|
||
|
|
||
|
m_uin.msg = bDoubleClick ? DEVUINM_DOUBLECLICK : DEVUINM_CLICK;
|
||
|
m_uin.click.bLeftButton = bLeft;
|
||
|
m_ui.Notify(m_uin);
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::Unpopulate()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::Highlight(BOOL bHighlight)
|
||
|
{
|
||
|
if (m_bHighlight == bHighlight)
|
||
|
return;
|
||
|
|
||
|
// If the callout text is the default text, no action is assigned, and we don't highlight it.
|
||
|
//@@BEGIN_MSINTERNAL
|
||
|
// ISSUE-2000/12/21-MarcAnd This breaks highlighting of unassigned controls
|
||
|
// When you're trying to assign
|
||
|
//@@END_MSINTERNAL
|
||
|
if (!lstrcmp(m_ptszCaption, g_tszUnassignedControlCaption) && bHighlight && !m_ui.m_uig.InEditMode())
|
||
|
return;
|
||
|
|
||
|
m_bHighlight = bHighlight;
|
||
|
|
||
|
// If the view has scrolling enabled, we need to adjust the scroll
|
||
|
// bar position to make this callout visible.
|
||
|
if (bHighlight)
|
||
|
m_view.ScrollToMakeControlVisible(m_rectCalloutMax);
|
||
|
|
||
|
CalcCallout();
|
||
|
|
||
|
// We do not invalidate rectangle if we are unhighlighting. Let CDeviceView handle that.
|
||
|
if (bHighlight) Invalidate();
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::GetInfo(GUID &rGuid, DWORD &rdwOffset)
|
||
|
{
|
||
|
m_ui.GetDeviceInstanceGuid(rGuid);
|
||
|
rdwOffset = m_dwDeviceControlOffset;
|
||
|
}
|
||
|
|
||
|
BOOL CDeviceControl::PrepCaption()
|
||
|
{
|
||
|
if (m_ptszCaption != NULL)
|
||
|
return TRUE;
|
||
|
m_ptszCaption = _tcsdup(g_tszUnassignedControlCaption);
|
||
|
return m_ptszCaption != NULL;
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::PrepLinePoints()
|
||
|
{
|
||
|
if (m_nLinePoints > 0)
|
||
|
return;
|
||
|
m_nLinePoints = 1;
|
||
|
POINT pt = {0, 0};
|
||
|
if (m_dwCalloutAlign & CAF_LEFT)
|
||
|
pt.x = m_rectCalloutMax.left;
|
||
|
if (m_dwCalloutAlign & CAF_RIGHT)
|
||
|
pt.x = m_rectCalloutMax.right - 1;
|
||
|
if (m_dwCalloutAlign & CAF_TOP)
|
||
|
pt.y = m_rectCalloutMax.top;
|
||
|
if (m_dwCalloutAlign & CAF_BOTTOM)
|
||
|
pt.y = m_rectCalloutMax.bottom - 1;
|
||
|
if (!(m_dwCalloutAlign & (CAF_LEFT | CAF_RIGHT)))
|
||
|
pt.x = (m_rectCalloutMax.left + m_rectCalloutMax.right - 1) / 2;
|
||
|
if (!(m_dwCalloutAlign & (CAF_BOTTOM | CAF_TOP)))
|
||
|
pt.y = (m_rectCalloutMax.top + m_rectCalloutMax.bottom - 1) / 2;
|
||
|
m_rgptLinePoint[0] = pt;
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::PrepCallout()
|
||
|
{
|
||
|
if (m_bCalledCalcCallout)
|
||
|
return;
|
||
|
CalcCallout();
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::PrepFont()
|
||
|
{
|
||
|
if (m_FontHeight != -1)
|
||
|
return;
|
||
|
|
||
|
HDC hDC = CreateCompatibleDC(NULL);
|
||
|
if (hDC != NULL)
|
||
|
{
|
||
|
RECT rect = {0, 0, 500, 1};
|
||
|
{
|
||
|
CPaintHelper ph(m_ui.m_uig, hDC);
|
||
|
ph.SetFont(UIF_CALLOUT);
|
||
|
m_FontHeight = DrawText(hDC, _T("Testify"), -1, &rect, m_dwDrawTextFlags);
|
||
|
}
|
||
|
DeleteDC(hDC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::CalcCallout()
|
||
|
{
|
||
|
m_bCalledCalcCallout = TRUE;
|
||
|
|
||
|
RECT max = m_rectCalloutMax;
|
||
|
InflateRect(&max, -1, -1);
|
||
|
RECT rect = max;
|
||
|
rect.bottom = rect.top + 1;
|
||
|
|
||
|
PrepFont();
|
||
|
|
||
|
HDC hDC = CreateCompatibleDC(NULL);
|
||
|
|
||
|
{
|
||
|
CPaintHelper ph(m_ui.m_uig, hDC);
|
||
|
ph.SetFont(UIF_CALLOUT);
|
||
|
|
||
|
// We make sure the max rect height is at least same as the font requires.
|
||
|
m_dwDrawTextFlags = DT_SINGLELINE | DT_CALCRECT | DT_NOPREFIX | DT_END_ELLIPSIS | DT_EDITCONTROL;
|
||
|
RECT hrect = rect;
|
||
|
DrawText(hDC, m_ptszCaption, -1, &hrect, m_dwDrawTextFlags);
|
||
|
if (hrect.bottom > max.bottom) max.bottom = hrect.bottom;
|
||
|
|
||
|
m_dwDrawTextFlags = DT_WORDBREAK | DT_CALCRECT | DT_NOPREFIX | DT_END_ELLIPSIS | DT_EDITCONTROL;
|
||
|
|
||
|
// first, drawtext/calcrect into the temporary rect
|
||
|
if (!PrepCaption())
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int th = DrawText(hDC, m_ptszCaption, -1, &rect, m_dwDrawTextFlags);
|
||
|
|
||
|
m_bCaptionClipped = rect.bottom > max.bottom || rect.right > max.right; // Set clipped flag.
|
||
|
|
||
|
BOOL bSingleTextLine = th <= m_FontHeight;
|
||
|
|
||
|
if (rect.right > max.right)
|
||
|
{
|
||
|
bSingleTextLine = TRUE;
|
||
|
rect.right = max.right;
|
||
|
}
|
||
|
|
||
|
if (bSingleTextLine)
|
||
|
m_dwDrawTextFlags &= ~DT_WORDBREAK;
|
||
|
|
||
|
m_dwDrawTextFlags &= ~DT_CALCRECT;
|
||
|
|
||
|
RECT rect2 = rect;
|
||
|
if (rect2.bottom > max.bottom)
|
||
|
rect2.bottom = max.bottom;
|
||
|
th = DrawText(hDC, m_ptszCaption, -1, &rect2, m_dwDrawTextFlags);
|
||
|
int ith = (th / m_FontHeight) * m_FontHeight;
|
||
|
//@@BEGIN_MSINTERNAL
|
||
|
//LTRACE(QSAFESTR(m_ptszCaption));
|
||
|
//LTRACE(" max = %s", RECTDIMSTR(max));
|
||
|
//LTRACE("!rect = %s", RECTDIMSTR(rect));
|
||
|
//@@END_MSINTERNAL
|
||
|
rect.bottom = rect.top + ith + 1;
|
||
|
//@@BEGIN_MSINTERNAL
|
||
|
//LTRACE(" rect = %s", RECTDIMSTR(rect));
|
||
|
//LTRACE("rect2 = %s", RECTDIMSTR(rect2));
|
||
|
//LTRACE("th = %d, ith = %d, m_FontHeight = %d", th, ith, m_FontHeight);
|
||
|
//@@END_MSINTERNAL
|
||
|
}
|
||
|
|
||
|
DeleteDC(hDC);
|
||
|
hDC = NULL;
|
||
|
|
||
|
if (rect.bottom > max.bottom)
|
||
|
rect.bottom = max.bottom;
|
||
|
|
||
|
assert(rect.right <= max.right);
|
||
|
assert(rect.bottom <= max.bottom);
|
||
|
|
||
|
PrepLinePoints();
|
||
|
POINT adj = {0, 0};
|
||
|
|
||
|
assert(rect.left == max.left);
|
||
|
assert(rect.top == max.top);
|
||
|
|
||
|
int w = rect.right - rect.left;
|
||
|
int h = rect.bottom - rect.top;
|
||
|
int mw = max.right - max.left;
|
||
|
int mh = max.bottom - max.top;
|
||
|
int dw = mw - w, dh = mh - h;
|
||
|
int cx = mw / 2 + max.left, cy = mh / 2 + max.top;
|
||
|
int cl = cx - w / 2, ct = cy - h / 2;
|
||
|
|
||
|
assert(dw >= 0);
|
||
|
assert(dh >= 0);
|
||
|
|
||
|
if (m_dwCalloutAlign & CAF_RIGHT && rect.right < max.right)
|
||
|
adj.x = max.right - rect.right;
|
||
|
if (m_dwCalloutAlign & CAF_BOTTOM && rect.bottom < max.bottom)
|
||
|
adj.y = max.bottom - rect.bottom;
|
||
|
if (!(m_dwCalloutAlign & (CAF_RIGHT | CAF_LEFT)) && w < mw && rect.left != cl)
|
||
|
adj.x = cl - rect.left;
|
||
|
if (!(m_dwCalloutAlign & (CAF_BOTTOM | CAF_TOP)) && h < mh && rect.top != ct)
|
||
|
adj.y = ct - rect.top;
|
||
|
|
||
|
OffsetRect(&rect, adj.x, adj.y);
|
||
|
|
||
|
InflateRect(&rect, 1, 1);
|
||
|
|
||
|
m_rectCallout = rect;
|
||
|
}
|
||
|
|
||
|
BOOL CDeviceControl::DrawOverlay(HDC hDC)
|
||
|
{
|
||
|
if (m_pbmOverlay == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
return m_pbmOverlay->Blend(hDC, m_ptOverlay);
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::OnPaint(HDC hDC)
|
||
|
{
|
||
|
if (!m_bInit)
|
||
|
return;
|
||
|
|
||
|
// If we are in view mode and the callout is not assigned, don't draw anything.
|
||
|
if (!m_ui.m_uig.InEditMode() && !lstrcmp(m_ptszCaption, g_tszUnassignedControlCaption))
|
||
|
return;
|
||
|
|
||
|
PrepCallout();
|
||
|
|
||
|
CPaintHelper ph(m_ui.m_uig, hDC);
|
||
|
UIELEMENT eCallout = m_bHighlight ? UIE_CALLOUTHIGH : UIE_CALLOUT;
|
||
|
|
||
|
// draw lines...
|
||
|
if (m_nLinePoints > 1)
|
||
|
{
|
||
|
ph.SetElement(UIE_CALLOUTSHADOW);
|
||
|
PolyLineArrowShadow(hDC, m_rgptLinePoint, m_nLinePoints);
|
||
|
ph.SetElement(eCallout);
|
||
|
PolyLineArrow(hDC, m_rgptLinePoint, m_nLinePoints);
|
||
|
}
|
||
|
|
||
|
//@@BEGIN_MSINTERNAL
|
||
|
#ifdef DDKBUILD
|
||
|
// if we're in edit mode, show the callout max rect
|
||
|
if (m_ui.InEditMode())
|
||
|
{
|
||
|
ph.SetElement(UIE_CALLOUTMAX);
|
||
|
ph.Rectangle(m_rectCalloutMax);
|
||
|
ph.SetElement(eCallout);
|
||
|
ph.Rectangle(m_rectCallout);
|
||
|
}
|
||
|
|
||
|
// if we're in edit mode, indicate alignment
|
||
|
if (m_ui.InEditMode())
|
||
|
{
|
||
|
ph.SetElement(UIE_CALLOUTALIGN);
|
||
|
|
||
|
const int &align = m_dwCalloutAlign;
|
||
|
const RECT &rect = m_rectCalloutMax;
|
||
|
|
||
|
int vert = align & (CAF_TOP | CAF_BOTTOM);
|
||
|
int horz = align & (CAF_LEFT | CAF_RIGHT);
|
||
|
|
||
|
BOOL bHorz = TRUE;
|
||
|
BOOL bVert = TRUE;
|
||
|
|
||
|
int hsq, hy, heq, vsq, vx, veq, s, e;
|
||
|
|
||
|
switch (vert)
|
||
|
{
|
||
|
case CAF_TOP:
|
||
|
hy = rect.top;
|
||
|
vsq = 0;
|
||
|
veq = 1;
|
||
|
break;
|
||
|
|
||
|
case 0:
|
||
|
bHorz = FALSE;
|
||
|
vsq = 1;
|
||
|
veq = 3;
|
||
|
break;
|
||
|
|
||
|
case CAF_BOTTOM:
|
||
|
hy = rect.bottom - 1;
|
||
|
vsq = 3;
|
||
|
veq = 4;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch (horz)
|
||
|
{
|
||
|
case CAF_LEFT:
|
||
|
vx = rect.left;
|
||
|
hsq = 0;
|
||
|
heq = 1;
|
||
|
break;
|
||
|
|
||
|
case 0:
|
||
|
bVert = FALSE;
|
||
|
hsq = 1;
|
||
|
heq = 3;
|
||
|
break;
|
||
|
|
||
|
case CAF_RIGHT:
|
||
|
vx = rect.right - 1;
|
||
|
hsq = 3;
|
||
|
heq = 4;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (bHorz)
|
||
|
{
|
||
|
s = ConvertVal(hsq, 0, 4, rect.left, rect.right - 1);
|
||
|
e = ConvertVal(heq, 0, 4, rect.left, rect.right - 1);
|
||
|
MoveToEx(hDC, s, hy, NULL);
|
||
|
LineTo(hDC, e + 1, hy);
|
||
|
}
|
||
|
|
||
|
if (bVert)
|
||
|
{
|
||
|
s = ConvertVal(vsq, 0, 4, rect.top, rect.bottom - 1);
|
||
|
e = ConvertVal(veq, 0, 4, rect.top, rect.bottom - 1);
|
||
|
MoveToEx(hDC, vx, s, NULL);
|
||
|
LineTo(hDC, vx, e + 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
//@@END_MSINTERNAL
|
||
|
// draw text
|
||
|
ph.SetElement(eCallout);
|
||
|
RECT rect = m_rectCallout;
|
||
|
InflateRect(&rect, -1, -1);
|
||
|
|
||
|
// If this control is assigned an action with DIA_FIXED (m_bFixed), use gray color for text.
|
||
|
COLORREF OldColor;
|
||
|
if (m_bFixed)
|
||
|
{
|
||
|
OldColor = ::SetTextColor(hDC, 0); // Set an arbitrary color to find out what we are currently using.
|
||
|
::SetTextColor(hDC, RGB(GetRValue(OldColor) >> 1, GetGValue(OldColor) >> 1, GetBValue(OldColor) >> 1));
|
||
|
}
|
||
|
|
||
|
if (m_ptszCaption)
|
||
|
DrawText(hDC, m_ptszCaption, -1, &rect, m_dwDrawTextFlags);
|
||
|
|
||
|
if (m_bFixed)
|
||
|
::SetTextColor(hDC, OldColor);
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::Invalidate()
|
||
|
{
|
||
|
m_view.Invalidate();
|
||
|
}
|
||
|
|
||
|
void MakeRect(RECT &rect, POINT a, POINT b)
|
||
|
{
|
||
|
rect.left = min(a.x, b.x);
|
||
|
rect.right = max(a.x, b.x);
|
||
|
rect.top = min(a.y, b.y);
|
||
|
rect.bottom = max(a.y, b.y);
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::PlaceCalloutMaxCorner(int nCorner, POINT point)
|
||
|
{
|
||
|
switch (nCorner)
|
||
|
{
|
||
|
case 0:
|
||
|
m_ptFirstCorner = point;
|
||
|
m_bPlacedOnlyFirstCorner = TRUE;
|
||
|
Invalidate();
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
MakeRect(m_rectCalloutMax, m_ptFirstCorner, point);
|
||
|
m_bPlacedOnlyFirstCorner = FALSE;
|
||
|
if (!m_bInit)
|
||
|
Init();
|
||
|
else
|
||
|
CalcCallout();
|
||
|
Invalidate();
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
assert(0);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::SetLastLinePoint(int nPoint, POINT point, BOOL bShiftDown)
|
||
|
{
|
||
|
if (!(nPoint >= 0 && nPoint < MAX_DEVICECONTROL_LINEPOINTS))
|
||
|
return;
|
||
|
|
||
|
// Check for SHIFT key state
|
||
|
if (nPoint && bShiftDown) // SHIFT key only makes a difference if we are setting 2nd and subsequent points.
|
||
|
{
|
||
|
// SHIFT key down. Need to draw controlled line.
|
||
|
if (labs(m_rgptLinePoint[nPoint-1].x - point.x) > labs(m_rgptLinePoint[nPoint-1].y - point.y))
|
||
|
{
|
||
|
// Wider. Draw horizontal.
|
||
|
m_rgptLinePoint[nPoint].x = point.x;
|
||
|
m_rgptLinePoint[nPoint].y = m_rgptLinePoint[nPoint-1].y;
|
||
|
} else
|
||
|
{
|
||
|
// Taller. Draw vertical
|
||
|
m_rgptLinePoint[nPoint].x = m_rgptLinePoint[nPoint-1].x;
|
||
|
m_rgptLinePoint[nPoint].y = point.y;
|
||
|
}
|
||
|
} else
|
||
|
m_rgptLinePoint[nPoint] = point; // SHIFT key not down. Draw line as usual.
|
||
|
m_nLinePoints = nPoint + 1;
|
||
|
Invalidate();
|
||
|
|
||
|
if (m_nLinePoints < 2)
|
||
|
return;
|
||
|
|
||
|
POINT prev = m_rgptLinePoint[m_nLinePoints - 2];
|
||
|
|
||
|
// remove identical points
|
||
|
if (point.x == prev.x && point.y == prev.y)
|
||
|
{
|
||
|
m_nLinePoints--;
|
||
|
return;
|
||
|
}
|
||
|
//@@BEGIN_MSINTERNAL
|
||
|
|
||
|
// TODO: remove the midpoint of colinear triples
|
||
|
//@@END_MSINTERNAL
|
||
|
}
|
||
|
|
||
|
void PlaceRectCenter(RECT &rect, POINT point)
|
||
|
{
|
||
|
POINT center = {
|
||
|
(rect.left + rect.right) / 2,
|
||
|
(rect.top + rect.bottom) / 2};
|
||
|
|
||
|
OffsetRect(&rect, point.x - center.x, point.y - center.y);
|
||
|
}
|
||
|
|
||
|
void OffsetRectToWithin(RECT &rect, const RECT &bounds)
|
||
|
{
|
||
|
POINT adj = {0, 0};
|
||
|
|
||
|
if (rect.left < bounds.left)
|
||
|
adj.x = bounds.left - rect.left;
|
||
|
if (rect.right > bounds.right)
|
||
|
adj.x = bounds.right - rect.right;
|
||
|
if (rect.top < bounds.top)
|
||
|
adj.y = bounds.top - rect.top;
|
||
|
if (rect.bottom > bounds.bottom)
|
||
|
adj.y = bounds.bottom - rect.bottom;
|
||
|
|
||
|
OffsetRect(&rect, adj.x, adj.y);
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::Position(POINT point)
|
||
|
{
|
||
|
PlaceRectCenter(m_rectCalloutMax, point);
|
||
|
RECT client;
|
||
|
m_view.GetClientRect(&client);
|
||
|
OffsetRectToWithin(m_rectCalloutMax, client);
|
||
|
CalcCallout();
|
||
|
Invalidate();
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::ConsiderAlignment(POINT point)
|
||
|
{
|
||
|
POINT center = {
|
||
|
(m_rectCalloutMax.right + m_rectCalloutMax.left) / 2,
|
||
|
(m_rectCalloutMax.bottom + m_rectCalloutMax.top) / 2};
|
||
|
SIZE dim = {
|
||
|
m_rectCalloutMax.right - m_rectCalloutMax.left,
|
||
|
m_rectCalloutMax.bottom - m_rectCalloutMax.top};
|
||
|
SIZE delta = {point.x - center.x, point.y - center.y};
|
||
|
int MININ = m_FontHeight;
|
||
|
SIZE in = {max(dim.cx / 4, MININ), max(dim.cy / 4, MININ)};
|
||
|
DWORD align = 0;
|
||
|
if (delta.cx < -in.cx)
|
||
|
align |= CAF_LEFT;
|
||
|
if (delta.cx > in.cx)
|
||
|
align |= CAF_RIGHT;
|
||
|
if (delta.cy < -in.cy)
|
||
|
align |= CAF_TOP;
|
||
|
if (delta.cy > in.cy)
|
||
|
align |= CAF_BOTTOM;
|
||
|
m_dwCalloutAlign = align;
|
||
|
CalcCallout();
|
||
|
Invalidate();
|
||
|
}
|
||
|
|
||
|
//@@BEGIN_MSINTERNAL
|
||
|
#ifdef DDKBUILD
|
||
|
void CDeviceControl::ReselectControl()
|
||
|
{
|
||
|
SelectControl(TRUE);
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::SelectControl(BOOL bReselect)
|
||
|
{
|
||
|
CSelControlDlg dlg(m_view, *this, bReselect, m_dwDeviceControlOffset, m_ui.m_didi);
|
||
|
|
||
|
switch (dlg.DoModal(m_view.m_hWnd))
|
||
|
{
|
||
|
case SCDR_OK:
|
||
|
m_dwDeviceControlOffset = dlg.GetOffset();
|
||
|
m_bOffsetAssigned = TRUE;
|
||
|
Invalidate();
|
||
|
break;
|
||
|
|
||
|
case SCDR_CANCEL:
|
||
|
break;
|
||
|
|
||
|
case SCDR_NOFREE:
|
||
|
MessageBox(m_view.m_hWnd, _T("All device controls have been assigned for this view."),
|
||
|
_T("Can't reselect control."), MB_OK);
|
||
|
break;
|
||
|
|
||
|
case -1:
|
||
|
MessageBox(m_view.m_hWnd, _T("CSelControlDlg.DoModal() failed."), _T("oops"), MB_OK);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
assert(0);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
//@@END_MSINTERNAL
|
||
|
DWORD CDeviceControl::GetOffset()
|
||
|
{
|
||
|
if (m_bOffsetAssigned)
|
||
|
return m_dwDeviceControlOffset;
|
||
|
|
||
|
return (DWORD)-1;
|
||
|
}
|
||
|
|
||
|
BOOL CDeviceControl::IsOffsetAssigned()
|
||
|
{
|
||
|
return m_bOffsetAssigned;
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::FillImageInfo(DIDEVICEIMAGEINFOW *pImgInfo)
|
||
|
{
|
||
|
if (!pImgInfo) return;
|
||
|
|
||
|
if (m_ptszOverlayPath != NULL)
|
||
|
CopyStr(pImgInfo->tszImagePath, m_ptszOverlayPath, MAX_PATH);
|
||
|
else
|
||
|
wcscpy(pImgInfo->tszImagePath, L""); // Overlay Image not yet supported
|
||
|
|
||
|
SIZE size = {0, 0};
|
||
|
if (m_pbmOverlay != NULL)
|
||
|
m_pbmOverlay->GetSize(&size);
|
||
|
RECT rect = {m_ptOverlay.x, m_ptOverlay.y,
|
||
|
m_ptOverlay.x + size.cx, m_ptOverlay.y + size.cy};
|
||
|
|
||
|
pImgInfo->dwFlags = DIDIFT_OVERLAY; // This is an overlay
|
||
|
pImgInfo->rcOverlay = rect;
|
||
|
pImgInfo->dwObjID = GetOffset();
|
||
|
pImgInfo->dwcValidPts = m_nLinePoints;
|
||
|
DWORD dwPtsToCopy = m_nLinePoints > 5 ? 5 : m_nLinePoints;
|
||
|
for (DWORD i = 0; i < dwPtsToCopy; ++i)
|
||
|
pImgInfo->rgptCalloutLine[i] = m_rgptLinePoint[i];
|
||
|
pImgInfo->rcCalloutRect = m_rectCalloutMax;
|
||
|
pImgInfo->dwTextAlign = m_dwCalloutAlign;
|
||
|
}
|
||
|
|
||
|
//@@BEGIN_MSINTERNAL
|
||
|
#ifdef DDKBUILD
|
||
|
void CDeviceControl::SelectOverlay()
|
||
|
{
|
||
|
LPCTSTR file = GetOpenFileName(
|
||
|
g_hModule,
|
||
|
m_view.m_hWnd,
|
||
|
_T("Select An Overlay Image for This Control"),
|
||
|
_T("PNG Files (*.png)\0*.png\0All Files (*.*)\0*.*\0"),
|
||
|
_T("png"));
|
||
|
|
||
|
if (file == NULL)
|
||
|
return;
|
||
|
|
||
|
ManualLoadImage(file);
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::ManualLoadImage(LPCTSTR tszPath)
|
||
|
{
|
||
|
if (!tszPath)
|
||
|
FormattedErrorBox(g_hModule, m_view.m_hWnd, IDS_TITLE_NOLOADVIEWIMAGE, IDS_NULLPATH);
|
||
|
|
||
|
LPDIRECT3DSURFACE8 pSurf = m_ui.m_uig.GetSurface3D(); // GetSurface3D() calls AddRef() on the surface.
|
||
|
CBitmap *pbmNewImage = CBitmap::CreateViaD3DX(tszPath, pSurf);
|
||
|
if (pSurf)
|
||
|
{
|
||
|
// Release surface instance after we are done with it so we don't leak memory.
|
||
|
pSurf->Release();
|
||
|
pSurf = NULL;
|
||
|
}
|
||
|
if (pbmNewImage == NULL)
|
||
|
{
|
||
|
FormattedErrorBox(g_hModule, m_view.m_hWnd, IDS_TITLE_NOLOADVIEWIMAGE, IDS_COULDNOTCREATEIMAGEFROMFILE, tszPath);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// replace
|
||
|
delete m_pbmOverlay;
|
||
|
m_pbmOverlay = pbmNewImage;
|
||
|
pbmNewImage = NULL;
|
||
|
if (m_ptszOverlayPath != NULL)
|
||
|
free(m_ptszOverlayPath);
|
||
|
m_ptszOverlayPath = _tcsdup(tszPath);
|
||
|
|
||
|
// redraw
|
||
|
Invalidate();
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::PositionOverlay(POINT point)
|
||
|
{
|
||
|
SIZE size = {1, 1};
|
||
|
RECT rect = {0, 0, size.cx, size.cy};
|
||
|
PlaceRectCenter(rect, point);
|
||
|
RECT client;
|
||
|
m_view.GetClientRect(&client);
|
||
|
OffsetRectToWithin(rect, client);
|
||
|
SRECT sr = rect;
|
||
|
m_ptOverlay = sr.ul;
|
||
|
Invalidate();
|
||
|
}
|
||
|
#endif
|
||
|
//@@END_MSINTERNAL
|
||
|
|
||
|
BOOL CDeviceControl::IsMapped()
|
||
|
{
|
||
|
return m_ui.IsControlMapped(this);
|
||
|
}
|
||
|
|
||
|
int CDeviceControl::GetControlIndex()
|
||
|
{
|
||
|
for (int i = 0; i < m_view.GetNumControls(); i++)
|
||
|
if (m_view.GetControl(i) == this)
|
||
|
return i;
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::SetLinePoints(int n, POINT *rgpt)
|
||
|
{
|
||
|
assert(n >= 0 && n <= MAX_DEVICECONTROL_LINEPOINTS && rgpt);
|
||
|
|
||
|
if (n < 0)
|
||
|
n = 0;
|
||
|
if (n > MAX_DEVICECONTROL_LINEPOINTS)
|
||
|
n = MAX_DEVICECONTROL_LINEPOINTS;
|
||
|
|
||
|
if (!rgpt)
|
||
|
n = 0;
|
||
|
|
||
|
m_nLinePoints = n;
|
||
|
|
||
|
for (int i = 0; i < n; i++)
|
||
|
m_rgptLinePoint[i] = rgpt[i];
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::SetOverlayPath(LPCTSTR tszPath)
|
||
|
{
|
||
|
if (m_ptszOverlayPath)
|
||
|
free(m_ptszOverlayPath);
|
||
|
m_ptszOverlayPath = NULL;
|
||
|
|
||
|
if (tszPath)
|
||
|
m_ptszOverlayPath = _tcsdup(tszPath);
|
||
|
|
||
|
delete m_pbmOverlay;
|
||
|
m_pbmOverlay = NULL;
|
||
|
|
||
|
if (m_ptszOverlayPath)
|
||
|
{
|
||
|
LPDIRECT3DSURFACE8 pSurf = m_ui.m_uig.GetSurface3D(); // GetSurface3D() calls AddRef() on the surface.
|
||
|
m_pbmOverlay = CBitmap::CreateViaD3DX(m_ptszOverlayPath, pSurf);
|
||
|
if (pSurf)
|
||
|
{
|
||
|
// Release surface instance after we are done with it so we don't leak memory.
|
||
|
pSurf->Release();
|
||
|
pSurf = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CDeviceControl::SetOverlayRect(const RECT &r)
|
||
|
{
|
||
|
m_ptOverlay.x = r.left;
|
||
|
m_ptOverlay.y = r.top;
|
||
|
}
|