#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); } } private void Ports_ItemsRemoved(object sender, Collections.CollectionItemsChangedEventArgs e) { AdjustSize(); foreach (var port in e.Items) { var portRectangle = port2primitive[port]; port2primitive.Remove(port); portRectangles.Remove(portRectangle); } } #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 } }