#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.Drawing; using System.Drawing.Drawing2D; using System.Linq; using HeuristicLab.Common; using HeuristicLab.Core.Networks; using HeuristicLab.Visualization; namespace HeuristicLab.Networks.Views.NetworkVisualization { [Primitive(typeof(IPort), true)] public class PortRectangle : FixedSizeRectangle, INetworkItemPrimitive { private const int size = 10; protected readonly INodeVisualProperties nodeVisualProperties; protected IPortVisualProperties VisualProperties { get { return (IPortVisualProperties)networkItem.VisualProperties; } set { if (networkItem.VisualProperties == value) return; networkItem.VisualProperties = value; } } protected bool IgnoreVisualPropertiesChanges { get; set; } public override Size Size { get { return new Size((int)Math.Round(size * Chart.WorldToPixelRatio.Width), (int)Math.Round(size * Chart.WorldToPixelRatio.Height)); } } protected IPort networkItem; public IPort 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 PortRectangle(IChart chart, IPort port, INode node) : base(chart, PointD.Empty, Size.Empty) { nodeVisualProperties = (INodeVisualProperties)node.VisualProperties; nodeVisualProperties.Changed += NodeVisualProperties_Changed; NetworkItem = port; } protected virtual void RegisterNetworkItemEvents() { if (VisualProperties != null) { VisualProperties.Changed += VisualProperties_Changed; } networkItem.NameChanged += NetworkItem_NameChanged; var connectablePort = networkItem as IConnectablePort; if (connectablePort != null) { connectablePort.ConnectedPortChanged += Port_ConnectedPortChanged; } } protected virtual void DeregisterNetworkItemEvents() { if (VisualProperties != null) { VisualProperties.Changed -= VisualProperties_Changed; } networkItem.NameChanged -= NetworkItem_NameChanged; var connectablePort = networkItem as IConnectablePort; if (connectablePort != null) { connectablePort.ConnectedPortChanged -= Port_ConnectedPortChanged; } } #region Overrides public override void SetPosition(PointD point) { base.SetPosition(point); SaveVisualProperties(); } public override void Draw(Graphics graphics) { base.Draw(graphics); var p = Chart.TransformWorldToPixel(Point); if (networkItem != null && VisualProperties != null) { using (var font = new Font(FontFamily.GenericSansSerif, (float)Math.Round(8.25 * Chart.WorldToPixelRatio.Width))) { var textSize = graphics.MeasureString(networkItem.Name, font); var vp = VisualProperties; switch (vp.Orientation) { case Orientation.North: p = new Point((int)(p.X - textSize.Width / 2), (int)(p.Y - Size.Height - textSize.Height)); break; case Orientation.East: p = new Point(p.X + Size.Width, (int)(p.Y - textSize.Height / 2)); break; case Orientation.South: p = new Point((int)(p.X - textSize.Width / 2), p.Y + Size.Height); break; case Orientation.West: p = new Point((int)(p.X - Size.Width - textSize.Width), (int)(p.Y - textSize.Height / 2)); break; } graphics.DrawString(networkItem.Name, font, Brushes.Black, p); } } } public override void PostDraw(Graphics graphics) { base.PostDraw(graphics); if (Selected) { var p = Chart.TransformWorldToPixel(Point); using (var pen = new Pen(Color.LightGray, 3) { DashStyle = DashStyle.Dash }) graphics.DrawRectangle(pen, p.X - Size.Width / 2, p.Y - Size.Height / 2, Size.Width, Size.Height); } } #endregion #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 NodeVisualProperties_Changed(object sender, EventArgs e) { if (IgnoreVisualPropertiesChanges) return; LoadVisualProperties(); } private void VisualProperties_Changed(object sender, EventArgs e) { if (IgnoreVisualPropertiesChanges) return; LoadVisualProperties(); } private void NetworkItem_NameChanged(object sender, EventArgs e) { OnRedrawRequired(); } private void Port_ConnectedPortChanged(object sender, EventArgs e) { var connectionLines = Chart.Group.GetAllPrimitives(Point).OfType(); var outgoingLines = connectionLines.Where(x => x.StartPortRectangle == this); foreach (var l in outgoingLines) Chart.Group.Remove(l); var connectablePort = (IConnectablePort)networkItem; var connectedPort = connectablePort.ConnectedPort as IConnectablePort; if (connectedPort == null) return; var nodeRectangles = Chart.Group.Primitives.OfType(); PortRectangle endPortRectangle = null; foreach (var nodeRectangle in nodeRectangles) { endPortRectangle = (PortRectangle)nodeRectangle.GetPortPrimitive(connectedPort); if (endPortRectangle != null) break; } if (endPortRectangle == null) return; var connectionShape = new ConnectionLine(Chart, this, endPortRectangle); Chart.Group.Add(connectionShape); } #endregion #region Helpers private void LoadVisualProperties() { if (VisualProperties == null) VisualProperties = new PortVisualProperties(); var vp = VisualProperties; IgnoreVisualPropertiesChanges = true; try { var p = PointD.Empty; double nodeWidth = nodeVisualProperties.UpperRight.X - nodeVisualProperties.LowerLeft.X; double nodeHeight = nodeVisualProperties.UpperRight.Y - nodeVisualProperties.LowerLeft.Y; switch (vp.Orientation) { case Orientation.North: p.X = nodeVisualProperties.LowerLeft.X + vp.Offset * nodeWidth; p.Y = nodeVisualProperties.UpperRight.Y; break; case Orientation.East: p.X = nodeVisualProperties.UpperRight.X; p.Y = nodeVisualProperties.UpperRight.Y - vp.Offset * nodeHeight; break; case Orientation.South: p.X = nodeVisualProperties.UpperRight.X - vp.Offset * nodeWidth; p.Y = nodeVisualProperties.LowerLeft.Y; break; case Orientation.West: p.X = nodeVisualProperties.LowerLeft.X; p.Y = nodeVisualProperties.LowerLeft.Y + vp.Offset * nodeHeight; break; } SetPosition(p); 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 { var lowerLeft = nodeVisualProperties.LowerLeft; var upperRight = nodeVisualProperties.UpperRight; if (lowerLeft.X <= Point.X && Point.X <= upperRight.X) { // check N/S if (Point.Y.IsAlmost(upperRight.Y)) { vp.Orientation = Orientation.North; vp.Offset = (Point.X - lowerLeft.X) / (upperRight.X - lowerLeft.X); } else if (Point.Y.IsAlmost(lowerLeft.Y)) { vp.Orientation = Orientation.South; vp.Offset = (upperRight.X - Point.X) / (upperRight.X - lowerLeft.X); } } if (lowerLeft.Y <= Point.Y && Point.Y <= upperRight.Y) { // check E/W if (Point.X.IsAlmost(upperRight.X)) { vp.Orientation = Orientation.East; vp.Offset = (upperRight.Y - Point.Y) / (upperRight.Y - lowerLeft.Y); } else if (Point.X.IsAlmost(lowerLeft.X)) { vp.Orientation = Orientation.West; vp.Offset = (Point.Y - lowerLeft.Y) / (upperRight.Y - lowerLeft.Y); } } vp.BrushColor = ((SolidBrush)Brush).Color; vp.PenColor = Pen.Color; } finally { IgnoreVisualPropertiesChanges = false; } } #endregion } }