You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

615 lines
16 KiB
C++

// XTPFlowGraphPaintManager.cpp : implementation of the CXTPFlowGraphPaintManager class.
//
// This file is a part of the XTREME TOOLKIT PRO MFC class library.
// (c)1998-2012 Codejock Software, All Rights Reserved.
//
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
// CONSENT OF CODEJOCK SOFTWARE.
//
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
// SINGLE COMPUTER.
//
// CONTACT INFORMATION:
// support@codejock.com
// http://www.codejock.com
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <math.h>
#include "Common/XTPDrawHelpers.h"
#include "GraphicLibrary/GdiPlus/GdiPlus.h"
#include "XTPFlowGraphTools.h"
#include "XTPFlowGraphElement.h"
#include "XTPFlowGraphPaintManager.h"
#include "XTPFlowGraphDrawContext.h"
#include "XTPFlowGraphControl.h"
#include "XTPFlowGraphNode.h"
#include "XTPFlowGraphNodeGroup.h"
#include "XTPFlowGraphPage.h"
#include "XTPFlowGraphConnection.h"
#include "XTPFlowGraphConnectionPoint.h"
#include "XTPFlowGraphConnectionPoints.h"
#include "XTPFlowGraphImage.h"
using namespace Gdiplus;
CXTPFlowGraphPaintManager::CXTPFlowGraphPaintManager()
{
m_clrBackground = RGB(255, 255, 255);
m_clrSelection = RGB(247, 150, 70);
m_clrTextColor = RGB(255, 255, 255);
m_clrConnection = RGB(0, 0, 0);
m_clrGridColor = RGB(240, 240, 240);
m_clrNodeBackground = RGB(192, 80, 77);
m_nNodeFrameSize = 3;
m_nPointTextMargin = 6;
m_bDrawArrow = TRUE;
m_nEllipseSize = 9;
m_bShowGrid = FALSE;
m_bShowNodeGroupsAlways = FALSE;
m_nConnectorType = xtpFlowGraphConnectorCurved;
memset(&m_lfText, 0, sizeof(m_lfText));
XTPDrawHelpers()->GetIconLogFont(&m_lfText);
m_lfText.lfHeight = 18;
m_lfText.lfWeight = FW_NORMAL;
m_lfCaption = m_lfText;
m_dMinZoomLevelBackground = 0.2;
m_dMinZoomLevelConnectionPoints = 0.3;
m_dMinZoomLevelConnections = 0.05;
m_dMinZoomLevelGDIQuality = 0.4;
}
CXTPFlowGraphPaintManager::~CXTPFlowGraphPaintManager()
{
}
void CXTPFlowGraphPaintManager::Cleanup()
{
}
void CXTPFlowGraphPaintManager::RefreshMetrics()
{
}
void CXTPFlowGraphPaintManager::DrawControlBackground(CXTPFlowGraphDrawContext* pDC, CXTPFlowGraphControl* pControl)
{
pDC->Clear(m_clrBackground);
if (m_bShowGrid && !pDC->IsPrinting() && pControl->GetZoomLevel() >= m_dMinZoomLevelBackground)
{
CRect rc;
pControl->GetClientRect(&rc);
pDC->DrawGrid(rc, pControl->GetScrollOffset(), pControl->GetZoomLevel(), m_clrGridColor);
}
}
void CXTPFlowGraphPaintManager::RecalcNodeLayout(CXTPFlowGraphDrawContext* pDC, CXTPFlowGraphNode* pNode)
{
pNode->m_rcWindow.SetRectEmpty();
CString strCaption = pNode->GetCaption();
pDC->SetFont(&m_lfCaption);
CSize szCaption;
if (pNode->GetMarkupUIElement())
szCaption = pDC->MeasureMarkupElement(pNode->GetMarkupUIElement());
else
szCaption = pDC->MeasureString(strCaption);
szCaption.cy += 2;
CXTPFlowGraphImage* pImage = pNode->GetImage();
if (pImage)
{
szCaption.cx += 2 + pImage->GetSize().cx + 3;
}
CSize szSize = szCaption;
pDC->SetFont(&m_lfText);
int i;
CXTPFlowGraphConnectionPoints* pPoints = pNode->GetConnectionPoints();
for (i = 0; i < pPoints->GetCount(); i++)
{
CXTPFlowGraphConnectionPoint* pPoint = pPoints->GetAt(i);
CSize szItem = pPoint->GetMarkupUIElement() ?
pDC->MeasureMarkupElement(pPoint->GetMarkupUIElement()) :
pDC->MeasureString(pPoint->GetCaption());
szItem.cy += 2;
if (pPoint->GetImage())
szItem.cx += pPoint->GetImage()->GetSize().cx + 2;
pPoint->m_rcPoint.SetRect(0, 0, szItem.cx, szItem.cy);
szSize.cy += szItem.cy;
szSize.cx = max(szSize.cx, szItem.cx);
}
int nTop = 2 * m_nNodeFrameSize + szCaption.cy;
szSize.cx += 10 + 2 * m_nPointTextMargin;
CSize sz = pNode->GetWindowSize();
if (sz != CSize(0, 0))
{
szSize.cy += sz.cy + m_nNodeFrameSize;
szSize.cx = max(szSize.cx, sz.cx + 2 * m_nNodeFrameSize);
pNode->m_rcWindow.SetRect(m_nNodeFrameSize, nTop, szSize.cx - m_nNodeFrameSize, nTop + sz.cy);
nTop += sz.cy + m_nNodeFrameSize;
}
szSize.cx = max(szSize.cx, pNode->GetUserSize().cx);
pNode->m_rcCaption.SetRect(m_nNodeFrameSize, m_nNodeFrameSize, szSize.cx - m_nNodeFrameSize, m_nNodeFrameSize + szCaption.cy);
if (pPoints->GetCount())
szSize.cy += m_nNodeFrameSize;
for (i = 0; i < pPoints->GetCount(); i++)
{
CXTPFlowGraphConnectionPoint* pPoint = pPoints->GetAt(i);
pPoint->m_rcPoint.SetRect(m_nNodeFrameSize, nTop, szSize.cx - m_nNodeFrameSize, nTop + pPoint->m_rcPoint.Height());
nTop += pPoint->m_rcPoint.Height();
}
szSize.cy += 2 * m_nNodeFrameSize;
szSize.cy = max(szSize.cy, pNode->GetUserSize().cy);
pNode->m_szActualSize = szSize;
}
void CXTPFlowGraphPaintManager::RecalcConnectionLayout(CXTPFlowGraphDrawContext* /*pDC*/, CXTPFlowGraphConnection* pConnection)
{
CXTPFlowGraphConnectionPoint* pFrom = pConnection->GetOutputPoint();
CXTPFlowGraphConnectionPoint* pTo = pConnection->GetInputPoint();
if (pFrom == NULL)
return;
CPoint ptFrom, ptTo;
ptFrom = pFrom->GetNode()->GetLocation();
ptFrom.x += pFrom->GetNode()->GetSize().cx;
ptFrom.y += pFrom->m_rcPoint.CenterPoint().y;
if (pTo != NULL)
{
ptTo = pTo->GetNode()->GetLocation();
ptTo.y += pTo->m_rcPoint.CenterPoint().y;
if (pTo->GetType() == xtpFlowGraphPointInputAndOutput)
{
if (ptFrom.x > ptTo.x)
ptTo.x += pTo->GetNode()->GetSize().cx;
}
pConnection->m_ptInputPoint = ptTo;
}
else
{
ptTo = pConnection->m_ptInputPoint;
}
if (pFrom->GetType() == xtpFlowGraphPointInputAndOutput)
{
if (ptTo.x < ptFrom.x - pFrom->GetNode()->GetSize().cx / 2)
ptFrom.x -= pFrom->GetNode()->GetSize().cx;
}
pConnection->m_ptOutputPoint = ptFrom;
GraphicsPath* path = new GraphicsPath();
XTPFlowGraphConnectorType type = pConnection->GetStyle() == -1 ? m_nConnectorType : (XTPFlowGraphConnectorType)pConnection->GetStyle();
CPoint ptControlPoint = pConnection->GetControlPoint();
if (type == xtpFlowGraphConnectorStraight)
{
if (ptControlPoint != CPoint(-1, -1))
{
Point pts[] = {Point(ptFrom.x, ptFrom.y), Point(ptControlPoint.x, ptControlPoint.y), Point(ptTo.x, ptTo.y) };
path->AddLines(pts, 3);
}
else
{
path->AddLine(ptFrom.x, ptFrom.y, ptTo.x, ptTo.y);
}
}
else
{
if (ptControlPoint != CPoint(-1, -1))
{
/*PointF pt[3] = {PointF((REAL)ptFrom.x, (REAL)ptFrom.y), PointF((REAL)pConnection->GetControlPoint().x, (REAL)pConnection->GetControlPoint().y),
PointF((REAL)ptTo.x, (REAL)ptTo.y) };
path->AddCurve(pt, 3);*/
PointF pt2[4] = {PointF((REAL)ptFrom.x, (REAL)ptFrom.y),
PointF((REAL)ptControlPoint.x, (REAL)ptFrom.y), PointF((REAL)ptControlPoint.x, (REAL)ptTo.y),
PointF((REAL)ptTo.x, (REAL)ptTo.y) };
path->AddBeziers(pt2, 4);
}
else
{
PointF pt[4] = {PointF((REAL)ptFrom.x, (REAL)ptFrom.y), PointF((REAL)(ptFrom.x + ptTo.x) / 2, (REAL)ptFrom.y),
PointF(REAL(ptFrom.x + ptTo.x) / 2, (REAL)ptTo.y), PointF((REAL)ptTo.x, (REAL)ptTo.y) };
path->AddBeziers(pt, 4);
}
}
SAFE_DELETE(pConnection->m_pPath);
pConnection->m_pPath = path;
}
COLORREF CXTPFlowGraphPaintManager::GetDarkColor(COLORREF clrBackground)
{
DWORD dwHSLBackground = CXTPDrawHelpers::RGBtoHSL(clrBackground);
DWORD dwL = GetBValue(dwHSLBackground);
DWORD dwDark = MulDiv(dwL, 2, 3);
return CXTPDrawHelpers::HSLtoRGB(RGB(GetRValue(dwHSLBackground), GetGValue(dwHSLBackground), dwDark));
}
void CXTPFlowGraphPaintManager::DrawNode(CXTPFlowGraphDrawContext* pDC, CXTPFlowGraphNode* pNode)
{
CRect rcNodeBounds = GetNodeBoundingRectangle(pNode);
CRect rcBounds = pDC->GetClipRect();
CXTPFlowGraphControl* pControl = pNode->GetControl();
if (rcBounds.right < rcNodeBounds.left || rcBounds.left > rcNodeBounds.right || rcBounds.top > rcNodeBounds.bottom || rcBounds.bottom < rcNodeBounds.top)
return;
CRect rc(pNode->GetLocation(), pNode->GetSize());
COLORREF clrNode = pNode->GetColor();
if (clrNode == -1) clrNode = m_clrNodeBackground;
COLORREF clrSelection = m_clrSelection;
if (pNode->IsSelected())
clrNode = clrSelection;
COLORREF clrFrame = GetDarkColor(clrNode);
pDC->FillSolidRect(rc, clrNode);
pDC->DrawFrame(rc.left, rc.top, rc.Width(), rc.Height(), m_nNodeFrameSize, clrFrame);
CString strCaption = pNode->GetCaption();
pDC->SetFont(&m_lfCaption);
int nCaptionHeight = pNode->m_rcCaption.Height();
pDC->SetTextColor(m_clrTextColor);
CRect rcCaption(rc.left, rc.top + m_nNodeFrameSize, rc.right, rc.top + nCaptionHeight + m_nNodeFrameSize);
UINT uCaptionFormat = DT_CENTER | DT_VCENTER;
CXTPFlowGraphImage* pImage = pNode->GetImage();
if (pImage)
{
CSize sz = pImage->GetSize();
pDC->DrawImage(pImage, CRect(CPoint(rc.left + m_nNodeFrameSize + 3, rc.top + m_nNodeFrameSize + (nCaptionHeight - sz.cy) / 2), sz));
rcCaption = CRect(rc.left + m_nNodeFrameSize + 3 + sz.cx + 2, rc.top + m_nNodeFrameSize, rc.right, rc.top + nCaptionHeight + m_nNodeFrameSize);
uCaptionFormat = DT_LEFT | DT_VCENTER;
}
if (pNode->GetMarkupUIElement())
{
pDC->DrawMarkupElement(pNode->GetMarkupUIElement(), rcCaption, uCaptionFormat);
}
else
{
pDC->DrawText(pNode->GetCaption(), rcCaption, uCaptionFormat);
}
int nCount = pNode->GetConnectionPoints()->GetCount();
if (nCount > 0 || !pNode->m_rcWindow.IsRectEmpty())
{
pDC->FillSolidRect(rc.left, rc.top + nCaptionHeight + m_nNodeFrameSize, rc.Width(), m_nNodeFrameSize, clrFrame);
}
if (nCount > 0 && !pNode->m_rcWindow.IsRectEmpty())
{
pDC->FillSolidRect(rc.left, rc.top + pNode->m_rcWindow.bottom, rc.Width(), m_nNodeFrameSize, clrFrame);
}
if (!pNode->m_rcWindow.IsRectEmpty() && pNode->GetWindowHandle() == NULL)
{
pNode->DrawWindowContent(pDC);
}
pDC->SetFont(&m_lfText);
for (int i = 0; i < nCount; i++)
{
CXTPFlowGraphConnectionPoint* pPoint = pNode->GetConnectionPoints()->GetAt(i);
CSize szItem = pPoint->m_rcPoint.Size();
int nTop = pPoint->m_rcPoint.top + rc.top;
if (pControl->GetZoomLevel() >= m_dMinZoomLevelConnectionPoints)
{
pDC->SetTextColor(m_clrTextColor);
CRect rcText(rc.left + m_nNodeFrameSize + m_nPointTextMargin, nTop, rc.right - (m_nNodeFrameSize + m_nPointTextMargin), nTop + szItem.cy);
if (pPoint->GetImage())
{
CSize sz = pPoint->GetImage()->GetSize();
pDC->DrawImage(pPoint->GetImage(), CRect(CPoint(rcText.left, (rcText.top + rcText.bottom - sz.cy) / 2), sz));
rcText.left += sz.cx + 2;
}
UINT uFormat = DT_VCENTER | (pPoint->GetType() == xtpFlowGraphPointOutput ? DT_RIGHT : DT_LEFT);
if (pPoint->GetMarkupUIElement())
pDC->DrawMarkupElement(pPoint->GetMarkupUIElement(), rcText, uFormat);
else
pDC->DrawText(pPoint->GetCaption(), rcText, uFormat);
}
COLORREF clrEllipse = pPoint->GetColor();
if (clrEllipse == (COLORREF)-1)
clrEllipse = clrFrame;
if (pPoint->GetType() & xtpFlowGraphPointInput)
{
pDC->SetBrush(pPoint->IsSelected() ? clrSelection : clrEllipse);
pDC->SetPen(0, 0);
CPoint pt(rc.left + 1, nTop + szItem.cy / 2);
pDC->Ellipse(CRect(pt.x - m_nEllipseSize / 2, pt.y - m_nEllipseSize / 2, pt.x + m_nEllipseSize - m_nEllipseSize / 2, pt.y + m_nEllipseSize - m_nEllipseSize / 2));
}
if (pPoint->GetType() & xtpFlowGraphPointOutput)
{
pDC->SetBrush(pPoint->IsSelected() ? clrSelection : clrEllipse);
pDC->SetPen(0, 0);
CPoint pt(rc.right - 1, nTop + szItem.cy / 2);
pDC->Ellipse(CRect(pt.x - (m_nEllipseSize - m_nEllipseSize / 2), pt.y - (m_nEllipseSize - m_nEllipseSize / 2), pt.x + m_nEllipseSize / 2, pt.y + m_nEllipseSize / 2));
}
}
}
REAL XTPGetDistance(PointF& a, PointF& b)
{
return (REAL)sqrt((a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y));
}
PointF XTPGetPosition(GraphicsPath* path, REAL dist)
{
int pointCount = path->GetPointCount();
PointF* points = (PointF*)malloc(pointCount * sizeof(PointF));
path->GetPathPoints(points, pointCount);
PointF res(points[pointCount - 1]);
if (dist >= 0)
{
for (int i = 0; i < pointCount - 1; i++)
{
REAL diff = (REAL)XTPGetDistance(points[i + 1], points[i]);
if (diff < dist)
{
dist -= diff;
continue;
}
res.X = points[i].X + (points[i + 1].X - points[i].X) * dist / diff;
res.Y = points[i].Y + (points[i + 1].Y - points[i].Y) * dist / diff;
break;
}
}
else
{
dist = -dist;
res = points[0];
for (int i = pointCount - 1; i >= 1; i--)
{
REAL diff = (REAL)XTPGetDistance(points[i - 1], points[i]);
if (diff < dist)
{
dist -= diff;
continue;
}
res.X = points[i].X + (points[i - 1].X - points[i].X) * dist / diff;
res.Y = points[i].Y + (points[i - 1].Y - points[i].Y) * dist / diff;
break;
}
}
free(points);
return res;
}
void CXTPFlowGraphPaintManager::DrawConnection(CXTPFlowGraphDrawContext* pDC, CXTPFlowGraphConnection* pConnection)
{
CRect rcConnectionBounds = pConnection->GetBoundingRect();
CRect rcBounds = pDC->GetClipRect();
CXTPFlowGraphControl* pControl = pConnection->GetControl();
if (rcBounds.right < rcConnectionBounds.left || rcBounds.left > rcConnectionBounds.right ||
rcBounds.top > rcConnectionBounds.bottom || rcBounds.bottom < rcConnectionBounds.top)
return;
if (pControl->GetZoomLevel() < m_dMinZoomLevelConnections)
return;
COLORREF clrConnection = pConnection->GetColor() != (COLORREF)-1 ? pConnection->GetColor() : m_clrConnection;
if (pConnection->IsSelected())
clrConnection = m_clrSelection;
pDC->SetPen(clrConnection, 2);
pDC->DrawCurve(pConnection->m_pPath);
if (pConnection->IsSelected() && pConnection->GetControlPoint() != CPoint(-1, -1))
{
pDC->SetPen(0, 0);
pDC->SetBrush(clrConnection);
CPoint pt(pConnection->GetControlPoint());
pDC->Ellipse(CRect(pt.x - 3, pt.y - 3, pt.x + 3, pt.y + 3));
}
if (m_bDrawArrow)
{
Gdiplus::GraphicsPath* path = pConnection->m_pPath->Clone();
path->Flatten();
const REAL rArrow = 10.0;
PointF ptEnd = XTPGetPosition(path, REAL(-m_nEllipseSize / 2));
PointF ptFrom = XTPGetPosition(path, REAL(- (m_nEllipseSize / 2 + rArrow)));
Point ptArrow[3];
PointF ptDiff = ptFrom - ptEnd;
ptArrow[0] = Point((int)ptEnd.X, (int)ptEnd.Y);
ptArrow[1] = Point(int(ptFrom.X - ptDiff.Y * (4.0) / rArrow), int(ptFrom.Y + ptDiff.X * (4.0) / rArrow));
ptArrow[2] = Point(int(ptFrom.X + ptDiff.Y * (4.0) / rArrow), int(ptFrom.Y - ptDiff.X * (4.0) / rArrow));
pDC->SetBrush(clrConnection);
pDC->FillPolygon((POINT*)ptArrow, 3);
delete path;
}
}
CRect CXTPFlowGraphPaintManager::GetNodeBoundingRectangle(CXTPFlowGraphNode* pNode)
{
CRect rc(pNode->GetLocation(), pNode->GetSize());
rc.InflateRect(m_nEllipseSize / 2, 0);
return rc;
}
BOOL CXTPFlowGraphPaintManager::HitTestConnectionArea(const CXTPFlowGraphConnectionPoint* pPoint, CPoint point)
{
CXTPFlowGraphNode* pNode = pPoint->GetNode();
if (!pNode)
return FALSE;
CXTPFlowGraphPage* pPage = pNode->GetPage();
point = pPage->ScreenToPage(point);
point.Offset(-pNode->GetLocation());
CRect rc = pPoint->m_rcPoint;
if (pPoint->GetType() & xtpFlowGraphPointInput)
{
CRect rc(rc.left - m_nEllipseSize / 2, rc.top, rc.left + m_nEllipseSize / 2, rc.bottom);
if (rc.PtInRect(point))
return TRUE;
}
if (pPoint->GetType() & xtpFlowGraphPointOutput)
{
CRect rc(rc.right - m_nEllipseSize / 2, rc.top, rc.right + m_nEllipseSize / 2, rc.bottom);
if (rc.PtInRect(point))
return TRUE;
}
return FALSE;
}
CRect CXTPFlowGraphPaintManager::GetCaptionRect(CXTPFlowGraphElement* pElement)
{
CRect rc(0, 0, 0, 0);
if (pElement->IsKindOf(RUNTIME_CLASS(CXTPFlowGraphNode)))
{
CXTPFlowGraphNode* pNode = (CXTPFlowGraphNode*)pElement;
rc = pNode->m_rcCaption;
rc.OffsetRect(pNode->GetLocation());
}
else if(pElement->IsKindOf(RUNTIME_CLASS(CXTPFlowGraphConnectionPoint)))
{
CXTPFlowGraphConnectionPoint* pConnectionPoint = (CXTPFlowGraphConnectionPoint*)pElement;
CXTPFlowGraphNode* pNode = pConnectionPoint->GetNode();
rc = pConnectionPoint->m_rcPoint;
rc.DeflateRect(m_nPointTextMargin, 0, m_nPointTextMargin, 1);
rc.OffsetRect(pNode->GetLocation());
}
else
{
ASSERT(FALSE);
}
return rc;
}
void CXTPFlowGraphPaintManager::DrawNodeGroup(CXTPFlowGraphDrawContext* pDC, CXTPFlowGraphNodeGroup* pNodeGroup)
{
CRect rc = pNodeGroup->GetWorkRect();
rc.InflateRect(m_nEllipseSize / 2, 10);
BOOL bSelected = pNodeGroup->GetAt(0)->IsSelected();
COLORREF clrGroup = bSelected ? m_clrSelection : RGB(220, 220, 220);
COLORREF clrSelection = clrGroup | 0x55000000;
pDC->FillSolidRect(rc, clrSelection);
COLORREF clrFrame = GetDarkColor(clrGroup);
pDC->DrawFrame(rc.left, rc.top, rc.Width(), rc.Height(), 1, clrFrame);
}