// 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 #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); }