You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
563 lines
13 KiB
563 lines
13 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose: Implements the angle custom control, a circle with a line indicating
|
||
|
// a rotation angle.
|
||
|
//
|
||
|
//=============================================================================//
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#include "hammer.h"
|
||
|
#include "AngleBox.h"
|
||
|
#include "hammer_mathlib.h"
|
||
|
#include "CustomMessages.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
#pragma warning(disable: 4244)
|
||
|
|
||
|
|
||
|
BEGIN_MESSAGE_MAP(CAngleBox, CWnd)
|
||
|
//{{AFX_MSG_MAP(CAngleBox)
|
||
|
ON_WM_MOUSEMOVE()
|
||
|
ON_WM_LBUTTONUP()
|
||
|
ON_WM_LBUTTONDOWN()
|
||
|
ON_WM_PAINT()
|
||
|
//}}AFX_MSG_MAP
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Constructor.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CAngleBox::CAngleBox(void)
|
||
|
{
|
||
|
m_vecAngles.Init();
|
||
|
m_bDragging = false;
|
||
|
m_pEdit = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Destructor.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CAngleBox::~CAngleBox()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : nFlags -
|
||
|
// point -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleBox::OnMouseMove(UINT nFlags, CPoint point)
|
||
|
{
|
||
|
if (m_bDragging)
|
||
|
{
|
||
|
//
|
||
|
// Remove old angle line by redrawing it (XOR).
|
||
|
//
|
||
|
DrawAngleLine(&m_DragDC);
|
||
|
|
||
|
//
|
||
|
// Calculate new yaw.
|
||
|
//
|
||
|
int nNewYaw = fixang(180 - (int)lineangle(point.x, point.y, m_ptClientCenter.x, m_ptClientCenter.y));
|
||
|
m_vecAngles.Init();
|
||
|
m_vecAngles[YAW] = nNewYaw;
|
||
|
|
||
|
//
|
||
|
// Draw the new angle line.
|
||
|
//
|
||
|
DrawAngleLine(&m_DragDC);
|
||
|
}
|
||
|
|
||
|
CWnd::OnMouseMove(nFlags, point);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : nFlags -
|
||
|
// point -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleBox::OnLButtonUp(UINT nFlags, CPoint point)
|
||
|
{
|
||
|
// release dc
|
||
|
if (m_bDragging)
|
||
|
{
|
||
|
::ReleaseDC(m_hWnd, m_DragDC.Detach());
|
||
|
m_bDragging = false;
|
||
|
ReleaseCapture();
|
||
|
|
||
|
//
|
||
|
// They've explicity set the angles, so clear the different flag for
|
||
|
// the multiselect case.
|
||
|
//
|
||
|
SetDifferent(false);
|
||
|
|
||
|
GetParent()->PostMessage(ABN_CHANGED, GetDlgCtrlID(), 0);
|
||
|
}
|
||
|
|
||
|
CWnd::OnLButtonUp(nFlags, point);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : nFlags -
|
||
|
// point -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleBox::OnLButtonDown(UINT nFlags, CPoint point)
|
||
|
{
|
||
|
//
|
||
|
// Start dragging.
|
||
|
//
|
||
|
m_DragDC.Attach(::GetDC(m_hWnd));
|
||
|
m_bDragging = true;
|
||
|
SetCapture();
|
||
|
|
||
|
CWnd::OnLButtonDown(nFlags, point);
|
||
|
|
||
|
OnMouseMove(0, point);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : pDC -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleBox::DrawAngleLine(CDC *pDC)
|
||
|
{
|
||
|
if ((m_vecAngles[PITCH] != 0) || (m_vecAngles[ROLL] != 0) ||
|
||
|
(m_vecAngles[YAW] < 0 || m_vecAngles[YAW] > 359) || m_bDifferent)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
pDC->SetROP2(R2_XORPEN);
|
||
|
pDC->SelectStockObject(WHITE_PEN);
|
||
|
|
||
|
CRect r;
|
||
|
GetClientRect(r);
|
||
|
m_ptClientCenter = r.CenterPoint();
|
||
|
|
||
|
double rad = r.Width() / 2 - 3;
|
||
|
|
||
|
CPoint pt;
|
||
|
pt.x = m_ptClientCenter.x + sin(DEG2RAD((double)(m_vecAngles[YAW] + 90))) * rad + 0.5;
|
||
|
pt.y = m_ptClientCenter.y + cos(DEG2RAD((double)(m_vecAngles[YAW] + 90))) * rad + 0.5;
|
||
|
|
||
|
pDC->MoveTo(m_ptClientCenter);
|
||
|
pDC->LineTo(pt);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Returns the current state of the control as a keyvalue string.
|
||
|
// Input : szAngles - Buffer to receive angles string.
|
||
|
// Output : Returns 'szAngles'.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CAngleBox::GetAngles(QAngle &vecAngles)
|
||
|
{
|
||
|
if (m_bDifferent)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
vecAngles = m_vecAngles;
|
||
|
|
||
|
return(true);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Returns the current state of the control as a keyvalue string.
|
||
|
// Input : szAngles - Buffer to receive angles string.
|
||
|
// Output : Returns 'szAngles'.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
char *CAngleBox::GetAngles(char *szAngles)
|
||
|
{
|
||
|
QAngle vecAngles;
|
||
|
GetAngles(vecAngles);
|
||
|
sprintf(szAngles, "%g %g %g", (double)vecAngles[0], (double)vecAngles[1], (double)vecAngles[2]);
|
||
|
return(szAngles);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Returns a string indicating the current state of the angle control.
|
||
|
// This is used for setting the text in the companion edit control.
|
||
|
// Input : szBuf - Buffer to receive string.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
char *CAngleBox::GetAngleEditText(char *szBuf)
|
||
|
{
|
||
|
szBuf[0] = '\0';
|
||
|
|
||
|
if (m_bDifferent)
|
||
|
{
|
||
|
strcpy(szBuf, "(diff)");
|
||
|
}
|
||
|
else if ((m_vecAngles[PITCH] == 90) && (m_vecAngles[YAW] == 0) && (m_vecAngles[ROLL] == 0))
|
||
|
{
|
||
|
strcpy(szBuf, "Down");
|
||
|
}
|
||
|
else if ((m_vecAngles[PITCH] == -90) && (m_vecAngles[YAW] == 0) && (m_vecAngles[ROLL] == 0))
|
||
|
{
|
||
|
strcpy(szBuf, "Up");
|
||
|
}
|
||
|
else if (m_vecAngles[YAW] >= 0)
|
||
|
{
|
||
|
itoa((int)m_vecAngles[YAW], szBuf, 10);
|
||
|
}
|
||
|
|
||
|
return(szBuf);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Called internally and by the linked combo box, this updates the angles
|
||
|
// without updating the linked combo box.
|
||
|
// Input : szAngles -
|
||
|
// bRedraw -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleBox::SetAnglesInternal(const QAngle &vecAngles, bool bRedraw)
|
||
|
{
|
||
|
QAngle vecAngleSet = vecAngles;
|
||
|
while (vecAngleSet[YAW] < 0)
|
||
|
{
|
||
|
vecAngleSet[YAW] += 360.0;
|
||
|
}
|
||
|
|
||
|
CDC *pDC = NULL;
|
||
|
|
||
|
if (bRedraw)
|
||
|
{
|
||
|
//
|
||
|
// Erase the old line.
|
||
|
//
|
||
|
Assert(::IsWindow(m_hWnd));
|
||
|
pDC = GetDC();
|
||
|
if (pDC != NULL)
|
||
|
{
|
||
|
DrawAngleLine(pDC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the data member.
|
||
|
//
|
||
|
m_vecAngles = vecAngleSet;
|
||
|
|
||
|
if ((bRedraw) && (pDC != NULL))
|
||
|
{
|
||
|
//
|
||
|
// Draw the new line.
|
||
|
//
|
||
|
DrawAngleLine(pDC);
|
||
|
ReleaseDC(pDC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Called from the client code, this sets our angles and updates the
|
||
|
// linked combo box.
|
||
|
// Input : szAngles -
|
||
|
// bRedraw -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleBox::SetAngles(const QAngle &vecAngles, bool bRedraw)
|
||
|
{
|
||
|
SetAnglesInternal(vecAngles, bRedraw);
|
||
|
UpdateAngleEditText();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Called from the client code, this sets our angles via a string and
|
||
|
// updates the linked combo box.
|
||
|
// Input : szAngles -
|
||
|
// bRedraw -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleBox::SetAngles(const char *szAngles, bool bRedraw)
|
||
|
{
|
||
|
QAngle vecAngles(0, 0, 0);
|
||
|
sscanf(szAngles, "%f %f %f", &vecAngles[PITCH], &vecAngles[YAW], &vecAngles[ROLL]);
|
||
|
SetAngles(vecAngles, bRedraw);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Called internally and by the linked combo box, this sets our
|
||
|
// 'different' state without updating the linked combo box.
|
||
|
// Input : bDifferent -
|
||
|
// bRedraw -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleBox::SetDifferentInternal(bool bDifferent, bool bRedraw)
|
||
|
{
|
||
|
CDC *pDC = NULL;
|
||
|
|
||
|
if (bRedraw)
|
||
|
{
|
||
|
//
|
||
|
// Erase the old line.
|
||
|
//
|
||
|
Assert(::IsWindow(m_hWnd));
|
||
|
pDC = GetDC();
|
||
|
if (pDC != NULL)
|
||
|
{
|
||
|
DrawAngleLine(pDC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the data member.
|
||
|
//
|
||
|
m_bDifferent = bDifferent;
|
||
|
|
||
|
if ((bRedraw) && (pDC != NULL))
|
||
|
{
|
||
|
//
|
||
|
// Draw the new line.
|
||
|
//
|
||
|
DrawAngleLine(pDC);
|
||
|
ReleaseDC(pDC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Sets our state to indicate multiselect of objects with different
|
||
|
// angles to avoid mucking with the angles unless they explicitly set
|
||
|
// them to something new.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleBox::SetDifferent(bool bDifferent, bool bRedraw)
|
||
|
{
|
||
|
SetDifferentInternal(bDifferent, bRedraw);
|
||
|
UpdateAngleEditText();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleBox::OnPaint(void)
|
||
|
{
|
||
|
PAINTSTRUCT ps;
|
||
|
CDC *pDC = BeginPaint(&ps);
|
||
|
|
||
|
if (pDC == NULL)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
CBrush brushWindow(GetSysColor(COLOR_3DFACE));
|
||
|
CBrush brushBlack(RGB(0, 0, 0));
|
||
|
|
||
|
CBrush *pBackBrush = IsWindowEnabled() ? &brushBlack : &brushWindow;
|
||
|
|
||
|
CRect r;
|
||
|
GetClientRect(r);
|
||
|
|
||
|
//
|
||
|
// Fill with the window color.
|
||
|
//
|
||
|
pDC->FillRect(&r, &brushWindow);
|
||
|
|
||
|
//
|
||
|
// Draw a 3D circle.
|
||
|
//
|
||
|
m_ptClientCenter = r.CenterPoint();
|
||
|
|
||
|
pDC->SelectStockObject(NULL_PEN);
|
||
|
pDC->SelectObject(pBackBrush);
|
||
|
pDC->Ellipse(r);
|
||
|
|
||
|
CPen hi(PS_SOLID, 2, GetSysColor(COLOR_3DSHADOW));
|
||
|
CPen lo(PS_SOLID, 2, GetSysColor(COLOR_3DHILIGHT));
|
||
|
|
||
|
pDC->SelectObject(hi);
|
||
|
pDC->Arc(r, CPoint(r.right, r.top), CPoint(r.left, r.bottom));
|
||
|
pDC->SelectObject(lo);
|
||
|
pDC->Arc(r, CPoint(r.left, r.bottom), CPoint(r.right, r.top));
|
||
|
|
||
|
//
|
||
|
// Draw center point.
|
||
|
//
|
||
|
pDC->SetPixel(m_ptClientCenter, RGB(0xff, 0xff, 0xff));
|
||
|
|
||
|
//
|
||
|
// Draw line indicating angles direction.
|
||
|
//
|
||
|
if (IsWindowEnabled())
|
||
|
{
|
||
|
DrawAngleLine(pDC);
|
||
|
}
|
||
|
|
||
|
EndPaint(&ps);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Enables or disables the angles controls.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleBox::Enable(bool bEnable)
|
||
|
{
|
||
|
if (bEnable)
|
||
|
{
|
||
|
EnableWindow(TRUE);
|
||
|
|
||
|
if (m_pEdit)
|
||
|
{
|
||
|
m_pEdit->EnableWindow(TRUE);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
EnableWindow(FALSE);
|
||
|
|
||
|
if (m_pEdit)
|
||
|
{
|
||
|
m_pEdit->EnableWindow(FALSE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Invalidate(FALSE);
|
||
|
UpdateWindow();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Hides or shows the angles controls.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleBox::Show(bool bShow)
|
||
|
{
|
||
|
if (bShow)
|
||
|
{
|
||
|
ShowWindow(SW_SHOW);
|
||
|
|
||
|
if (m_pEdit)
|
||
|
{
|
||
|
m_pEdit->ShowWindow(SW_SHOW);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ShowWindow(SW_HIDE);
|
||
|
|
||
|
if (m_pEdit)
|
||
|
{
|
||
|
m_pEdit->ShowWindow(SW_HIDE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Invalidate(FALSE);
|
||
|
UpdateWindow();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Updates the text in the angle combo to reflect the current angles
|
||
|
// in the angles control.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleBox::UpdateAngleEditText(void)
|
||
|
{
|
||
|
if (m_pEdit)
|
||
|
{
|
||
|
char szBuf[20];
|
||
|
GetAngleEditText(szBuf);
|
||
|
m_pEdit->SetAnglesInternal(szBuf);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
BEGIN_MESSAGE_MAP(CAngleCombo, CWnd)
|
||
|
//{{AFX_MSG_MAP(CAngleBox)
|
||
|
ON_CONTROL_REFLECT(CBN_EDITCHANGE, OnChangeAngleEdit)
|
||
|
ON_CONTROL_REFLECT(CBN_SELENDOK, OnSelChangeAngleEdit)
|
||
|
//}}AFX_MSG_MAP
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Construktor.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CAngleCombo::CAngleCombo()
|
||
|
: CComboBox()
|
||
|
{
|
||
|
m_pBox = NULL;
|
||
|
m_bEnableUpdate = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : *szAngles -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleCombo::SetAnglesInternal(const char *szAngles)
|
||
|
{
|
||
|
m_bEnableUpdate = false;
|
||
|
SetWindowText(szAngles);
|
||
|
m_bEnableUpdate = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Handles a change in the contents of the angle edit control.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleCombo::OnChangeAngleEdit(void)
|
||
|
{
|
||
|
if (m_bEnableUpdate)
|
||
|
{
|
||
|
char buf[64];
|
||
|
GetWindowText(buf, 64);
|
||
|
UpdateAngleBox(buf);
|
||
|
|
||
|
GetParent()->PostMessage(ABN_CHANGED, GetDlgCtrlID(), 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Handles a change in the current selection of the angle edit combo.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleCombo::OnSelChangeAngleEdit(void)
|
||
|
{
|
||
|
char buf[64];
|
||
|
int nSel = GetCurSel();
|
||
|
GetLBText(nSel, buf);
|
||
|
UpdateAngleBox(buf);
|
||
|
|
||
|
GetParent()->PostMessage(ABN_CHANGED, GetDlgCtrlID(), 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Updates angle box with the settings from the combo box. Call the
|
||
|
// internal functions so we don't get a reflected notification, mucking
|
||
|
// up our state.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CAngleCombo::UpdateAngleBox(char *szText)
|
||
|
{
|
||
|
if (m_pBox)
|
||
|
{
|
||
|
m_pBox->SetDifferentInternal(false);
|
||
|
|
||
|
if (V_isdigit(szText[0]))
|
||
|
{
|
||
|
QAngle vecAngles(0, atoi(szText), 0);
|
||
|
m_pBox->SetAnglesInternal(vecAngles, true);
|
||
|
}
|
||
|
else if (!stricmp(szText, "down"))
|
||
|
{
|
||
|
m_pBox->SetAnglesInternal(QAngle(90, 0, 0), true);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_pBox->SetAnglesInternal(QAngle(-90, 0, 0), true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|