#region License Information
/* HeuristicLab
* Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using HeuristicLab.Common;
using HeuristicLab.Core.Networks;
using HeuristicLab.Visualization;
namespace HeuristicLab.Networks.Views.NetworkVisualization {
[Primitive(typeof(INode), true)]
public class NodeRectangle : Visualization.Rectangle, INetworkItemPrimitive {
protected readonly Dictionary port2primitive = new Dictionary();
protected readonly IGroup portRectangles;
protected INodeVisualProperties VisualProperties {
get { return (INodeVisualProperties)networkItem.VisualProperties; }
set {
if (networkItem.VisualProperties == value) return;
networkItem.VisualProperties = value;
}
}
protected bool IgnoreVisualPropertiesChanges { get; set; }
protected INode networkItem;
public INode NetworkItem {
get { return networkItem; }
set {
if (networkItem == value) return;
if (networkItem != null) DeregisterNetworkItemEvents();
networkItem = value;
if (networkItem != null) RegisterNetworkItemEvents();
LoadVisualProperties();
OnNetworkItemChanged();
}
}
public override Brush Brush {
get { return base.Brush; }
set {
if (base.Brush == value) return;
base.Brush = value;
SaveVisualProperties();
}
}
public override Pen Pen {
get { return base.Pen; }
set {
if (base.Pen == value) return;
base.Pen = value;
SaveVisualProperties();
}
}
public NodeRectangle(IChart chart, INode node)
: base(chart, PointD.Empty, PointD.Empty) {
bool adjustSize = node.VisualProperties == null;
NetworkItem = node;
portRectangles = new Group(chart);
foreach (var port in node.Ports) {
var portPrimitive = PrimitiveAttribute.CreateDefaultPrimitive(port.GetType(), chart, port, node); // TODO: port.Parent != node
port2primitive.Add(port, portPrimitive);
portRectangles.Add(portPrimitive);
}
portRectangles.RedrawRequired += (sender, args) => OnRedrawRequired();
if (adjustSize) AdjustSize();
}
protected virtual void RegisterNetworkItemEvents() {
if (VisualProperties != null) {
VisualProperties.Changed += VisualProperties_Changed;
}
networkItem.NameChanged += NetworkItem_NameChanged;
if (networkItem.Ports != null) {
networkItem.Ports.ItemsAdded += Ports_ItemsAdded;
networkItem.Ports.ItemsRemoved += Ports_ItemsRemoved;
}
}
protected virtual void DeregisterNetworkItemEvents() {
if (VisualProperties != null) {
VisualProperties.Changed -= VisualProperties_Changed;
}
networkItem.NameChanged -= NetworkItem_NameChanged;
if (networkItem.Ports != null) {
networkItem.Ports.ItemsAdded -= Ports_ItemsAdded;
networkItem.Ports.ItemsRemoved -= Ports_ItemsRemoved;
}
}
#region Overrides
public override void SetPosition(PointD lowerLeft, PointD upperRight) {
base.SetPosition(lowerLeft, upperRight);
SaveVisualProperties();
}
public override void Draw(Graphics graphics) {
int cornerRadius = (int)Math.Round(6 * Chart.WorldToPixelRatio.Width);
var point = Chart.TransformWorldToPixel(new PointD(LowerLeft.X, LowerLeft.Y + Size.Height));
var size = Chart.TransformWorldToPixel(Size);
var bounds = new System.Drawing.Rectangle(point.X, point.Y, size.Width, size.Height);
var arc = new System.Drawing.Rectangle(point, new Size(cornerRadius * 2, cornerRadius * 2));
using (var path = new GraphicsPath()) {
path.AddArc(arc, 180f, 90f);
arc.X = bounds.Right - cornerRadius * 2;
path.AddArc(arc, 270f, 90);
arc.Y = bounds.Bottom - cornerRadius * 2;
path.AddArc(arc, 0f, 90f);
arc.X = bounds.Left;
path.AddArc(arc, 90f, 90f);
path.CloseFigure();
graphics.FillPath(Brush, path);
graphics.DrawPath(Pen, path);
}
var p = Chart.TransformWorldToPixel(new PointD(LowerLeft.X, LowerLeft.Y + Size.Height));
if (networkItem != null) {
using (var headerFont = new Font(FontFamily.GenericSansSerif, (float)Math.Round(8.25 * Chart.WorldToPixelRatio.Width), FontStyle.Bold))
using (var contentFont = new Font(FontFamily.GenericSansSerif, (float)Math.Round(8.25 * Chart.WorldToPixelRatio.Width))) {
int margin = 4, x, y, lastY;
x = p.X + (int)Math.Round(margin * Chart.WorldToPixelRatio.Width);
y = p.Y + (int)Math.Round(margin * Chart.WorldToPixelRatio.Height);
var imageSize = networkItem.ItemImage.Size;
float imageWidth = (float)Math.Round(imageSize.Width * Chart.WorldToPixelRatio.Width);
float imageHeight = (float)Math.Round(imageSize.Height * Chart.WorldToPixelRatio.Height);
graphics.DrawImage(networkItem.ItemImage, x, y, imageWidth, imageHeight);
lastY = y;
var textSize = graphics.MeasureString(networkItem.Name, headerFont);
x += (int)Math.Round((imageSize.Width + margin) * Chart.WorldToPixelRatio.Width);
y += (int)Math.Round((imageSize.Height - textSize.Height * Chart.PixelToWorldRatio.Height) / 2 * Chart.WorldToPixelRatio.Height);
graphics.DrawString(networkItem.Name, headerFont, Brushes.Black, x, y);
y = lastY + (int)Math.Round(imageSize.Height * Chart.WorldToPixelRatio.Height);
x = p.X;
y += (int)Math.Round(margin * Chart.WorldToPixelRatio.Height);
graphics.DrawLine(Pen, x, y, x + size.Width, y);
foreach (var port in networkItem.Ports) {
x = p.X + (int)Math.Round(margin * Chart.WorldToPixelRatio.Width);
y += (int)Math.Round(margin * Chart.WorldToPixelRatio.Height);
imageSize = port.ItemImage.Size;
imageWidth = (float)Math.Round(imageSize.Width * Chart.WorldToPixelRatio.Width);
imageHeight = (float)Math.Round(imageSize.Height * Chart.WorldToPixelRatio.Height);
graphics.DrawImage(port.ItemImage, x, y, imageWidth, imageHeight);
lastY = y;
textSize = graphics.MeasureString(port.Name, contentFont);
x += (int)Math.Round((imageSize.Width + margin) * Chart.WorldToPixelRatio.Width);
y += (int)Math.Round((imageSize.Height - textSize.Height * Chart.PixelToWorldRatio.Height) / 2 * Chart.WorldToPixelRatio.Height);
graphics.DrawString(port.Name, contentFont, Brushes.Black, x, y);
y = lastY + (int)Math.Round(imageSize.Height * Chart.WorldToPixelRatio.Height);
var parameterizedPort = port as IParameterizedPort;
if (parameterizedPort != null) {
foreach (var param in parameterizedPort.Parameters) {
x = p.X + (int)Math.Round(4 * margin * Chart.WorldToPixelRatio.Width);
y += (int)Math.Round(margin * Chart.WorldToPixelRatio.Height);
imageSize = param.ItemImage.Size;
imageWidth = (float)Math.Round(imageSize.Width * Chart.WorldToPixelRatio.Width);
imageHeight = (float)Math.Round(imageSize.Height * Chart.WorldToPixelRatio.Height);
graphics.DrawImage(param.ItemImage, x, y, imageWidth, imageHeight);
lastY = y;
textSize = graphics.MeasureString(param.Name, contentFont);
x += (int)Math.Round((imageSize.Width + margin) * Chart.WorldToPixelRatio.Width);
y += (int)Math.Round((imageSize.Height - textSize.Height * Chart.PixelToWorldRatio.Height) / 2 * Chart.WorldToPixelRatio.Height);
graphics.DrawString(param.Name, contentFont, Brushes.Black, x, y);
y = lastY + (int)Math.Round(imageSize.Height * Chart.WorldToPixelRatio.Height);
}
}
}
}
}
portRectangles.Draw(graphics);
}
#endregion
public IPrimitive GetPortPrimitive(IPort name) {
IPrimitive portPrimitive;
port2primitive.TryGetValue(name, out portPrimitive);
return portPrimitive;
}
#region Events
public event EventHandler NetworkItemChanged;
protected virtual void OnNetworkItemChanged() {
var handler = NetworkItemChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
#endregion
#region Event Handlers
private void VisualProperties_Changed(object sender, EventArgs e) {
if (IgnoreVisualPropertiesChanges) return;
LoadVisualProperties();
}
private void NetworkItem_NameChanged(object sender, EventArgs e) { OnRedrawRequired(); }
private void Ports_ItemsAdded(object sender, Collections.CollectionItemsChangedEventArgs e) {
AdjustSize();
foreach (var port in e.Items) {
var portRectangle = PrimitiveAttribute.CreateDefaultPrimitive(port.GetType(), Chart, port, networkItem);
port2primitive.Add(port, portRectangle);
portRectangles.Add(portRectangle);
var parameterizedPort = port as IParameterizedPort;
if (parameterizedPort != null) {
parameterizedPort.Parameters.ItemsAdded += Parameters_ItemsAdded;
parameterizedPort.Parameters.ItemsRemoved += Parameters_ItemsRemoved;
}
}
}
private void Ports_ItemsRemoved(object sender, Collections.CollectionItemsChangedEventArgs e) {
AdjustSize();
foreach (var port in e.Items) {
var parameterizedPort = port as IParameterizedPort;
if (parameterizedPort != null) {
parameterizedPort.Parameters.ItemsRemoved -= Parameters_ItemsRemoved;
parameterizedPort.Parameters.ItemsAdded -= Parameters_ItemsAdded;
}
var portRectangle = port2primitive[port];
portRectangles.Remove(portRectangle);
port2primitive.Remove(port);
}
}
private void Parameters_ItemsAdded(object sender, Collections.CollectionItemsChangedEventArgs e) { AdjustSize(); }
private void Parameters_ItemsRemoved(object sender, Collections.CollectionItemsChangedEventArgs e) { AdjustSize(); }
#endregion
#region Helpers
private void LoadVisualProperties() {
INodeVisualProperties vp = new NodeVisualProperties();
if (networkItem != null)
if (VisualProperties == null) VisualProperties = vp;
else vp = VisualProperties;
IgnoreVisualPropertiesChanges = true;
try {
SetPosition(new PointD(vp.LowerLeft.X, vp.LowerLeft.Y), new PointD(vp.UpperRight.X, vp.UpperRight.Y));
Brush = new SolidBrush(vp.BrushColor);
Pen = new Pen(vp.PenColor);
} finally { IgnoreVisualPropertiesChanges = false; }
}
private void SaveVisualProperties() {
if (networkItem == null || IgnoreVisualPropertiesChanges) return;
var vp = VisualProperties;
IgnoreVisualPropertiesChanges = true;
try {
vp.LowerLeft = new Point2D(LowerLeft.X, LowerLeft.Y);
vp.UpperRight = new Point2D(UpperRight.X, UpperRight.Y);
vp.BrushColor = ((SolidBrush)Brush).Color;
vp.PenColor = Pen.Color;
} finally { IgnoreVisualPropertiesChanges = false; }
}
private void AdjustSize() {
if (networkItem == null) return;
var p = Chart.TransformWorldToPixel(new PointD(LowerLeft.X, LowerLeft.Y + Size.Height));
int margin = 4, x = p.X, y = p.Y;
using (var headerFont = new Font(FontFamily.GenericSansSerif, (float)Math.Round(8.25), FontStyle.Bold))
using (var contentFont = new Font(FontFamily.GenericSansSerif, (float)Math.Round(8.25))) {
x += 2 * margin; y += 2 * margin;
int headerWidth = networkItem.ItemImage.Width + margin + TextRenderer.MeasureText(networkItem.Name, headerFont).Width;
y += networkItem.ItemImage.Height + margin;
int maxPortWidth = 0, maxPortParamWidth = 0;
foreach (var port in networkItem.Ports) {
int portWidth = port.ItemImage.Width + margin + TextRenderer.MeasureText(port.Name, contentFont).Width;
if (portWidth > maxPortWidth) maxPortWidth = portWidth;
y += margin + port.ItemImage.Height;
var parameterizedPort = port as IParameterizedPort;
if (parameterizedPort != null) {
foreach (var param in parameterizedPort.Parameters) {
int portParamWidth = param.ItemImage.Width + 3 * margin + TextRenderer.MeasureText(param.Name, contentFont).Width;
if (portParamWidth > maxPortParamWidth) maxPortParamWidth = portParamWidth;
y += margin + param.ItemImage.Height;
}
}
}
x += Math.Max(headerWidth, Math.Max(maxPortWidth, maxPortParamWidth));
var ll = Chart.TransformPixelToWorld(new Point(p.X, y));
var ur = Chart.TransformPixelToWorld(new Point(x, p.Y));
SetPosition(ll, ur);
}
}
#endregion
}
}