!B (Sandbox) (CE-21795) Importing models with multisubmaterials via fbx switches...
[CRYENGINE.git] / Code / Sandbox / Plugins / EditorSchematyc2 / Editor / GraphView.cpp
blob36ae64960fb4bbfff0b60b8127a6d98c2e936870
1 // Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "StdAfx.h"
4 #include "GraphView.h"
6 #include <CrySystem/ITimer.h>
8 #include "GdiplusUtils.h"
9 #include "GraphViewFormatter.h"
10 #include "PluginUtils.h"
12 #pragma warning(disable: 4355)
14 namespace Schematyc2
16 const float CDefaultGraphViewPainter::NODE_BEVEL = 15.0f;
17 const BYTE CDefaultGraphViewPainter::NODE_HEADER_ALPHA = 200;
18 const BYTE CDefaultGraphViewPainter::NODE_HEADER_ALPHA_DISABLED = 80;
19 const float CDefaultGraphViewPainter::NODE_HEADER_TEXT_BORDER_X = 8.0f;
20 const float CDefaultGraphViewPainter::NODE_HEADER_TEXT_BORDER_Y = 2.0f;
21 const Gdiplus::Color CDefaultGraphViewPainter::NODE_HEADER_TEXT_COLOR = Gdiplus::Color(60, 60, 60);
22 const Gdiplus::Color CDefaultGraphViewPainter::NODE_ERROR_FILL_COLOR = Gdiplus::Color(210, 66, 66);
23 const char* CDefaultGraphViewPainter::NODE_ERROR_TEXT = "Error!";
24 const float CDefaultGraphViewPainter::NODE_ERROR_TEXT_BORDER_X = 8.0f;
25 const float CDefaultGraphViewPainter::NODE_ERROR_TEXT_BORDER_Y = 2.0f;
26 const Gdiplus::Color CDefaultGraphViewPainter::NODE_ERROR_TEXT_COLOR = Gdiplus::Color(60, 60, 60);
27 const Gdiplus::Color CDefaultGraphViewPainter::NODE_WARNING_FILL_COLOR = Gdiplus::Color(255, 255, 0);
28 const char* CDefaultGraphViewPainter::NODE_WARNING_TEXT = "Warning!";
29 const float CDefaultGraphViewPainter::NODE_WARNING_TEXT_BORDER_X = 8.0f;
30 const float CDefaultGraphViewPainter::NODE_WARNING_TEXT_BORDER_Y = 2.0f;
31 const Gdiplus::Color CDefaultGraphViewPainter::NODE_WARNING_TEXT_COLOR = Gdiplus::Color(60, 60, 60);
32 const Gdiplus::Color CDefaultGraphViewPainter::NODE_CONTENTS_FILL_COLOR = Gdiplus::Color(200, 40, 40, 40);
33 const float CDefaultGraphViewPainter::NODE_CONTENTS_TEXT_BORDER_X = 8.0f;
34 const float CDefaultGraphViewPainter::NODE_CONTENTS_TEXT_BORDER_Y = 2.0f;
35 const float CDefaultGraphViewPainter::NODE_CONTENTS_TEXT_MAX_WIDTH = 300.0f;
36 const float CDefaultGraphViewPainter::NODE_CONTENTS_TEXT_MAX_HEIGHT = 200.0f;
37 const Gdiplus::Color CDefaultGraphViewPainter::NODE_CONTENTS_TEXT_COLOR = Gdiplus::Color(255, 255, 255);
38 const Gdiplus::Color CDefaultGraphViewPainter::NODE_BODY_FILL_COLOR = Gdiplus::Color(200, 40, 40, 40);
39 const Gdiplus::Color CDefaultGraphViewPainter::NODE_BODY_FILL_COLOR_DISABLED = Gdiplus::Color(80, 40, 40, 40);
40 const Gdiplus::Color CDefaultGraphViewPainter::NODE_BODY_OUTLINE_COLOR = Gdiplus::Color(200, 40, 40, 40);
41 const Gdiplus::Color CDefaultGraphViewPainter::NODE_BODY_OUTLINE_COLOR_HIGHLIGHT = Gdiplus::Color(250, 232, 12);
42 const float CDefaultGraphViewPainter::NODE_BODY_OUTLINE_WIDTH = 1.0f;
43 const float CDefaultGraphViewPainter::NODE_INPUT_OUPUT_HORZ_SPACING = 40.0f;
44 const float CDefaultGraphViewPainter::NODE_INPUT_OUPUT_VERT_SPACING = 5.0f;
45 const float CDefaultGraphViewPainter::NODE_INPUT_ICON_BORDER = 5.0f;
46 const float CDefaultGraphViewPainter::NODE_INPUT_ICON_WIDTH = 11.0f;
47 const float CDefaultGraphViewPainter::NODE_INPUT_ICON_HEIGHT = 11.0f;
48 const Gdiplus::Color CDefaultGraphViewPainter::NODE_INPUT_ICON_OUTLINE_COLOR = Gdiplus::Color(200, 20, 20, 20);
49 const float CDefaultGraphViewPainter::NODE_INPUT_ICON_OUTLINE_WIDTH = 1.0f;
50 const float CDefaultGraphViewPainter::NODE_INPUT_NAME_BORDER = 2.0f;
51 const float CDefaultGraphViewPainter::NODE_INPUT_NAME_WIDTH_MAX = 400.0f;
52 const Gdiplus::Color CDefaultGraphViewPainter::NODE_INPUT_NAME_COLOR = Gdiplus::Color(240, 240, 240);
53 const float CDefaultGraphViewPainter::NODE_OUTPUT_ICON_BORDER = 5.0f;
54 const float CDefaultGraphViewPainter::NODE_OUTPUT_ICON_WIDTH = 11.0f;
55 const float CDefaultGraphViewPainter::NODE_OUTPUT_ICON_HEIGHT = 11.0f;
56 const Gdiplus::Color CDefaultGraphViewPainter::NODE_OUTPUT_ICON_OUTLINE_COLOR = Gdiplus::Color(200, 20, 20, 20);
57 const float CDefaultGraphViewPainter::NODE_OUTPUT_ICON_OUTLINE_WIDTH = 1.0f;
58 const float CDefaultGraphViewPainter::NODE_OUTPUT_NAME_BORDER = 2.0f;
59 const float CDefaultGraphViewPainter::NODE_OUTPUT_NAME_WIDTH_MAX = 400.0f;
60 const Gdiplus::Color CDefaultGraphViewPainter::NODE_OUTPUT_NAME_COLOR = Gdiplus::Color(240, 240, 240);
61 const float CDefaultGraphViewPainter::NODE_OUTPUT_SPACER_OFFSET = 3.0f;
62 const float CDefaultGraphViewPainter::NODE_OUTPUT_SPACER_WIDTH = 80.0f;
63 const float CDefaultGraphViewPainter::NODE_OUTPUT_SPACER_LINE_WIDTH = 1.0f;
64 const Gdiplus::Color CDefaultGraphViewPainter::NODE_OUTPUT_SPACER_COLOR = Gdiplus::Color(140, 120, 120, 120);
65 const float CDefaultGraphViewPainter::LINK_WIDTH = 2.0f;
66 const float CDefaultGraphViewPainter::LINK_CURVE_OFFSET = 45.0f;
67 const float CDefaultGraphViewPainter::LINK_DODGE_X = 15.0f;
68 const float CDefaultGraphViewPainter::LINK_DODGE_Y = 15.0f;
69 const float CDefaultGraphViewPainter::ALPHA_HIGHLIGHT_MIN = 0.3f;
70 const float CDefaultGraphViewPainter::ALPHA_HIGHLIGHT_MAX = 1.0f;
71 const float CDefaultGraphViewPainter::ALPHA_HIGHLIGHT_SPEED = 0.8f;
72 const Gdiplus::Color CDefaultGraphViewPainter::SELECTION_FILL_COLOR = Gdiplus::Color(100, 0, 142, 212);
73 const Gdiplus::Color CDefaultGraphViewPainter::SELECTION_OUTLINE_COLOR = Gdiplus::Color(0, 162, 232);
75 const float CGraphView::MIN_ZOOM = 0.28f;
76 const float CGraphView::MAX_ZOOM = 1.6f;
77 const float CGraphView::DELTA_ZOOM = 0.001f;
78 const float CGraphView::DEFAULT_NODE_SPACING = 40.0f;
80 namespace Assets
82 static const wchar_t* FONT_NAME = L"Tahoma";
83 static const float FONT_SIZE = 8.0f;
84 static const Gdiplus::FontStyle FONT_STYLE = Gdiplus::FontStyleBold;
86 //////////////////////////////////////////////////////////////////////////
87 static inline const Gdiplus::Font& GetFont()
89 static const Gdiplus::Font font(FONT_NAME, FONT_SIZE, FONT_STYLE);
90 return font;
94 static const UINT UPDATE_TIMER_EVENT_ID = 1;
96 //////////////////////////////////////////////////////////////////////////
97 SGraphViewFormatSettings::SGraphViewFormatSettings()
98 : horzSpacing(40.0f)
99 , vertSpacing(10.0f)
100 , bSnapToGrid(true)
103 //////////////////////////////////////////////////////////////////////////
104 void SGraphViewFormatSettings::Serialize(Serialization::IArchive& archive)
106 archive(horzSpacing, "horzSpacing", "Horizontal Spacing");
107 archive(vertSpacing, "vertSpacing", "Vertical Spacing");
108 archive(bSnapToGrid, "bSnapToGrid", "Snap To Grid");
111 //////////////////////////////////////////////////////////////////////////
112 void SGraphViewSettings::Serialize(Serialization::IArchive& archive)
114 archive(format, "format", "Format");
117 //////////////////////////////////////////////////////////////////////////
118 CGraphViewNode::CGraphViewNode(const SGraphViewGrid& grid, Gdiplus::PointF pos)
119 : m_grid(grid)
120 , m_enabled(false)
121 , m_selected(false)
122 , m_statusFlags(EGraphViewNodeStatusFlags::None)
123 , m_paintRect(grid.SnapPos(pos), Gdiplus::SizeF()) // #SchematycTODO : Might make more sense to default initialize this.
126 //////////////////////////////////////////////////////////////////////////
127 CGraphViewNode::~CGraphViewNode() {}
129 //////////////////////////////////////////////////////////////////////////
130 const SGraphViewGrid& CGraphViewNode::GetGrid() const
132 return m_grid;
135 //////////////////////////////////////////////////////////////////////////
136 void CGraphViewNode::SetPos(Gdiplus::PointF pos, bool bSnapToGrid)
138 if(bSnapToGrid)
140 pos = m_grid.SnapPos(pos);
142 m_paintRect.X = pos.X;
143 m_paintRect.Y = pos.Y;
144 OnMove(m_paintRect);
147 //////////////////////////////////////////////////////////////////////////
148 Gdiplus::PointF CGraphViewNode::GetPos() const
150 return Gdiplus::PointF(m_paintRect.X, m_paintRect.Y);
153 //////////////////////////////////////////////////////////////////////////
154 void CGraphViewNode::SetPaintRect(Gdiplus::RectF paintRect)
156 m_paintRect = paintRect;
157 OnMove(m_paintRect);
160 //////////////////////////////////////////////////////////////////////////
161 Gdiplus::RectF CGraphViewNode::GetPaintRect() const
163 return m_paintRect;
166 //////////////////////////////////////////////////////////////////////////
167 void CGraphViewNode::Enable(bool enable)
169 m_enabled = enable;
172 //////////////////////////////////////////////////////////////////////////
173 bool CGraphViewNode::IsEnabled() const
175 return m_enabled;
178 //////////////////////////////////////////////////////////////////////////
179 void CGraphViewNode::Select(bool select)
181 m_selected = select;
184 //////////////////////////////////////////////////////////////////////////
185 bool CGraphViewNode::IsSelected() const
187 return m_selected;
190 //////////////////////////////////////////////////////////////////////////
191 void CGraphViewNode::SetStatusFlags(EGraphViewNodeStatusFlags statusFlags)
193 m_statusFlags = statusFlags;
196 //////////////////////////////////////////////////////////////////////////
197 EGraphViewNodeStatusFlags CGraphViewNode::GetStatusFlags() const
199 return m_statusFlags;
202 //////////////////////////////////////////////////////////////////////////
203 size_t CGraphViewNode::FindInput(Gdiplus::PointF pos) const
205 // #SchematycTODO : Do we need to double check that the paint rects are valid? Maybe we should just reference the ports by name to be safe?
206 for(TRectVector::const_iterator iBeginInputPaintRect = m_inputPaintRects.begin(), iEndInputPaintRect = m_inputPaintRects.end(), iInputPaintRect = iBeginInputPaintRect; iInputPaintRect != iEndInputPaintRect; ++ iInputPaintRect)
208 if(iInputPaintRect->Contains(pos))
210 return iInputPaintRect - iBeginInputPaintRect;
213 return INVALID_INDEX;
216 //////////////////////////////////////////////////////////////////////////
217 void CGraphViewNode::SetInputPaintRect(size_t iInput, Gdiplus::RectF paintRect)
219 if(iInput >= m_inputPaintRects.size())
221 m_inputPaintRects.resize(iInput + 1);
223 m_inputPaintRects[iInput] = paintRect;
226 //////////////////////////////////////////////////////////////////////////
227 Gdiplus::RectF CGraphViewNode::GetInputPaintRect(size_t iInput) const
229 return iInput < m_inputPaintRects.size() ? m_inputPaintRects[iInput] : Gdiplus::RectF();
232 //////////////////////////////////////////////////////////////////////////
233 Gdiplus::PointF CGraphViewNode::GetInputLinkPoint(size_t iInput) const
235 if(iInput < m_inputPaintRects.size())
237 const Gdiplus::RectF paintRect = m_inputPaintRects[iInput];
238 return Gdiplus::PointF(paintRect.GetLeft(), paintRect.Y + (paintRect.Height / 2.0f));
240 else
242 return Gdiplus::PointF();
246 //////////////////////////////////////////////////////////////////////////
247 size_t CGraphViewNode::FindOutput(Gdiplus::PointF pos) const
249 // #SchematycTODO : Do we need to double check that the paint rects are valid? Maybe we should just reference the ports by name to be safe?
250 for(TRectVector::const_iterator iBeginOutputPaintRect = m_outputPaintRects.begin(), iEndOutputPaintRect = m_outputPaintRects.end(), iOutputPaintRect = iBeginOutputPaintRect; iOutputPaintRect != iEndOutputPaintRect; ++ iOutputPaintRect)
252 if(iOutputPaintRect->Contains(pos))
254 return iOutputPaintRect - iBeginOutputPaintRect;
257 return INVALID_INDEX;
260 //////////////////////////////////////////////////////////////////////////
261 void CGraphViewNode::SetOutputPaintRect(size_t iOutput, Gdiplus::RectF paintRect)
263 if(iOutput >= m_outputPaintRects.size())
265 m_outputPaintRects.resize(iOutput + 1);
267 m_outputPaintRects[iOutput] = paintRect;
270 //////////////////////////////////////////////////////////////////////////
271 Gdiplus::RectF CGraphViewNode::GetOutputPaintRect(size_t iOutput) const
273 return iOutput < m_outputPaintRects.size() ? m_outputPaintRects[iOutput] : Gdiplus::RectF();
276 //////////////////////////////////////////////////////////////////////////
277 Gdiplus::PointF CGraphViewNode::GetOutputLinkPoint(size_t iOutput) const
279 if(iOutput < m_outputPaintRects.size())
281 const Gdiplus::RectF paintRect = m_outputPaintRects[iOutput];
282 return Gdiplus::PointF(paintRect.GetRight(), paintRect.Y + (paintRect.Height / 2.0f));
284 else
286 return Gdiplus::PointF();
290 //////////////////////////////////////////////////////////////////////////
291 const char* CGraphViewNode::GetName() const
293 return "";
296 //////////////////////////////////////////////////////////////////////////
297 const char* CGraphViewNode::GetContents() const
299 return nullptr;
302 //////////////////////////////////////////////////////////////////////////
303 Gdiplus::Color CGraphViewNode::GetHeaderColor() const
305 return Gdiplus::Color();
308 //////////////////////////////////////////////////////////////////////////
309 size_t CGraphViewNode::GetInputCount() const
311 return 0;
314 //////////////////////////////////////////////////////////////////////////
315 size_t CGraphViewNode::FindInput(const char* name) const
317 return INVALID_INDEX;
320 //////////////////////////////////////////////////////////////////////////
321 const char* CGraphViewNode::GetInputName(size_t iInput) const
323 return "";
326 //////////////////////////////////////////////////////////////////////////
327 EScriptGraphPortFlags CGraphViewNode::GetInputFlags(size_t iInput) const
329 return EScriptGraphPortFlags::None;
332 //////////////////////////////////////////////////////////////////////////
333 Gdiplus::Color CGraphViewNode::GetInputColor(size_t iInput) const
335 return Gdiplus::Color();
338 //////////////////////////////////////////////////////////////////////////
339 size_t CGraphViewNode::GetOutputCount() const
341 return 0;
344 //////////////////////////////////////////////////////////////////////////
345 size_t CGraphViewNode::FindOutput(const char* name) const
347 return INVALID_INDEX;
350 //////////////////////////////////////////////////////////////////////////
351 const char* CGraphViewNode::GetOutputName(size_t iOutput) const
353 return "";
356 //////////////////////////////////////////////////////////////////////////
357 EScriptGraphPortFlags CGraphViewNode::GetOutputFlags(size_t iOutput) const
359 return EScriptGraphPortFlags::None;
362 //////////////////////////////////////////////////////////////////////////
363 Gdiplus::Color CGraphViewNode::GetOutputColor(size_t iInput) const
365 return Gdiplus::Color();
368 //////////////////////////////////////////////////////////////////////////
369 void CGraphViewNode::OnMove(Gdiplus::RectF paintRect) {}
371 //////////////////////////////////////////////////////////////////////////
372 SGraphViewLink::SGraphViewLink(CGraphView* _pGraphView, const CGraphViewNodeWeakPtr& _pSrcNode, const char* _srcOutputName, const CGraphViewNodeWeakPtr& _pDstNode, const char* _dstInputName)
373 : pGraphView(_pGraphView)
374 , pSrcNode(_pSrcNode)
375 , srcOutputName(_srcOutputName)
376 , pDstNode(_pDstNode)
377 , dstInputName(_dstInputName)
378 , selected(false)
381 //////////////////////////////////////////////////////////////////////////
382 SDefaultGraphViewPainterSettings::SDefaultGraphViewPainterSettings()
383 : backgroundColor(60, 60, 60, 255)
385 gridLineColors[0] = ColorB(68, 68, 68, 255);
386 gridLineColors[1] = ColorB(48, 48, 48, 255);
389 //////////////////////////////////////////////////////////////////////////
390 void SDefaultGraphViewPainterSettings::Serialize(Serialization::IArchive& archive)
392 archive(backgroundColor, "backgroundColor", "Background Color");
393 archive(gridLineColors, "gridLineColors", "Grid Line Colors");
396 //////////////////////////////////////////////////////////////////////////
397 Serialization::SStruct CDefaultGraphViewPainter::GetSettings()
399 return Serialization::SStruct(m_settings);
402 //////////////////////////////////////////////////////////////////////////
403 Gdiplus::Color CDefaultGraphViewPainter::GetBackgroundColor() const
405 return GdiplusUtils::CreateColor(m_settings.backgroundColor);
408 //////////////////////////////////////////////////////////////////////////
409 void CDefaultGraphViewPainter::PaintGrid(Gdiplus::Graphics& graphics, const SGraphViewGrid& grid) const
411 const int left = static_cast<int>(grid.bounds.X);
412 const int right = static_cast<int>(grid.bounds.X + grid.bounds.Width);
413 const int top = static_cast<int>(grid.bounds.Y);
414 const int bottom = static_cast<int>(grid.bounds.Y + grid.bounds.Height);
415 const int horzSpacing = static_cast<int>(grid.spacing.X);
416 const int vertSpacing = static_cast<int>(grid.spacing.Y);
418 Gdiplus::Pen pen0(GdiplusUtils::CreateColor(m_settings.gridLineColors[0]), 1.0f);
419 Gdiplus::Pen pen1(GdiplusUtils::CreateColor(m_settings.gridLineColors[1]), 1.0f);
421 for(int x = left + horzSpacing; x < right; x += static_cast<int>(grid.spacing.X))
423 if(x % (horzSpacing * 10))
425 graphics.DrawLine(&pen0, x, top, x, bottom);
427 else
429 graphics.DrawLine(&pen1, x, top, x, bottom);
433 for(int y = top + vertSpacing; y < bottom; y += static_cast<int>(grid.spacing.Y))
435 if(y % (vertSpacing * 10))
437 graphics.DrawLine(&pen0, left, y, right, y);
439 else
441 graphics.DrawLine(&pen1, left, y, right, y);
446 //////////////////////////////////////////////////////////////////////////
447 void CDefaultGraphViewPainter::UpdateNodePaintRect(Gdiplus::Graphics& graphics, const SGraphViewGrid& grid, CGraphViewNode& node) const
449 // Calculate node header and status sizes.
450 const Gdiplus::PointF headerSize = CalculateNodeHeaderSize(graphics, node);
451 const Gdiplus::PointF statusSize = CalculateNodeStatusSize(graphics, node);
452 const Gdiplus::PointF headerAndStatusSize(std::max(headerSize.X, statusSize.X), headerSize.Y + statusSize.Y);
453 const Gdiplus::PointF contentsSize = CalculateNodeContentsSize(graphics, node);
454 const Gdiplus::PointF headerAndStatusAndContentsSize(std::max(headerAndStatusSize.X, contentsSize.X), headerAndStatusSize.Y + contentsSize.Y);
455 // Calculate total size of all node inputs.
456 Gdiplus::PointF totalInputSize;
457 const size_t inputCount = node.GetInputCount();
458 for(size_t inputIdx = 0; inputIdx < inputCount; ++ inputIdx)
460 const Gdiplus::PointF inputSize = CalculateNodeInputSize(graphics, node.GetInputName(inputIdx));
461 if(inputSize.X > totalInputSize.X)
463 totalInputSize.X = inputSize.X;
465 totalInputSize.Y += inputSize.Y + NODE_INPUT_OUPUT_VERT_SPACING;
467 totalInputSize.Y += NODE_INPUT_OUPUT_VERT_SPACING;
468 // Calculate total size of all node outputs.
469 Gdiplus::PointF totalOutputSize;
470 const size_t outputCount = node.GetOutputCount();
471 for(size_t outputIdx = 0; outputIdx < outputCount; ++ outputIdx)
473 const Gdiplus::PointF output = CalculateNodeOutputSize(graphics, node.GetOutputName(outputIdx));
474 if(output.X > totalOutputSize.X)
476 totalOutputSize.X = output.X;
478 totalOutputSize.Y += output.Y + NODE_INPUT_OUPUT_VERT_SPACING;
480 totalOutputSize.Y += NODE_INPUT_OUPUT_VERT_SPACING;
481 // Update node paint rect.
482 const Gdiplus::SizeF paintSize(std::max(headerAndStatusAndContentsSize.X, totalInputSize.X + NODE_INPUT_OUPUT_HORZ_SPACING + totalOutputSize.X), headerAndStatusAndContentsSize.Y + std::max(totalInputSize.Y, totalOutputSize.Y));
483 const Gdiplus::RectF paintRect(node.GetPos(), grid.SnapSize(paintSize));
484 node.SetPaintRect(paintRect);
485 // Update node input paint rects.
486 Gdiplus::PointF inputPos(paintRect.X, paintRect.Y + headerAndStatusAndContentsSize.Y + NODE_INPUT_OUPUT_VERT_SPACING);
487 for(size_t inputIdx = 0; inputIdx < inputCount; ++ inputIdx)
489 UpdateNodeInputPaintRect(graphics, node, inputIdx, inputPos, totalInputSize.X);
490 inputPos.Y += node.GetInputPaintRect(inputIdx).Height + NODE_INPUT_OUPUT_VERT_SPACING;
492 // Update node output paint rects.
493 Gdiplus::PointF outputPos(paintRect.X + paintRect.Width - totalOutputSize.X, paintRect.Y + headerAndStatusAndContentsSize.Y + NODE_INPUT_OUPUT_VERT_SPACING);
494 for(size_t outputIdx = 0; outputIdx < outputCount; ++ outputIdx)
496 UpdateNodeOutputPaintRect(graphics, node, outputIdx, outputPos, totalOutputSize.X);
497 outputPos.Y += node.GetOutputPaintRect(outputIdx).Height + NODE_INPUT_OUPUT_VERT_SPACING;
501 //////////////////////////////////////////////////////////////////////////
502 void CDefaultGraphViewPainter::PaintNode(Gdiplus::Graphics& graphics, const SGraphViewGrid& grid, CGraphViewNode& node) const
504 // Paint node body, header and outline
505 const Gdiplus::RectF paintRect = node.GetPaintRect();
506 const Gdiplus::PointF headerSize = CalculateNodeHeaderSize(graphics, node);
507 const Gdiplus::PointF statusSize = CalculateNodeStatusSize(graphics, node);
508 const Gdiplus::PointF contentsSize = CalculateNodeContentsSize(graphics, node);
509 PaintNodeBody(graphics, node, Gdiplus::RectF(paintRect.X, paintRect.Y + headerSize.Y + statusSize.Y + contentsSize.Y, paintRect.Width, paintRect.Height - headerSize.Y - statusSize.Y - contentsSize.Y));
510 PaintNodeHeader(graphics, node, Gdiplus::RectF(paintRect.X, paintRect.Y, paintRect.Width, headerSize.Y));
511 PaintNodeContents(graphics, node, Gdiplus::RectF(paintRect.X, paintRect.Y + headerSize.Y, paintRect.Width, contentsSize.Y));
512 PaintNodeStatus(graphics, node, Gdiplus::RectF(paintRect.X, paintRect.Y + headerSize.Y + contentsSize.Y, paintRect.Width, headerSize.Y));
513 PaintNodeOutline(graphics, node, paintRect);
514 // Paint node inputs.
515 for(size_t inputIdx = 0, inputCount = node.GetInputCount(); inputIdx < inputCount; ++ inputIdx)
517 PaintNodeInput(graphics, node, inputIdx);
519 // Paint node outputs.
520 for(size_t outputIdx = 0, outputCount = node.GetOutputCount(); outputIdx < outputCount; ++ outputIdx)
522 PaintNodeOutput(graphics, node, outputIdx);
526 //////////////////////////////////////////////////////////////////////////
527 Gdiplus::RectF CDefaultGraphViewPainter::PaintLink(Gdiplus::Graphics& graphics, Gdiplus::PointF startPoint, Gdiplus::PointF endPoint, Gdiplus::Color color, bool highlight, float scale) const
529 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
530 // Part of temporary fix for slow graphs with lots of links, until we can optimize the intersection tests properly.
531 if((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0)
533 scale *= 1.75f;
535 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
536 // Animate color?
537 if(highlight)
539 const float time = gEnv->pTimer->GetAsyncTime().GetSeconds();
540 const float sinA = sin_tpl(time * ALPHA_HIGHLIGHT_SPEED * gf_PI2);
541 const float alpha = ALPHA_HIGHLIGHT_MIN + clamp_tpl(ALPHA_HIGHLIGHT_MIN + (sinA * ((ALPHA_HIGHLIGHT_MAX - ALPHA_HIGHLIGHT_MIN) * 0.5f)), 0.0f, 1.0f);
542 color = Gdiplus::Color::MakeARGB(static_cast<BYTE>(alpha * 255.0f), color.GetRed(), color.GetGreen(), color.GetBlue());
544 // Calculate control points.
545 const bool leftToRight = startPoint.X < endPoint.X;
546 const float verticalSign = startPoint.Y < endPoint.Y ? 1.0f : -1.0f;
547 Gdiplus::PointF midPoint((startPoint.X + endPoint.X) / 2.0f, (startPoint.Y + endPoint.Y) / 2.0f);
548 const size_t controlPointCount = 8;
549 Gdiplus::PointF controlPoints[controlPointCount];
550 controlPoints[0] = startPoint;
552 const float offset = fabs_tpl(midPoint.X - startPoint.X) * 0.5f;
553 controlPoints[1].X = startPoint.X + offset;
554 if(leftToRight)
556 controlPoints[1].Y = startPoint.Y;
558 else
560 const float extent = fabs_tpl(midPoint.Y - startPoint.Y) * 0.95f;
561 controlPoints[1].Y = startPoint.Y + clamp_tpl(offset * 0.5f * verticalSign, -extent, extent);
564 controlPoints[2].X = Lerp(controlPoints[1].X, midPoint.X, 0.75f);
565 controlPoints[2].Y = Lerp(controlPoints[1].Y, midPoint.Y, 0.75f);
566 controlPoints[3] = midPoint;
567 controlPoints[7] = endPoint;
569 const float offset = fabs_tpl(midPoint.X - endPoint.X) * 0.5f;
570 controlPoints[6].X = endPoint.X - offset;
571 if(leftToRight)
573 controlPoints[6].Y = endPoint.Y;
575 else
577 const float extent = fabs_tpl(midPoint.Y - endPoint.Y) * 0.95f;
578 controlPoints[6].Y = endPoint.Y - clamp_tpl(offset * 0.5f * verticalSign, -extent, extent);
581 controlPoints[5].X = Lerp(controlPoints[6].X, midPoint.X, 0.75f);
582 controlPoints[5].Y = Lerp(controlPoints[6].Y, midPoint.Y, 0.75f);
583 controlPoints[4] = midPoint;
584 // Catch weird glitch when control points are close to parallel on y axis.
585 for(size_t i = 1; i < controlPointCount; ++ i)
587 if(fabs_tpl(controlPoints[i].Y - controlPoints[0].Y) <= 1.0f)
589 controlPoints[i].Y = controlPoints[0].Y;
592 // Draw beziers.
593 const float width = LINK_WIDTH * scale;
594 Gdiplus::Pen pen(color, width);
595 graphics.DrawBezier(&pen, controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3]);
596 graphics.DrawBezier(&pen, controlPoints[4], controlPoints[5], controlPoints[6], controlPoints[7]);
597 #if FALSE
598 // Debug draw control points.
599 for(size_t i = 0; i < controlPointCount; ++ i)
601 graphics.DrawEllipse(&pen, Gdiplus::RectF(controlPoints[i].X - 2.0f, controlPoints[i].Y - 2.0f, 4.0f, 4.0f));
603 #endif
604 // Calculate paint rectangle.
605 // N.B. Technically this not how you calculate the bounds of a bezier, but it's sufficient for our purposes.
606 Gdiplus::PointF pmin = controlPoints[0];
607 Gdiplus::PointF pmax = controlPoints[0];
608 for(size_t i = 1; i < controlPointCount; ++ i)
610 if(controlPoints[i].X < pmin.X)
612 pmin.X = controlPoints[i].X;
614 if(controlPoints[i].Y < pmin.Y)
616 pmin.Y = controlPoints[i].Y;
618 if(controlPoints[i].X > pmax.X)
620 pmax.X = controlPoints[i].X;
622 if(controlPoints[i].Y > pmax.Y)
624 pmax.Y = controlPoints[i].Y;
627 return Gdiplus::RectF(pmin.X - width, pmin.Y - width, (pmax.X - pmin.X) + (width * 2.0f), (pmax.Y - pmin.Y) + (width * 2.0f));
630 //////////////////////////////////////////////////////////////////////////
631 void CDefaultGraphViewPainter::PaintSelectionRect(Gdiplus::Graphics& graphics, Gdiplus::RectF rect) const
633 Gdiplus::SolidBrush brush(SELECTION_FILL_COLOR);
634 graphics.FillRectangle(&brush, rect);
636 Gdiplus::Pen pen(SELECTION_OUTLINE_COLOR, 1.0f);
637 graphics.DrawRectangle(&pen, rect);
640 //////////////////////////////////////////////////////////////////////////
641 Gdiplus::PointF CDefaultGraphViewPainter::CalculateNodeHeaderSize(Gdiplus::Graphics& graphics, const CGraphViewNode& node) const
643 CStringW headerText(node.GetName());
644 if(headerText.IsEmpty())
646 headerText = " ";
648 Gdiplus::RectF headerTextRect;
649 graphics.MeasureString(headerText, headerText.GetLength(), &Assets::GetFont(), Gdiplus::PointF(), &headerTextRect);
650 return Gdiplus::PointF(NODE_HEADER_TEXT_BORDER_X + headerTextRect.Width + NODE_HEADER_TEXT_BORDER_X, NODE_HEADER_TEXT_BORDER_Y + headerTextRect.Height + NODE_HEADER_TEXT_BORDER_Y);
653 //////////////////////////////////////////////////////////////////////////
654 Gdiplus::PointF CDefaultGraphViewPainter::CalculateNodeStatusSize(Gdiplus::Graphics& graphics, const CGraphViewNode& node) const
656 Gdiplus::RectF statusTextRect;
657 const EGraphViewNodeStatusFlags statusFlags = node.GetStatusFlags();
658 if((statusFlags & EGraphViewNodeStatusFlags::ContainsErrors) != 0)
660 const CStringW statusText(NODE_ERROR_TEXT);
661 graphics.MeasureString(statusText, statusText.GetLength(), &Assets::GetFont(), Gdiplus::PointF(), &statusTextRect);
662 statusTextRect.Width += NODE_ERROR_TEXT_BORDER_X + NODE_ERROR_TEXT_BORDER_X;
663 statusTextRect.Height += NODE_ERROR_TEXT_BORDER_Y + NODE_ERROR_TEXT_BORDER_Y;
665 else if((statusFlags & EGraphViewNodeStatusFlags::ContainsWarnings) != 0)
667 const CStringW statusText(NODE_WARNING_TEXT);
668 graphics.MeasureString(statusText, statusText.GetLength(), &Assets::GetFont(), Gdiplus::PointF(), &statusTextRect);
669 statusTextRect.Width += NODE_WARNING_TEXT_BORDER_X + NODE_WARNING_TEXT_BORDER_X;
670 statusTextRect.Height += NODE_WARNING_TEXT_BORDER_Y + NODE_WARNING_TEXT_BORDER_Y;
672 return Gdiplus::PointF(statusTextRect.Width, statusTextRect.Height);
675 //////////////////////////////////////////////////////////////////////////
676 Gdiplus::PointF CDefaultGraphViewPainter::CalculateNodeContentsSize(Gdiplus::Graphics& graphics, const CGraphViewNode& node) const
678 if (const char * const nodeContents = node.GetContents())
680 Gdiplus::RectF contentsTextRect;
681 const CStringW contentsText(nodeContents);
682 const Gdiplus::RectF layoutRect(0.0f, 0.0f, NODE_CONTENTS_TEXT_MAX_WIDTH, NODE_CONTENTS_TEXT_MAX_HEIGHT);
683 graphics.MeasureString(contentsText, contentsText.GetLength(), &Assets::GetFont(), layoutRect, Gdiplus::StringFormat::GenericTypographic(), &contentsTextRect);
684 contentsTextRect.Width += NODE_CONTENTS_TEXT_BORDER_X + NODE_CONTENTS_TEXT_BORDER_X;
685 contentsTextRect.Height += NODE_CONTENTS_TEXT_BORDER_Y + NODE_CONTENTS_TEXT_BORDER_Y;
686 return Gdiplus::PointF(contentsTextRect.Width, contentsTextRect.Height);
688 return Gdiplus::PointF();
691 //////////////////////////////////////////////////////////////////////////
692 Gdiplus::PointF CDefaultGraphViewPainter::CalculateNodeInputSize(Gdiplus::Graphics& graphics, const char* name) const
694 const CStringW stringW(name != NULL ? name : "");
695 Gdiplus::RectF nameRect;
696 graphics.MeasureString(stringW, stringW.GetLength(), &Assets::GetFont(), Gdiplus::PointF(), &nameRect);
698 const Gdiplus::PointF iconSize(NODE_INPUT_ICON_BORDER + NODE_INPUT_ICON_WIDTH + NODE_INPUT_ICON_BORDER, NODE_INPUT_ICON_BORDER + NODE_INPUT_ICON_HEIGHT + NODE_INPUT_ICON_BORDER);
699 const Gdiplus::PointF nameSize(NODE_INPUT_NAME_BORDER + nameRect.Width + NODE_INPUT_NAME_BORDER, NODE_INPUT_NAME_BORDER + nameRect.Height + NODE_INPUT_NAME_BORDER);
700 return Gdiplus::PointF(iconSize.X + nameSize.X, std::max(iconSize.Y, nameSize.Y));
703 //////////////////////////////////////////////////////////////////////////
704 Gdiplus::PointF CDefaultGraphViewPainter::CalculateNodeOutputSize(Gdiplus::Graphics& graphics, const char* name) const
706 const CStringW stringW(name != NULL ? name : "");
707 Gdiplus::RectF nameRect;
708 graphics.MeasureString(stringW, stringW.GetLength(), &Assets::GetFont(), Gdiplus::PointF(), &nameRect);
710 const Gdiplus::PointF iconSize(NODE_OUTPUT_ICON_BORDER + NODE_OUTPUT_ICON_WIDTH + NODE_OUTPUT_ICON_BORDER, NODE_OUTPUT_ICON_BORDER + NODE_OUTPUT_ICON_HEIGHT + NODE_OUTPUT_ICON_BORDER);
711 const Gdiplus::PointF nameSize(NODE_OUTPUT_NAME_BORDER + nameRect.Width + NODE_OUTPUT_NAME_BORDER, NODE_OUTPUT_NAME_BORDER + nameRect.Height + NODE_OUTPUT_NAME_BORDER);
712 return Gdiplus::PointF(iconSize.X + nameSize.X, std::max(iconSize.Y, nameSize.Y));
715 //////////////////////////////////////////////////////////////////////////
716 void CDefaultGraphViewPainter::PaintNodeBody(Gdiplus::Graphics& graphics, CGraphViewNode& node, Gdiplus::RectF paintRect) const
718 const float bevels[4] = { 0.0f, 0.0f, NODE_BEVEL, NODE_BEVEL };
719 const Gdiplus::SolidBrush brush(node.IsEnabled() ? NODE_BODY_FILL_COLOR : NODE_BODY_FILL_COLOR_DISABLED);
720 GdiplusUtils::FillBeveledRect(graphics, paintRect, bevels, brush);
723 //////////////////////////////////////////////////////////////////////////
724 void CDefaultGraphViewPainter::PaintNodeHeader(Gdiplus::Graphics& graphics, CGraphViewNode& node, Gdiplus::RectF paintRect) const
726 const Gdiplus::Color headerColor = node.GetHeaderColor();
727 const BYTE alpha = node.IsEnabled() ? NODE_HEADER_ALPHA : NODE_HEADER_ALPHA_DISABLED;
728 const Gdiplus::Color fillColorLeft = Gdiplus::Color(alpha, headerColor.GetRed(), headerColor.GetGreen(), headerColor.GetBlue());
729 const Gdiplus::Color fillColorRight = GdiplusUtils::LerpColor(fillColorLeft, Gdiplus::Color(alpha, 255, 255, 255), 0.6f);
731 const float bevels[4] = { NODE_BEVEL, NODE_BEVEL, 0.0f, 0.0f };
732 const Gdiplus::LinearGradientBrush brush(paintRect, fillColorLeft, fillColorRight, Gdiplus::LinearGradientModeForwardDiagonal);
733 GdiplusUtils::FillBeveledRect(graphics, paintRect, bevels, brush);
735 const CStringW headerText(node.GetName());
736 const Gdiplus::SolidBrush textBrush(NODE_HEADER_TEXT_COLOR);
737 graphics.DrawString(headerText, headerText.GetLength(), &Assets::GetFont(), Gdiplus::PointF(paintRect.X + NODE_HEADER_TEXT_BORDER_X, paintRect.Y + NODE_HEADER_TEXT_BORDER_Y), &textBrush);
740 //////////////////////////////////////////////////////////////////////////
741 void CDefaultGraphViewPainter::PaintNodeContents(Gdiplus::Graphics& graphics, CGraphViewNode& node, Gdiplus::RectF paintRect) const
743 if (const char * const nodeContents = node.GetContents())
745 Gdiplus::SolidBrush brush(NODE_CONTENTS_FILL_COLOR);
746 graphics.FillRectangle(&brush, paintRect);
747 const CStringW contentsText(nodeContents);
748 const Gdiplus::SolidBrush textBrush(NODE_CONTENTS_TEXT_COLOR);
749 const Gdiplus::PointF pos(paintRect.X + NODE_CONTENTS_TEXT_BORDER_X, paintRect.Y + NODE_CONTENTS_TEXT_BORDER_Y);
750 const Gdiplus::RectF layoutRect(pos.X, pos.Y, NODE_CONTENTS_TEXT_MAX_WIDTH, NODE_CONTENTS_TEXT_MAX_HEIGHT);
751 graphics.DrawString(contentsText, contentsText.GetLength(), &Assets::GetFont(), layoutRect, Gdiplus::StringFormat::GenericTypographic(), &textBrush);
755 //////////////////////////////////////////////////////////////////////////
756 void CDefaultGraphViewPainter::PaintNodeStatus(Gdiplus::Graphics& graphics, CGraphViewNode& node, Gdiplus::RectF paintRect) const
758 const EGraphViewNodeStatusFlags statusFlags = node.GetStatusFlags();
759 if((statusFlags & EGraphViewNodeStatusFlags::ContainsErrors) != 0)
761 Gdiplus::SolidBrush brush(NODE_ERROR_FILL_COLOR);
762 graphics.FillRectangle(&brush, paintRect);
764 const CStringW statusText(NODE_ERROR_TEXT);
765 const Gdiplus::SolidBrush textBrush(NODE_ERROR_TEXT_COLOR);
766 graphics.DrawString(statusText, statusText.GetLength(), &Assets::GetFont(), Gdiplus::PointF(paintRect.X + NODE_ERROR_TEXT_BORDER_X, paintRect.Y + NODE_ERROR_TEXT_BORDER_Y), &textBrush);
768 else if((statusFlags & EGraphViewNodeStatusFlags::ContainsWarnings) != 0)
770 Gdiplus::SolidBrush brush(NODE_WARNING_FILL_COLOR);
771 graphics.FillRectangle(&brush, paintRect);
773 const CStringW statusText(NODE_WARNING_TEXT);
774 const Gdiplus::SolidBrush textBrush(NODE_WARNING_TEXT_COLOR);
775 graphics.DrawString(statusText, statusText.GetLength(), &Assets::GetFont(), Gdiplus::PointF(paintRect.X + NODE_WARNING_TEXT_BORDER_X, paintRect.Y + NODE_WARNING_TEXT_BORDER_Y), &textBrush);
779 //////////////////////////////////////////////////////////////////////////
780 void CDefaultGraphViewPainter::PaintNodeOutline(Gdiplus::Graphics& graphics, CGraphViewNode& node, Gdiplus::RectF paintRect) const
782 Gdiplus::Color outlineColor = NODE_BODY_OUTLINE_COLOR;
783 if(node.IsSelected())
785 const float time = gEnv->pTimer->GetAsyncTime().GetSeconds();
786 const float sinA = sin_tpl(time * ALPHA_HIGHLIGHT_SPEED * gf_PI2);
787 const float alpha = ALPHA_HIGHLIGHT_MIN + clamp_tpl(ALPHA_HIGHLIGHT_MIN + (sinA * ((ALPHA_HIGHLIGHT_MAX - ALPHA_HIGHLIGHT_MIN) * 0.5f)), 0.0f, 1.0f);
788 outlineColor = Gdiplus::Color::MakeARGB(static_cast<BYTE>(alpha * 255.0f), NODE_BODY_OUTLINE_COLOR_HIGHLIGHT.GetRed(), NODE_BODY_OUTLINE_COLOR_HIGHLIGHT.GetGreen(), NODE_BODY_OUTLINE_COLOR_HIGHLIGHT.GetBlue());
791 const float bevels[4] = { NODE_BEVEL, NODE_BEVEL, NODE_BEVEL, NODE_BEVEL };
792 const Gdiplus::Pen pen(outlineColor, NODE_BODY_OUTLINE_WIDTH);
793 GdiplusUtils::PaintBeveledRect(graphics, paintRect, bevels, pen);
796 //////////////////////////////////////////////////////////////////////////
797 void CDefaultGraphViewPainter::UpdateNodeInputPaintRect(Gdiplus::Graphics& graphics, CGraphViewNode& node, size_t iInput, Gdiplus::PointF pos, float maxWidth) const
799 const CStringW stringW(node.GetInputName(iInput));
800 Gdiplus::RectF nameRect;
801 graphics.MeasureString(stringW, stringW.GetLength(), &Assets::GetFont(), Gdiplus::PointF(), &nameRect);
803 const Gdiplus::PointF iconSize(NODE_INPUT_ICON_BORDER + NODE_INPUT_ICON_WIDTH + NODE_INPUT_ICON_BORDER, NODE_INPUT_ICON_BORDER + NODE_INPUT_ICON_HEIGHT + NODE_INPUT_ICON_BORDER);
804 const Gdiplus::PointF nameSize(NODE_INPUT_NAME_BORDER + nameRect.Width + NODE_INPUT_NAME_BORDER, NODE_INPUT_NAME_BORDER + nameRect.Height + NODE_INPUT_NAME_BORDER);
805 const Gdiplus::RectF paintRect(pos.X, pos.Y, iconSize.X + nameSize.X, std::max(iconSize.Y, nameSize.Y));
806 node.SetInputPaintRect(iInput, paintRect);
809 //////////////////////////////////////////////////////////////////////////
810 void CDefaultGraphViewPainter::PaintNodeInput(Gdiplus::Graphics& graphics, CGraphViewNode& node, size_t iInput) const
812 const Gdiplus::RectF paintRect = node.GetInputPaintRect(iInput);
813 const Gdiplus::PointF iconSize(NODE_INPUT_ICON_BORDER + NODE_INPUT_ICON_WIDTH + NODE_INPUT_ICON_BORDER, NODE_INPUT_ICON_BORDER + NODE_INPUT_ICON_HEIGHT + NODE_INPUT_ICON_BORDER);
814 Gdiplus::PointF iconPos(paintRect.X + NODE_INPUT_ICON_BORDER, paintRect.Y + NODE_INPUT_ICON_BORDER);
815 if(iconSize.Y < paintRect.Height)
817 iconPos.Y += (paintRect.Height - iconSize.Y) * 0.5f;
819 PaintNodeInputIcon(graphics, iconPos, node.GetInputColor(iInput), (node.GetInputFlags(iInput) & EScriptGraphPortFlags::Execute) != 0);
821 const CStringW stringW(node.GetInputName(iInput));
822 const Gdiplus::RectF alignedNameRect(paintRect.X + iconSize.X, paintRect.Y, paintRect.Width - iconSize.X, paintRect.Height);
823 Gdiplus::StringFormat stringFormat;
824 stringFormat.SetAlignment(Gdiplus::StringAlignmentNear);
825 stringFormat.SetLineAlignment(Gdiplus::StringAlignmentCenter);
826 const Gdiplus::SolidBrush nameBrush(NODE_OUTPUT_NAME_COLOR);
827 graphics.DrawString(stringW, stringW.GetLength(), &Assets::GetFont(), alignedNameRect, &stringFormat, &nameBrush);
830 //////////////////////////////////////////////////////////////////////////
831 void CDefaultGraphViewPainter::PaintNodeInputIcon(Gdiplus::Graphics& graphics, Gdiplus::PointF pos, Gdiplus::Color color, bool active) const
833 const Gdiplus::SolidBrush fillBrush(color);
834 const Gdiplus::Pen pen(NODE_INPUT_ICON_OUTLINE_COLOR, NODE_INPUT_ICON_OUTLINE_WIDTH);
835 if(active)
837 const Gdiplus::PointF points[] =
839 Gdiplus::PointF(pos.X, pos.Y),
840 Gdiplus::PointF(pos.X + NODE_INPUT_ICON_WIDTH, pos.Y + (NODE_INPUT_ICON_HEIGHT * 0.5f)),
841 Gdiplus::PointF(pos.X, pos.Y + NODE_INPUT_ICON_HEIGHT)
843 graphics.FillPolygon(&fillBrush, points, CRY_ARRAY_COUNT(points));
845 else
847 graphics.FillEllipse(&fillBrush, pos.X, pos.Y, NODE_INPUT_ICON_WIDTH, NODE_INPUT_ICON_HEIGHT);
851 //////////////////////////////////////////////////////////////////////////
852 void CDefaultGraphViewPainter::UpdateNodeOutputPaintRect(Gdiplus::Graphics& graphics, CGraphViewNode& node, size_t iOutput, Gdiplus::PointF pos, float maxWidth) const
854 const CStringW stringW(node.GetOutputName(iOutput));
855 Gdiplus::RectF nameRect;
856 graphics.MeasureString(stringW, stringW.GetLength(), &Assets::GetFont(), Gdiplus::PointF(), &nameRect);
858 const Gdiplus::PointF iconSize(NODE_OUTPUT_ICON_BORDER + NODE_OUTPUT_ICON_WIDTH + NODE_OUTPUT_ICON_BORDER, NODE_OUTPUT_ICON_BORDER + NODE_OUTPUT_ICON_HEIGHT + NODE_OUTPUT_ICON_BORDER);
859 const Gdiplus::PointF nameSize(NODE_OUTPUT_NAME_BORDER + nameRect.Width + NODE_OUTPUT_NAME_BORDER, NODE_OUTPUT_NAME_BORDER + nameRect.Height + NODE_OUTPUT_NAME_BORDER);
860 const Gdiplus::RectF paintRect(pos.X + maxWidth - (iconSize.X + nameSize.X), pos.Y, iconSize.X + nameSize.X, std::max(iconSize.Y, nameSize.Y));
861 node.SetOutputPaintRect(iOutput, paintRect);
864 //////////////////////////////////////////////////////////////////////////
865 void CDefaultGraphViewPainter::PaintNodeOutput(Gdiplus::Graphics& graphics, CGraphViewNode& node, size_t iOutput) const
867 const Gdiplus::RectF paintRect = node.GetOutputPaintRect(iOutput);
868 const Gdiplus::PointF iconSize(NODE_OUTPUT_ICON_BORDER + NODE_OUTPUT_ICON_WIDTH + NODE_OUTPUT_ICON_BORDER, NODE_OUTPUT_ICON_BORDER + NODE_OUTPUT_ICON_HEIGHT + NODE_OUTPUT_ICON_BORDER);
869 Gdiplus::PointF iconPos(paintRect.X + paintRect.Width - NODE_OUTPUT_ICON_WIDTH - NODE_OUTPUT_ICON_BORDER, paintRect.Y + NODE_OUTPUT_ICON_BORDER);
870 if(iconSize.Y < paintRect.Height)
872 iconPos.Y += (paintRect.Height - iconSize.Y) * 0.5f;
874 PaintNodeOutputIcon(graphics, iconPos, node.GetOutputColor(iOutput), (node.GetOutputFlags(iOutput) & EScriptGraphPortFlags::Execute) != 0);
876 const CStringW stringW(node.GetOutputName(iOutput));
877 const Gdiplus::RectF alignedNameRect(paintRect.X, paintRect.Y, paintRect.Width - iconSize.X, paintRect.Height);
878 Gdiplus::StringFormat stringFormat;
879 stringFormat.SetAlignment(Gdiplus::StringAlignmentFar);
880 stringFormat.SetLineAlignment(Gdiplus::StringAlignmentCenter);
881 const Gdiplus::SolidBrush nameBrush(NODE_OUTPUT_NAME_COLOR);
882 graphics.DrawString(stringW, stringW.GetLength(), &Assets::GetFont(), alignedNameRect, &stringFormat, &nameBrush);
884 if(((node.GetOutputFlags(iOutput) & EScriptGraphPortFlags::SpacerAbove) != 0) && (iOutput > 0))
886 const Gdiplus::Pen pen(NODE_OUTPUT_SPACER_COLOR, NODE_OUTPUT_SPACER_LINE_WIDTH);
887 const float right = paintRect.GetRight() - NODE_OUTPUT_SPACER_OFFSET;
888 const float left = right - NODE_OUTPUT_SPACER_WIDTH;
889 const float top = paintRect.GetTop() - (NODE_INPUT_OUPUT_VERT_SPACING * 0.5f);
890 graphics.DrawLine(&pen, Gdiplus::PointF(left, top), Gdiplus::PointF(right, top));
893 if(((node.GetOutputFlags(iOutput) & EScriptGraphPortFlags::SpacerBelow) != 0) && (iOutput < (node.GetOutputCount() - 1)))
895 const Gdiplus::Pen pen(NODE_OUTPUT_SPACER_COLOR, NODE_OUTPUT_SPACER_LINE_WIDTH);
896 const float right = paintRect.GetRight() - NODE_OUTPUT_SPACER_OFFSET;
897 const float left = right - NODE_OUTPUT_SPACER_WIDTH;
898 const float bottom = paintRect.GetBottom() + (NODE_INPUT_OUPUT_VERT_SPACING * 0.5f);
899 graphics.DrawLine(&pen, Gdiplus::PointF(left, bottom), Gdiplus::PointF(right, bottom));
903 //////////////////////////////////////////////////////////////////////////
904 void CDefaultGraphViewPainter::PaintNodeOutputIcon(Gdiplus::Graphics& graphics, Gdiplus::PointF pos, Gdiplus::Color color, bool active) const
906 const Gdiplus::SolidBrush fillBrush(color);
907 const Gdiplus::Pen pen(NODE_OUTPUT_ICON_OUTLINE_COLOR, NODE_OUTPUT_ICON_OUTLINE_WIDTH);
908 if(active)
910 const Gdiplus::PointF points[] =
912 Gdiplus::PointF(pos.X, pos.Y),
913 Gdiplus::PointF(pos.X + NODE_OUTPUT_ICON_WIDTH, pos.Y + (NODE_OUTPUT_ICON_HEIGHT * 0.5f)),
914 Gdiplus::PointF(pos.X, pos.Y + NODE_OUTPUT_ICON_HEIGHT)
916 graphics.FillPolygon(&fillBrush, points, CRY_ARRAY_COUNT(points));
918 else
920 graphics.FillEllipse(&fillBrush, pos.X, pos.Y, NODE_OUTPUT_ICON_WIDTH, NODE_OUTPUT_ICON_HEIGHT);
924 //////////////////////////////////////////////////////////////////////////
925 BEGIN_MESSAGE_MAP(CGraphView, CWnd)
926 ON_WM_KILLFOCUS()
927 ON_WM_PAINT()
928 ON_WM_MOUSEMOVE()
929 ON_WM_MOUSEWHEEL()
930 ON_WM_LBUTTONDOWN()
931 ON_WM_LBUTTONUP()
932 ON_WM_RBUTTONDOWN()
933 ON_WM_RBUTTONUP()
934 ON_WM_MBUTTONDOWN()
935 ON_WM_MBUTTONUP()
936 ON_WM_KEYDOWN()
937 ON_WM_KEYUP()
938 ON_WM_SETCURSOR()
939 ON_WM_TIMER()
940 END_MESSAGE_MAP()
942 //////////////////////////////////////////////////////////////////////////
943 BOOL CGraphView::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
945 if(!m_hWnd)
947 // Create view.
948 LPCTSTR lpClassName = AfxRegisterWndClass(CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, AfxGetApp()->LoadStandardCursor(IDC_ARROW), NULL, NULL);
949 VERIFY(CreateEx(NULL, lpClassName, "SchematycView", dwStyle, CRect(0, 0, 100, 100), pParentWnd, nID));
950 if(m_hWnd)
952 // Create timer.
953 CWnd::SetTimer(UPDATE_TIMER_EVENT_ID, 1000 / 30, NULL);
954 // Register drop target.
955 m_dropTarget.Register(this);
956 return true;
959 return false;
962 //////////////////////////////////////////////////////////////////////////
963 void CGraphView::Refresh()
965 __super::Invalidate(TRUE);
968 //////////////////////////////////////////////////////////////////////////
969 SGraphViewSettings& CGraphView::GetSettings()
971 return m_settings;
974 //////////////////////////////////////////////////////////////////////////
975 const SGraphViewGrid& CGraphView::GetGrid() const
977 return m_grid;
980 //////////////////////////////////////////////////////////////////////////
981 const IGraphViewPainterPtr& CGraphView::GetPainter() const
983 return m_pPainter;
986 //////////////////////////////////////////////////////////////////////////
987 TGraphViewLinkVector& CGraphView::GetLinks()
989 return m_links;
992 //////////////////////////////////////////////////////////////////////////
993 const TGraphViewLinkVector& CGraphView::GetLinks() const
995 return m_links;
998 //////////////////////////////////////////////////////////////////////////
999 void CGraphView::DoQuickSearch(const char* defaultFilter, const CGraphViewNodePtr& pNode, size_t iNodeOutput, const CPoint* pPoint)
1001 SET_LOCAL_RESOURCE_SCOPE
1002 CPoint cursorPoint;
1003 GetCursorPos(&cursorPoint);
1004 if(const IQuickSearchOptions* pQuickSearchOptions = GetQuickSearchOptions(cursorPoint, pNode, iNodeOutput))
1006 CQuickSearchDlg quickSearchDlg(this, CPoint(cursorPoint.x - 20, cursorPoint.y - 20), "Nodes", defaultFilter, nullptr, *pQuickSearchOptions);
1007 if(quickSearchDlg.DoModal() == IDOK)
1009 SetFocus(); // Ensure we regain focus from quick search dialog.
1010 OnQuickSearchResult(pPoint ? *pPoint : cursorPoint, pNode, iNodeOutput, quickSearchDlg.GetResult());
1015 //////////////////////////////////////////////////////////////////////////
1016 CGraphView::CGraphView(const SGraphViewGrid& grid, const IGraphViewPainterPtr& pPainter)
1017 : m_grid(grid)
1018 , m_pPainter(pPainter ? pPainter : IGraphViewPainterPtr(new CDefaultGraphViewPainter()))
1019 , m_enabled(false)
1020 , m_prevMousePoint(0, 0)
1021 , m_zoom(1.0f)
1022 , m_iSelectedLink(INVALID_INDEX)
1023 , m_dropTarget(*this)
1026 //////////////////////////////////////////////////////////////////////////
1027 void CGraphView::Enable(bool enable)
1029 if(enable != m_enabled)
1031 ExitDragState();
1033 m_enabled = enable;
1036 //////////////////////////////////////////////////////////////////////////
1037 bool CGraphView::IsEnabled() const
1039 return m_enabled;
1042 //////////////////////////////////////////////////////////////////////////
1043 void CGraphView::ClearSelection()
1045 ClearNodeSelection();
1046 ClearLinkSelection();
1047 OnSelection(CGraphViewNodePtr(), nullptr);
1048 __super::Invalidate(TRUE);
1051 //////////////////////////////////////////////////////////////////////////
1052 void CGraphView::SetScrollOffset(Gdiplus::PointF scrollOffset)
1054 m_scrollOffset = scrollOffset;
1055 OnMove(m_scrollOffset);
1058 //////////////////////////////////////////////////////////////////////////
1059 void CGraphView::AddNode(const CGraphViewNodePtr& pNode, bool select, bool scrollToFit)
1061 CRY_ASSERT(pNode);
1062 if(pNode)
1064 m_nodes.push_back(pNode);
1065 if(select)
1067 ClearNodeSelection();
1068 SelectNode(pNode);
1070 if(scrollToFit)
1072 CPaintDC paintDC(this);
1073 Gdiplus::Graphics mainGraphics(paintDC.GetSafeHdc());
1074 m_pPainter->UpdateNodePaintRect(mainGraphics, m_grid, *pNode);
1075 ScrollToFit(pNode->GetPaintRect());
1077 __super::Invalidate(TRUE);
1081 //////////////////////////////////////////////////////////////////////////
1082 void CGraphView::RemoveNode(CGraphViewNodePtr pNode)
1084 CRY_ASSERT(pNode != NULL);
1085 if(pNode != NULL)
1087 // Make sure node is not selected.
1088 ClearSelection();
1089 // Remove links connected to node.
1090 RemoveLinksConnectedToNode(pNode);
1091 // Remove node.
1092 TGraphViewNodePtrVector::iterator iEndNode = m_nodes.end();
1093 TGraphViewNodePtrVector::iterator iEraseNode = std::find(m_nodes.begin(), iEndNode, pNode);
1094 CRY_ASSERT(iEraseNode != iEndNode);
1095 if(iEraseNode != iEndNode)
1097 m_nodes.erase(iEraseNode);
1098 OnNodeRemoved(*pNode);
1100 __super::Invalidate(TRUE);
1104 //////////////////////////////////////////////////////////////////////////
1105 CGraphViewNodePtr CGraphView::FindNode(Gdiplus::PointF pos)
1107 for(TGraphViewNodePtrVector::const_iterator iNode = m_nodes.begin(), iEndNode = m_nodes.end(); iNode != iEndNode; ++ iNode)
1109 const CGraphViewNodePtr& pNode = *iNode;
1110 if(pNode->GetPaintRect().Contains(pos))
1112 return pNode;
1115 return CGraphViewNodePtr();
1118 //////////////////////////////////////////////////////////////////////////
1119 TGraphViewNodePtrVector& CGraphView::GetNodes()
1121 return m_nodes;
1124 //////////////////////////////////////////////////////////////////////////
1125 const TGraphViewNodePtrVector& CGraphView::GetNodes() const
1127 return m_nodes;
1130 //////////////////////////////////////////////////////////////////////////
1131 void CGraphView::ClearNodeSelection()
1133 for(TGraphViewNodePtrVector::iterator iSelectedNode = m_selectedNodes.begin(), iEndSelectedNode = m_selectedNodes.end(); iSelectedNode != iEndSelectedNode; ++ iSelectedNode)
1135 (*iSelectedNode)->Select(false);
1137 m_selectedNodes.clear();
1140 //////////////////////////////////////////////////////////////////////////
1141 void CGraphView::SelectNode(const CGraphViewNodePtr& pNode)
1143 if((pNode != NULL) && (pNode->IsSelected() == false))
1145 ClearLinkSelection();
1146 m_selectedNodes.push_back(pNode);
1147 pNode->Select(true);
1148 OnSelection(pNode, nullptr);
1149 __super::Invalidate(TRUE);
1153 //////////////////////////////////////////////////////////////////////////
1154 void CGraphView::SelectNodesInRect(const Gdiplus::RectF& rect)
1156 ClearSelection();
1157 for(TGraphViewNodePtrVector::iterator iNode = m_nodes.begin(), iEndNode = m_nodes.end(); iNode != iEndNode; ++ iNode)
1159 CGraphViewNodePtr pNode = *iNode;
1160 if(rect.IntersectsWith(pNode->GetPaintRect()) != 0)
1162 SelectNode(pNode);
1167 //////////////////////////////////////////////////////////////////////////
1168 const TGraphViewNodePtrVector& CGraphView::GetSelectedNodes() const
1170 return m_selectedNodes;
1173 //////////////////////////////////////////////////////////////////////////
1174 void CGraphView::MoveSelectedNodes(Gdiplus::PointF transform, bool bSnapToGrid)
1176 for(TGraphViewNodePtrVector::iterator iNode = m_selectedNodes.begin(), iEndNode = m_selectedNodes.end(); iNode != iEndNode; ++ iNode)
1178 CGraphViewNodePtr pNode = *iNode;
1179 pNode->SetPos(pNode->GetPos() + transform, m_settings.format.bSnapToGrid && bSnapToGrid);
1183 //////////////////////////////////////////////////////////////////////////
1184 bool CGraphView::CreateLink(const CGraphViewNodePtr& pSrcNode, const char* srcOutputName, const CGraphViewNodePtr& pDstNode, const char* dstInputName)
1186 CRY_ASSERT((pSrcNode != NULL) && (srcOutputName != NULL) && (pDstNode != NULL) && (dstInputName != NULL));
1187 if((pSrcNode != NULL) && (srcOutputName != NULL) && (pDstNode != NULL) && (dstInputName != NULL))
1189 if(CanCreateLink(*pSrcNode, srcOutputName, *pDstNode, dstInputName) == true)
1191 m_links.push_back(SGraphViewLink(this, pSrcNode, srcOutputName, pDstNode, dstInputName));
1192 OnLinkCreated(m_links.back());
1193 __super::Invalidate(TRUE);
1194 return true;
1197 return false;
1200 //////////////////////////////////////////////////////////////////////////
1201 void CGraphView::AddLink(const CGraphViewNodePtr& pSrcNode, const char* srcOutputName, const CGraphViewNodePtr& pDstNode, const char* dstInputName)
1203 CRY_ASSERT((pSrcNode != NULL) && (srcOutputName != NULL) && (pDstNode != NULL) && (dstInputName != NULL));
1204 if((pSrcNode != NULL) && (srcOutputName != NULL) && (pDstNode != NULL) && (dstInputName != NULL))
1206 m_links.push_back(SGraphViewLink(this, pSrcNode, srcOutputName, pDstNode, dstInputName));
1207 __super::Invalidate(TRUE);
1211 //////////////////////////////////////////////////////////////////////////
1212 void CGraphView::RemoveLink(size_t iLink)
1214 const size_t linkCount = m_links.size();
1215 CRY_ASSERT(iLink < linkCount);
1216 if(iLink < linkCount)
1218 if(m_iSelectedLink == iLink)
1220 m_iSelectedLink = INVALID_INDEX;
1222 OnLinkRemoved(m_links[iLink]);
1223 m_links.erase(m_links.begin() + iLink);
1224 __super::Invalidate(TRUE);
1228 //////////////////////////////////////////////////////////////////////////
1229 void CGraphView::RemoveLinksConnectedToNode(const CGraphViewNodePtr& pNode)
1231 if(!m_links.empty())
1233 TGraphViewLinkVector::iterator iEndLink = m_links.end();
1234 TGraphViewLinkVector::iterator iLastLink = iEndLink - 1;
1235 for(TGraphViewLinkVector::iterator iLink = m_links.begin(); iLink <= iLastLink; )
1237 SGraphViewLink& link = *iLink;
1238 if((link.pSrcNode.lock() == pNode) || (link.pDstNode.lock() == pNode))
1240 OnLinkRemoved(link);
1241 if(iLink < iLastLink)
1243 *iLink = *iLastLink;
1245 -- iLastLink;
1247 else
1249 ++ iLink;
1252 m_links.erase(iLastLink + 1, iEndLink);
1253 __super::Invalidate(TRUE);
1257 //////////////////////////////////////////////////////////////////////////
1258 void CGraphView::RemoveLinksConnectedToNodeInput(const CGraphViewNodePtr& pNode, const char* inputName)
1260 CRY_ASSERT(inputName != NULL);
1261 if((inputName != NULL) && (m_links.empty() == false))
1263 TGraphViewLinkVector::iterator iEndLink = m_links.end();
1264 TGraphViewLinkVector::iterator iLastLink = iEndLink - 1;
1265 for(TGraphViewLinkVector::iterator iLink = m_links.begin(); iLink <= iLastLink; )
1267 SGraphViewLink& link = *iLink;
1268 if((link.pDstNode.lock() == pNode) && (strcmp(link.dstInputName.c_str(), inputName) == 0))
1270 OnLinkRemoved(link);
1271 if(iLink < iLastLink)
1273 *iLink = *iLastLink;
1275 -- iLastLink;
1277 else
1279 ++ iLink;
1282 m_links.erase(iLastLink + 1, iEndLink);
1283 __super::Invalidate(TRUE);
1287 //////////////////////////////////////////////////////////////////////////
1288 void CGraphView::RemoveLinksConnectedToNodeOutput(const CGraphViewNodePtr& pNode, const char* outputName)
1290 CRY_ASSERT(outputName != NULL);
1291 if((outputName != NULL) && (m_links.empty() == false))
1293 TGraphViewLinkVector::iterator iEndLink = m_links.end();
1294 TGraphViewLinkVector::iterator iLastLink = iEndLink - 1;
1295 for(TGraphViewLinkVector::iterator iLink = m_links.begin(); iLink <= iLastLink; )
1297 SGraphViewLink& link = *iLink;
1298 if((link.pSrcNode.lock() == pNode) && (strcmp(link.srcOutputName.c_str(), outputName) == 0))
1300 OnLinkRemoved(link);
1301 if(iLink < iLastLink)
1303 *iLink = *iLastLink;
1305 -- iLastLink;
1307 else
1309 ++ iLink;
1312 m_links.erase(iLastLink + 1, iEndLink);
1313 __super::Invalidate(TRUE);
1317 //////////////////////////////////////////////////////////////////////////
1318 void CGraphView::FindLinks(const CGraphViewNodePtr& pDstNode, const char* inputName, TSizeTVector& output) const
1320 CRY_ASSERT(inputName != NULL);
1321 if(inputName != NULL)
1323 for(TGraphViewLinkVector::const_iterator iBeginLink = m_links.begin(), iEndLink = m_links.end(), iLink = iBeginLink; iLink != iEndLink; ++ iLink)
1325 const SGraphViewLink& link = *iLink;
1326 if((link.pDstNode.lock() == pDstNode) && (strcmp(link.dstInputName.c_str(), inputName) == 0))
1328 output.push_back(iLink - iBeginLink);
1334 //////////////////////////////////////////////////////////////////////////
1335 size_t CGraphView::FindLink(const CGraphViewNodePtr& pSrcNode, const char* srcOutputName, const CGraphViewNodePtr& pDstNode, const char* dstInputName)
1337 for(TGraphViewLinkVector::iterator iBeginLink = m_links.begin(), iEndLink = m_links.end(), iLink = iBeginLink; iLink != iEndLink; ++ iLink)
1339 SGraphViewLink& link = *iLink;
1340 if((link.pSrcNode.lock() == pSrcNode) && (strcmp(link.srcOutputName, srcOutputName) == 0) && (link.pDstNode.lock() == pDstNode) && (strcmp(link.dstInputName, dstInputName) == 0))
1342 return iLink - iBeginLink;
1345 return INVALID_INDEX;
1348 //////////////////////////////////////////////////////////////////////////
1349 size_t CGraphView::FindLink(Gdiplus::PointF point)
1351 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1352 // Part of temporary fix for slow graphs with lots of links, until we can optimize the intersection tests properly.
1353 if((GetAsyncKeyState(VK_SHIFT) & 0x8000) == 0)
1355 return INVALID_INDEX;
1357 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1358 for(TGraphViewLinkVector::iterator iBeginLink = m_links.begin(), iEndLink = m_links.end(), iLink = iBeginLink; iLink != iEndLink; ++ iLink)
1360 SGraphViewLink& link = *iLink;
1361 if(link.paintRect.Contains(point))
1363 const CGraphViewNodePtr pSrcNode = link.pSrcNode.lock();
1364 const CGraphViewNodePtr pDstNode = link.pDstNode.lock();
1365 CRY_ASSERT(pSrcNode && pDstNode);
1366 if(pSrcNode && pDstNode)
1368 const size_t iSrcOutput = pSrcNode->FindOutput(link.srcOutputName);
1369 const size_t iDstInput = pDstNode->FindInput(link.dstInputName);
1370 CRY_ASSERT((iSrcOutput != INVALID_INDEX) && (iDstInput != INVALID_INDEX));
1371 if((iSrcOutput != INVALID_INDEX) && (iDstInput != INVALID_INDEX))
1373 // N.B. This hit test is far from optimal and it may be better to implement something along the lines of what is proposed here: https://msdn.microsoft.com/en-us/library/ms969920.aspx.
1374 Gdiplus::Bitmap bitmap(static_cast<INT>(link.paintRect.Width), static_cast<INT>(link.paintRect.Height), PixelFormat4bppIndexed);
1375 if(Gdiplus::Graphics* pGraphics = Gdiplus::Graphics::FromImage(&bitmap))
1377 const Gdiplus::Color backgroundColor(255, 0, 0, 0);
1378 pGraphics->Clear(backgroundColor);
1380 const Gdiplus::PointF topLeft(link.paintRect.X, link.paintRect.Y);
1381 Gdiplus::PointF startPoint = pSrcNode->GetOutputLinkPoint(iSrcOutput);
1382 Gdiplus::PointF endPoint = pDstNode->GetInputLinkPoint(iDstInput);
1383 Gdiplus::PointF testPoint = point;
1384 startPoint.X -= topLeft.X;
1385 startPoint.Y -= topLeft.Y;
1386 endPoint.X -= topLeft.X;
1387 endPoint.Y -= topLeft.Y;
1388 testPoint.X -= topLeft.X;
1389 testPoint.Y -= topLeft.Y;
1391 m_pPainter->PaintLink(*pGraphics, startPoint, endPoint, pSrcNode->GetOutputColor(iSrcOutput), link.selected, 1.2f);
1393 Gdiplus::Color testColor = backgroundColor;
1394 const Gdiplus::Status testStatus = bitmap.GetPixel(static_cast<INT>(testPoint.X), static_cast<INT>(testPoint.Y), &testColor);
1396 delete pGraphics;
1398 if((testStatus == Gdiplus::Ok) && (testColor.GetValue() != backgroundColor.GetValue()))
1400 return iLink - iBeginLink;
1407 return INVALID_INDEX;
1410 //////////////////////////////////////////////////////////////////////////
1411 void CGraphView::ClearLinkSelection()
1413 if(m_iSelectedLink != INVALID_INDEX)
1415 m_links[m_iSelectedLink].selected = false;
1416 m_iSelectedLink = INVALID_INDEX;
1420 //////////////////////////////////////////////////////////////////////////
1421 void CGraphView::SelectLink(size_t iLink)
1423 CRY_ASSERT(iLink != INVALID_INDEX);
1424 if((iLink != INVALID_INDEX) && (iLink != m_iSelectedLink))
1426 ClearSelection();
1427 m_iSelectedLink = iLink;
1428 m_links[m_iSelectedLink].selected = true;
1429 OnSelection(CGraphViewNodePtr(), &m_links[m_iSelectedLink]);
1430 __super::Invalidate(TRUE);
1434 //////////////////////////////////////////////////////////////////////////
1435 size_t CGraphView::GetSelectedLink() const
1437 return m_iSelectedLink;
1440 //////////////////////////////////////////////////////////////////////////
1441 void CGraphView::Clear()
1443 m_nodes.clear();
1444 m_links.clear();
1447 //////////////////////////////////////////////////////////////////////////
1448 Gdiplus::PointF CGraphView::ClientToGraph(LONG x, LONG y) const
1450 return Gdiplus::PointF((x + m_scrollOffset.X) / m_zoom, (y + m_scrollOffset.Y) / m_zoom);
1452 //////////////////////////////////////////////////////////////////////////
1453 Gdiplus::PointF CGraphView::ClientToGraph(Gdiplus::PointF pos) const
1455 return Gdiplus::PointF((pos.X + m_scrollOffset.X) / m_zoom, (pos.Y + m_scrollOffset.Y) / m_zoom);
1458 //////////////////////////////////////////////////////////////////////////
1459 Gdiplus::PointF CGraphView::ClientToGraphTransform(Gdiplus::PointF transform) const
1461 return Gdiplus::PointF(transform.X / m_zoom, transform.Y / m_zoom);
1464 //////////////////////////////////////////////////////////////////////////
1465 Gdiplus::RectF CGraphView::ClientToGraph(Gdiplus::RectF rect) const
1467 return Gdiplus::RectF((rect.X + m_scrollOffset.X) / m_zoom, (rect.Y + m_scrollOffset.Y) / m_zoom, rect.Width / m_zoom, rect.Height / m_zoom);
1470 //////////////////////////////////////////////////////////////////////////
1471 Gdiplus::PointF CGraphView::GraphToClient(Gdiplus::PointF pos) const
1473 return Gdiplus::PointF((pos.X * m_zoom) - m_scrollOffset.X, (pos.Y * m_zoom) - m_scrollOffset.Y);
1476 //////////////////////////////////////////////////////////////////////////
1477 Gdiplus::PointF CGraphView::GraphToClientTransform(Gdiplus::PointF transform) const
1479 return Gdiplus::PointF(transform.X * m_zoom, transform.Y * m_zoom);
1482 //////////////////////////////////////////////////////////////////////////
1483 Gdiplus::RectF CGraphView::GraphToClient(Gdiplus::RectF rect) const
1485 return Gdiplus::RectF((rect.X * m_zoom) - m_scrollOffset.X, (rect.Y * m_zoom) - m_scrollOffset.Y, rect.Width * m_zoom, rect.Height * m_zoom);
1488 //////////////////////////////////////////////////////////////////////////
1489 void CGraphView::ScrollToFit(Gdiplus::RectF rect)
1491 CRect clientRect;
1492 GetClientRect(&clientRect);
1493 Gdiplus::RectF graphViewRect = ClientToGraph(Gdiplus::RectF(static_cast<Gdiplus::REAL>(clientRect.left), static_cast<Gdiplus::REAL>(clientRect.top),
1494 static_cast<Gdiplus::REAL>(clientRect.Width()), static_cast<Gdiplus::REAL>(clientRect.Height())));
1495 const float dx = graphViewRect.GetRight() - rect.GetRight();
1496 if(dx < 0.0f)
1498 m_scrollOffset.X += (-dx + DEFAULT_NODE_SPACING) * m_zoom;
1499 OnMove(m_scrollOffset);
1501 // #SchematycTODO : Check left, top and bottom of view too?
1504 //////////////////////////////////////////////////////////////////////////
1505 void CGraphView::CenterPosition(Gdiplus::PointF point)
1507 CRect clientRect;
1508 GetClientRect(&clientRect);
1509 Gdiplus::RectF graphViewRect = ClientToGraph(Gdiplus::RectF(static_cast<Gdiplus::REAL>(clientRect.left), static_cast<Gdiplus::REAL>(clientRect.top),
1510 static_cast<Gdiplus::REAL>(clientRect.Width()), static_cast<Gdiplus::REAL>(clientRect.Height())));
1511 const float dx = graphViewRect.X - point.X + (graphViewRect.Width/2);
1512 const float dy = graphViewRect.Y - point.Y + (graphViewRect.Height/2);
1514 m_scrollOffset.X += (-dx + DEFAULT_NODE_SPACING) * m_zoom;
1515 m_scrollOffset.Y += (-dy + DEFAULT_NODE_SPACING) * m_zoom;
1517 OnMove(m_scrollOffset);
1520 //////////////////////////////////////////////////////////////////////////
1521 BOOL CGraphView::PreTranslateMessage(MSG* pMsg)
1523 if((pMsg->message == WM_KEYDOWN) && (GetFocus() == this))
1525 OnKeyDown((UINT)pMsg->wParam, LOWORD(pMsg->lParam), HIWORD(pMsg->lParam));
1526 return TRUE;
1528 else
1530 return __super::PreTranslateMessage(pMsg);
1534 //////////////////////////////////////////////////////////////////////////
1535 void CGraphView::OnDraw(CDC* pDC) {}
1537 //////////////////////////////////////////////////////////////////////////
1538 void CGraphView::OnKillFocus(CWnd* pNewWnd)
1540 ExitDragState();
1543 //////////////////////////////////////////////////////////////////////////
1544 void CGraphView::OnPaint()
1546 if(m_pPainter)
1548 CPaintDC paintDC(this);
1549 const CRect paintRect = paintDC.m_ps.rcPaint;
1550 Gdiplus::Bitmap backBuffer(paintRect.Width(), paintRect.Height());
1551 if(Gdiplus::Graphics* pBackBufferGraphics = Gdiplus::Graphics::FromImage(&backBuffer))
1553 pBackBufferGraphics->TranslateTransform(-m_scrollOffset.X, -m_scrollOffset.Y);
1554 pBackBufferGraphics->ScaleTransform(m_zoom, m_zoom);
1555 pBackBufferGraphics->SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
1556 pBackBufferGraphics->SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAliasGridFit);
1557 pBackBufferGraphics->Clear(m_pPainter->GetBackgroundColor());
1559 if(m_enabled)
1561 m_pPainter->PaintGrid(*pBackBufferGraphics, m_grid);
1563 for(TGraphViewNodePtrVector::iterator iNode = m_nodes.begin(), iEndNode = m_nodes.end(); iNode != iEndNode; ++ iNode)
1565 m_pPainter->UpdateNodePaintRect(*pBackBufferGraphics, m_grid, *(*iNode));
1568 for(TGraphViewLinkVector::iterator iLink = m_links.begin(), iEndLink = m_links.end(); iLink != iEndLink; ++ iLink)
1570 SGraphViewLink& link = *iLink;
1571 const CGraphViewNodePtr pSrcNode = link.pSrcNode.lock();
1572 const CGraphViewNodePtr pDstNode = link.pDstNode.lock();
1573 if(pSrcNode && pDstNode)
1575 const size_t srcOutputIdx = pSrcNode->FindOutput(link.srcOutputName);
1576 const size_t dstInputIdx = pDstNode->FindInput(link.dstInputName);
1577 if((srcOutputIdx != INVALID_INDEX) && (dstInputIdx != INVALID_INDEX))
1579 link.paintRect = m_pPainter->PaintLink(*pBackBufferGraphics, pSrcNode->GetOutputLinkPoint(srcOutputIdx), pDstNode->GetInputLinkPoint(dstInputIdx), pSrcNode->GetOutputColor(srcOutputIdx), link.selected, 1.0f);
1584 if(m_pDragState != NULL)
1586 m_pDragState->Paint(*this, *pBackBufferGraphics, *m_pPainter);
1589 for(TGraphViewNodePtrVector::reverse_iterator iNode = m_nodes.rbegin(), iEndNode = m_nodes.rend(); iNode != iEndNode; ++ iNode)
1591 m_pPainter->PaintNode(*pBackBufferGraphics, m_grid, *(*iNode));
1595 Gdiplus::Graphics mainGraphics(paintDC.GetSafeHdc());
1596 mainGraphics.DrawImage(&backBuffer, paintRect.left, paintRect.top, paintRect.Width(), paintRect.Height());
1598 delete pBackBufferGraphics;
1603 //////////////////////////////////////////////////////////////////////////
1604 void CGraphView::OnMouseMove(UINT nFlags, CPoint point)
1606 if(m_enabled && (GetFocus() == this))
1608 if(m_pDragState)
1610 m_pDragState->Update(*this, Gdiplus::PointF(static_cast<float>(point.x), static_cast<float>(point.y)));
1611 __super::Invalidate(TRUE);
1613 else if((point.x != m_prevMousePoint.x) || (point.y != m_prevMousePoint.y))
1615 if((nFlags & MK_LBUTTON) != 0)
1617 OnLeftMouseButtonDrag(m_prevMousePoint);
1619 else if((nFlags & MK_RBUTTON) != 0)
1621 OnRightMouseButtonDrag(m_prevMousePoint);
1623 else if((nFlags & MK_MBUTTON) != 0)
1625 OnMiddleMouseButtonDrag(m_prevMousePoint);
1629 m_prevMousePoint = point;
1630 __super::OnMouseMove(nFlags, point);
1633 //////////////////////////////////////////////////////////////////////////
1634 BOOL CGraphView::OnMouseWheel(UINT nFlags, short zDelta, CPoint point)
1636 // Store previous focus.
1637 CPoint cursorPoint;
1638 GetCursorPos(&cursorPoint);
1639 ScreenToClient(&cursorPoint);
1640 const Gdiplus::PointF prevClientFocus((Gdiplus::REAL)cursorPoint.x, (Gdiplus::REAL)cursorPoint.y);
1641 const Gdiplus::PointF prevGraphFocus = ClientToGraph(prevClientFocus);
1642 // Increment zoom.
1643 m_zoom = clamp_tpl(m_zoom + (DELTA_ZOOM * zDelta), MIN_ZOOM, MAX_ZOOM);
1644 // Scroll to previous focus point.
1645 const Gdiplus::PointF newClientFocus = GraphToClient(prevGraphFocus);
1646 m_scrollOffset.X += newClientFocus.X - prevClientFocus.X;
1647 m_scrollOffset.Y += newClientFocus.Y - prevClientFocus.Y;
1648 OnMove(m_scrollOffset);
1649 // Force re-draw.
1650 __super::Invalidate(TRUE);
1651 return TRUE;
1654 //////////////////////////////////////////////////////////////////////////
1655 void CGraphView::OnLButtonDown(UINT nFlags, CPoint point)
1657 SetFocus();
1660 //////////////////////////////////////////////////////////////////////////
1661 void CGraphView::OnLButtonUp(UINT nFlags, CPoint point)
1663 if(m_pDragState != NULL)
1665 ExitDragState();
1667 else
1669 ClearSelection();
1670 const Gdiplus::PointF graphPos = ClientToGraph(point.x, point.y);
1671 CGraphViewNodePtr pNode = FindNode(graphPos);
1672 if(pNode != NULL)
1674 SelectNode(pNode);
1676 else
1678 const size_t iLink = FindLink(graphPos);
1679 if(iLink != INVALID_INDEX)
1681 SelectLink(iLink);
1687 //////////////////////////////////////////////////////////////////////////
1688 afx_msg void CGraphView::OnRButtonDown(UINT nFlags, CPoint point)
1690 SetFocus();
1693 //////////////////////////////////////////////////////////////////////////
1694 afx_msg void CGraphView::OnRButtonUp(UINT nFlags, CPoint point)
1696 if(m_pDragState != NULL)
1698 ExitDragState();
1700 else
1702 CMenu popupMenu;
1703 popupMenu.CreatePopupMenu();
1704 const Gdiplus::PointF graphPos = ClientToGraph(point.x, point.y);
1705 CGraphViewNodePtr pNode = FindNode(graphPos);
1706 const size_t iNodeInput = pNode ? pNode->FindInput(graphPos) : INVALID_INDEX;
1707 const size_t iNodeOutput = pNode ? pNode->FindOutput(graphPos) : INVALID_INDEX;
1708 ClientToScreen(&point);
1709 GetPopupMenuItems(popupMenu, pNode, iNodeInput, iNodeOutput, point);
1710 const int popupMenuItemCount = popupMenu.GetMenuItemCount();
1711 if(popupMenuItemCount == 1)
1713 OnPopupMenuResult(popupMenu.GetMenuItemID(0), pNode, iNodeInput, iNodeOutput, point);
1715 else if(popupMenuItemCount > 0)
1717 int popupMenuItem = popupMenu.TrackPopupMenuEx(TPM_RETURNCMD, point.x, point.y, this, NULL);
1718 OnPopupMenuResult(popupMenuItem, pNode, iNodeInput, iNodeOutput, point);
1723 //////////////////////////////////////////////////////////////////////////
1724 afx_msg void CGraphView::OnMButtonDown(UINT nFlags, CPoint point)
1726 SetFocus();
1729 //////////////////////////////////////////////////////////////////////////
1730 afx_msg void CGraphView::OnMButtonUp(UINT nFlags, CPoint point)
1732 if(m_pDragState != NULL)
1734 ExitDragState();
1738 //////////////////////////////////////////////////////////////////////////
1739 void CGraphView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
1741 if((GetFocus() == this) && m_enabled && (nRepCnt == 1))
1743 const bool bCtrlDown = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
1744 const bool bShiftDown = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
1745 if(bCtrlDown)
1747 switch(nChar)
1749 case 'F':
1751 FormatSelectedNodes();
1752 return;
1756 else if (bShiftDown)
1758 switch (nChar)
1760 case 'W':
1762 OnUpArrowPressed();
1763 return;
1765 case 'S':
1767 OnDownArrowPressed();
1768 return;
1772 else
1774 switch(nChar)
1776 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1777 // Part of temporary fix for slow graphs with lots of links, until we can optimize the intersection tests properly.
1778 case VK_SHIFT:
1780 UpdateCursor();
1781 return;
1783 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1784 case VK_DELETE:
1786 OnDelete();
1787 return;
1789 case VK_UP:
1791 OnUpArrowPressed();
1792 return;
1794 case VK_DOWN:
1796 OnDownArrowPressed();
1797 return;
1800 if((nChar >= 'A') && (nChar <= 'Z'))
1802 const char defaultFilter[2] = { static_cast<char>(nChar), '\0' };
1803 if(m_selectedNodes.size() == 1)
1805 CGraphViewNodePtr pSelectedNode = m_selectedNodes[0];
1806 Gdiplus::RectF paintRect = pSelectedNode->GetPaintRect();
1807 Gdiplus::PointF nextPos = GraphToClient(Gdiplus::PointF(paintRect.GetRight() + DEFAULT_NODE_SPACING, paintRect.GetTop()));
1808 CPoint screenPoint(static_cast<int>(nextPos.X), static_cast<int>(nextPos.Y));
1809 ClientToScreen(&screenPoint);
1810 DoQuickSearch(defaultFilter, pSelectedNode, 0, &screenPoint); // Rather than passing zero it would be nice to pass the index of the first/selected execution output.
1812 else
1814 DoQuickSearch(defaultFilter);
1816 return;
1820 __super::OnKeyDown(nChar, nRepCnt, nFlags);
1823 //////////////////////////////////////////////////////////////////////////
1824 void CGraphView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
1826 if((GetFocus() == this) && m_enabled && (nRepCnt == 1))
1828 switch(nChar)
1830 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1831 // Part of temporary fix for slow graphs with lots of links, until we can optimize the intersection tests properly.
1832 case VK_SHIFT:
1834 SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
1835 return;
1837 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1840 __super::OnKeyUp(nChar, nRepCnt, nFlags);
1843 //////////////////////////////////////////////////////////////////////////
1844 BOOL CGraphView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
1846 if(UpdateCursor())
1848 return TRUE;
1850 return __super::OnSetCursor(pWnd, nHitTest, message);
1853 //////////////////////////////////////////////////////////////////////////
1854 void CGraphView::OnMove(Gdiplus::PointF scrollOffset) {}
1856 //////////////////////////////////////////////////////////////////////////
1857 void CGraphView::OnSelection(const CGraphViewNodePtr& pSelectedNode, SGraphViewLink* pSelectedLink) {}
1859 //////////////////////////////////////////////////////////////////////////
1860 void CGraphView::OnLinkModify(const SGraphViewLink& link) {}
1862 //////////////////////////////////////////////////////////////////////////
1863 void CGraphView::OnNodeRemoved(const CGraphViewNode& node) {}
1865 //////////////////////////////////////////////////////////////////////////
1866 bool CGraphView::CanCreateLink(const CGraphViewNode& srcNode, const char* srcOutputName, const CGraphViewNode& dstNode, const char* dstInputName) const
1868 return true;
1871 //////////////////////////////////////////////////////////////////////////
1872 void CGraphView::OnLinkCreated(const SGraphViewLink& link) {}
1874 //////////////////////////////////////////////////////////////////////////
1875 void CGraphView::OnLinkRemoved(const SGraphViewLink& link) {}
1877 //////////////////////////////////////////////////////////////////////////
1878 DROPEFFECT CGraphView::OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
1880 return DROPEFFECT_NONE;
1883 //////////////////////////////////////////////////////////////////////////
1884 BOOL CGraphView::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
1886 return false;
1889 //////////////////////////////////////////////////////////////////////////
1890 void CGraphView::GetPopupMenuItems(CMenu& popupMenu, const CGraphViewNodePtr& pNode, size_t iNodeInput, size_t iNodeOutput, CPoint point)
1892 if(pNode)
1894 popupMenu.AppendMenu(MF_STRING, EPopupMenuItem::REMOVE_NODE, "Remove Node");
1896 else
1898 popupMenu.AppendMenu(MF_STRING, EPopupMenuItem::QUICK_SEARCH, "Quick Search");
1902 //////////////////////////////////////////////////////////////////////////
1903 void CGraphView::OnPopupMenuResult(BOOL popupMenuItem, const CGraphViewNodePtr& pNode, size_t iNodeInput, size_t iNodeOutput, CPoint point)
1905 switch(popupMenuItem)
1907 case EPopupMenuItem::QUICK_SEARCH:
1909 DoQuickSearch();
1910 break;
1912 case EPopupMenuItem::REMOVE_NODE:
1914 CRY_ASSERT(pNode != NULL);
1915 RemoveNode(pNode);
1916 break;
1921 //////////////////////////////////////////////////////////////////////////
1922 const IQuickSearchOptions* CGraphView::GetQuickSearchOptions(CPoint point, const CGraphViewNodePtr& pNode, size_t iNodeOutput)
1924 return NULL;
1927 //////////////////////////////////////////////////////////////////////////
1928 void CGraphView::OnQuickSearchResult(CPoint point, const CGraphViewNodePtr& pNode, size_t iNodeOutput, size_t iSelectedOption) {}
1930 //////////////////////////////////////////////////////////////////////////
1931 bool CGraphView::ShouldUpdate() const
1933 return GetFocus() == this;
1936 //////////////////////////////////////////////////////////////////////////
1937 void CGraphView::OnUpdate() {}
1939 //////////////////////////////////////////////////////////////////////////
1940 CGraphView::CScrollDragState::CScrollDragState(Gdiplus::PointF pos)
1941 : m_prevPos(pos)
1944 //////////////////////////////////////////////////////////////////////////
1945 void CGraphView::CScrollDragState::Update(CGraphView& graphView, Gdiplus::PointF pos)
1947 if((pos.X != m_prevPos.X) || (pos.Y != m_prevPos.Y))
1949 graphView.Scroll(pos - m_prevPos);
1950 m_prevPos = pos;
1954 //////////////////////////////////////////////////////////////////////////
1955 void CGraphView::CScrollDragState::Paint(CGraphView& graphView, Gdiplus::Graphics& graphics, IGraphViewPainter& painter) {}
1957 //////////////////////////////////////////////////////////////////////////
1958 void CGraphView::CScrollDragState::Exit(CGraphView& graphView) {}
1960 //////////////////////////////////////////////////////////////////////////
1961 CGraphView::CMoveDragState::CMoveDragState(Gdiplus::PointF pos)
1962 : m_prevPos(pos)
1965 //////////////////////////////////////////////////////////////////////////
1966 void CGraphView::CMoveDragState::Update(CGraphView& graphView, Gdiplus::PointF pos)
1968 graphView.MoveSelectedNodes(graphView.ClientToGraphTransform(pos - m_prevPos), false);
1969 m_prevPos = pos;
1972 //////////////////////////////////////////////////////////////////////////
1973 void CGraphView::CMoveDragState::Paint(CGraphView& graphView, Gdiplus::Graphics& graphics, IGraphViewPainter& painter) {}
1975 //////////////////////////////////////////////////////////////////////////
1976 void CGraphView::CMoveDragState::Exit(CGraphView& graphView)
1978 graphView.MoveSelectedNodes(Gdiplus::PointF(), true);
1981 //////////////////////////////////////////////////////////////////////////
1982 CGraphView::CLinkDragState::CLinkDragState(const CGraphViewNodeWeakPtr& pSrcNode, const char* srcOutputName, Gdiplus::PointF start, Gdiplus::PointF end, Gdiplus::Color color, bool newLink)
1983 : m_pSrcNode(pSrcNode)
1984 , m_srcOutputName(srcOutputName)
1985 , m_start(start)
1986 , m_end(end)
1987 , m_color(color)
1988 , m_newLink(newLink)
1991 //////////////////////////////////////////////////////////////////////////
1992 void CGraphView::CLinkDragState::Update(CGraphView& graphView, Gdiplus::PointF pos)
1994 m_end = pos;
1997 //////////////////////////////////////////////////////////////////////////
1998 void CGraphView::CLinkDragState::Paint(CGraphView& graphView, Gdiplus::Graphics& graphics, IGraphViewPainter& painter)
2000 if(CGraphViewNodePtr pSrcNode = m_pSrcNode.lock())
2002 painter.PaintLink(graphics, m_start, graphView.ClientToGraph(m_end), m_color, true, 1.0f);
2006 //////////////////////////////////////////////////////////////////////////
2007 void CGraphView::CLinkDragState::Exit(CGraphView& graphView)
2009 const Gdiplus::PointF graphPos = graphView.ClientToGraph(m_end);
2010 if(CGraphViewNodePtr pDstNode = graphView.FindNode(graphPos))
2012 const size_t iDstInput = pDstNode->FindInput(graphPos);
2013 if(iDstInput != INVALID_INDEX)
2015 graphView.CreateLink(m_pSrcNode.lock(), m_srcOutputName.c_str(), pDstNode, pDstNode->GetInputName(iDstInput));
2018 else if(m_newLink)
2020 CGraphViewNodePtr pSrcNode = m_pSrcNode.lock();
2021 CRY_ASSERT(pSrcNode);
2022 if(pSrcNode)
2024 graphView.DoQuickSearch(NULL, pSrcNode, pSrcNode->FindOutput(m_srcOutputName.c_str()));
2029 //////////////////////////////////////////////////////////////////////////
2030 CGraphView::CSelectDragState::CSelectDragState(Gdiplus::PointF pos)
2031 : m_startPos(pos)
2032 , m_endPos(pos)
2035 //////////////////////////////////////////////////////////////////////////
2036 void CGraphView::CSelectDragState::Update(CGraphView& graphView, Gdiplus::PointF pos)
2038 m_endPos = pos;
2039 graphView.SelectNodesInRect(graphView.ClientToGraph(GetSelectionRect()));
2042 //////////////////////////////////////////////////////////////////////////
2043 void CGraphView::CSelectDragState::Paint(CGraphView& graphView, Gdiplus::Graphics& graphics, IGraphViewPainter& painter)
2045 painter.PaintSelectionRect(graphics, graphView.ClientToGraph(GetSelectionRect()));
2048 //////////////////////////////////////////////////////////////////////////
2049 void CGraphView::CSelectDragState::Exit(CGraphView& graphView) {}
2051 //////////////////////////////////////////////////////////////////////////
2052 Gdiplus::RectF CGraphView::CSelectDragState::GetSelectionRect() const
2054 Gdiplus::RectF selectionRect;
2055 selectionRect.X = std::min(m_startPos.X, m_endPos.X);
2056 selectionRect.Y = std::min(m_startPos.Y, m_endPos.Y);
2057 selectionRect.Width = fabs_tpl(m_endPos.X - m_startPos.X);
2058 selectionRect.Height = fabs_tpl(m_endPos.Y - m_startPos.Y);
2059 return selectionRect;
2062 //////////////////////////////////////////////////////////////////////////
2063 CGraphView::CDropTarget::CDropTarget(CGraphView& graphView)
2064 : m_graphView(graphView)
2067 //////////////////////////////////////////////////////////////////////////
2068 DROPEFFECT CGraphView::CDropTarget::OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
2070 return m_graphView.OnDragOver(pWnd, pDataObject, dwKeyState, point);
2073 //////////////////////////////////////////////////////////////////////////
2074 BOOL CGraphView::CDropTarget::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
2076 return m_graphView.OnDrop(pWnd, pDataObject, dropEffect, point);
2079 //////////////////////////////////////////////////////////////////////////
2080 void CGraphView::OnTimer(UINT_PTR nIDEvent)
2082 if(nIDEvent == UPDATE_TIMER_EVENT_ID)
2084 if(ShouldUpdate())
2086 OnUpdate();
2087 //if(!m_selectedNodes.empty() || (m_iSelectedLink != INVALID_INDEX)) // Removed as part of temporary fix for slow graphs with lots of links, until we can optimize the intersection tests properly.
2089 __super::Invalidate(TRUE);
2095 //////////////////////////////////////////////////////////////////////////
2096 void CGraphView::OnLeftMouseButtonDrag(CPoint point)
2098 const Gdiplus::PointF clientPos(static_cast<float>(point.x), static_cast<float>(point.y));
2099 const Gdiplus::PointF graphPos = ClientToGraph(clientPos);
2100 if(CGraphViewNodePtr pNode = FindNode(graphPos))
2102 if(m_selectedNodes.size() < 2)
2104 ClearSelection();
2105 SelectNode(pNode);
2107 const size_t iSrcOutput = pNode->FindOutput(graphPos);
2108 if(iSrcOutput != INVALID_INDEX)
2110 m_pDragState = IGraphViewDragStatePtr(new CLinkDragState(pNode, pNode->GetOutputName(iSrcOutput), pNode->GetOutputLinkPoint(iSrcOutput), clientPos, pNode->GetOutputColor(iSrcOutput), true));
2112 else
2114 const size_t iDstInput = pNode->FindInput(graphPos);
2115 if(iDstInput != INVALID_INDEX)
2117 const char* dstInputName = pNode->GetInputName(iDstInput);
2118 TSizeTVector links;
2119 FindLinks(pNode, dstInputName, links);
2120 if(links.size() == 1)
2122 const size_t iLink = links.front();
2123 const SGraphViewLink& link = m_links[iLink];
2124 if(CGraphViewNodePtr pSrcNode = link.pSrcNode.lock())
2126 const size_t iLinkSrcOutput = pSrcNode->FindOutput(link.srcOutputName);
2127 CRY_ASSERT(iLinkSrcOutput != INVALID_INDEX);
2128 if(iLinkSrcOutput != INVALID_INDEX)
2130 m_pDragState = IGraphViewDragStatePtr(new CLinkDragState(link.pSrcNode, link.srcOutputName, pSrcNode->GetOutputLinkPoint(iLinkSrcOutput), clientPos, pSrcNode->GetOutputColor(iLinkSrcOutput), false));
2133 RemoveLink(iLink);
2136 else
2138 const Gdiplus::RectF nodePaintRect = pNode->GetPaintRect();
2139 m_pDragState = IGraphViewDragStatePtr(new CMoveDragState(clientPos));
2143 else
2145 m_pDragState = IGraphViewDragStatePtr(new CSelectDragState(clientPos));
2149 //////////////////////////////////////////////////////////////////////////
2150 void CGraphView::OnRightMouseButtonDrag(CPoint point)
2152 m_pDragState = IGraphViewDragStatePtr(new CScrollDragState(Gdiplus::PointF(static_cast<float>(point.x), static_cast<float>(point.y))));
2155 //////////////////////////////////////////////////////////////////////////
2156 void CGraphView::OnMiddleMouseButtonDrag(CPoint point)
2158 m_pDragState = IGraphViewDragStatePtr(new CScrollDragState(Gdiplus::PointF(static_cast<float>(point.x), static_cast<float>(point.y))));
2161 //////////////////////////////////////////////////////////////////////////
2162 void CGraphView::Scroll(Gdiplus::PointF delta)
2164 m_scrollOffset.X = clamp_tpl(m_scrollOffset.X - delta.X, m_grid.bounds.X, m_grid.bounds.GetRight());
2165 m_scrollOffset.Y = clamp_tpl(m_scrollOffset.Y - delta.Y, m_grid.bounds.Y, m_grid.bounds.GetBottom());
2166 OnMove(m_scrollOffset);
2169 //////////////////////////////////////////////////////////////////////////
2170 void CGraphView::ExitDragState()
2172 if(m_pDragState != NULL)
2174 IGraphViewDragStatePtr pDragState = m_pDragState;
2175 m_pDragState.reset();
2176 pDragState->Exit(*this);
2177 __super::Invalidate(TRUE);
2181 //////////////////////////////////////////////////////////////////////////
2182 void CGraphView::OnDelete()
2184 if(m_selectedNodes.empty() == false)
2186 // TODO: Switch to CryMessageBox when porting to Qt, we cannot use it right now due to a MFC / Qt conflict.
2187 if (::MessageBoxA(CView::GetSafeHwnd(), "Remove selected node(s)?", "Remove Node(s)", 1) == IDOK)
2189 TGraphViewNodePtrVector removeNodes = m_selectedNodes;
2190 for(TGraphViewNodePtrVector::iterator iRemoveNode = removeNodes.begin(), iEndRemoveNode = removeNodes.end(); iRemoveNode != iEndRemoveNode; ++ iRemoveNode)
2192 RemoveNode(*iRemoveNode);
2196 else if(m_iSelectedLink)
2198 // TODO: Switch to CryMessageBox when porting to Qt, we cannot use it right now due to a MFC / Qt conflict.
2199 if (::MessageBoxA(CView::GetSafeHwnd(), "Remove selected link?", "Remove Link", 1) == IDOK)
2201 RemoveLink(m_iSelectedLink);
2206 //////////////////////////////////////////////////////////////////////////
2207 bool CGraphView::UpdateCursor()
2209 if(!m_pDragState)
2211 CPoint cursorPoint;
2212 GetCursorPos(&cursorPoint);
2213 ScreenToClient(&cursorPoint);
2214 const Gdiplus::PointF graphPos = ClientToGraph(Gdiplus::PointF(static_cast<float>(cursorPoint.x), static_cast<float>(cursorPoint.y)));
2215 if(FindNode(graphPos) || (FindLink(graphPos) != INVALID_INDEX))
2217 SetCursor(AfxGetApp()->LoadStandardCursor(IDC_HAND));
2218 return true;
2221 return false;
2224 //////////////////////////////////////////////////////////////////////////
2225 void CGraphView::FormatSelectedNodes()
2227 if(GetSchematyc()->IsExperimentalFeatureEnabled("GraphNodeFormat"))
2229 CGraphViewFormatter formatter(*this, m_settings.format);
2230 formatter.FormatNodes(m_selectedNodes);