#region License Information
/* HeuristicLab
* Copyright (C) 2002-2019 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 System.Threading;
using System.Windows.Forms;
using HeuristicLab.Collections;
using HeuristicLab.MainForm;
using HeuristicLab.MainForm.WindowsForms;
using Netron.Diagramming.Core;
namespace HeuristicLab.Operators.Views.GraphVisualization.Views {
[View("GraphVisualizationInfo View")]
[Content(typeof(IGraphVisualizationInfo), true)]
public partial class GraphVisualizationInfoView : AsynchronousContentView {
private BidirectionalDictionary shapeInfoShapeMapping;
private BidirectionalDictionary connectionInfoConnectionMapping;
private LinePenStyle connectionPenStyle;
public GraphVisualizationInfoView() {
InitializeComponent();
this.shapeInfoShapeMapping = new BidirectionalDictionary();
this.connectionInfoConnectionMapping = new BidirectionalDictionary();
this.connectionPenStyle = new LinePenStyle();
this.connectionPenStyle.EndCap = LineCap.ArrowAnchor;
PasteTool pasteTool = (PasteTool)this.Controller.Tools.Where(t => t.Name == ControllerBase.PasteToolName).FirstOrDefault();
CopyTool copyTool = (CopyTool)this.Controller.Tools.Where(t => t.Name == ControllerBase.CopyToolName).FirstOrDefault();
HeuristicLab.Netron.Controller controller = this.Controller as HeuristicLab.Netron.Controller;
if (controller != null) {
if (pasteTool != null) controller.RemoveTool(pasteTool);
if (copyTool != null) controller.RemoveTool(copyTool);
}
}
public IController Controller {
get { return this.graphVisualization.Controller; }
}
public new IGraphVisualizationInfo Content {
get { return (IGraphVisualizationInfo)base.Content; }
set { base.Content = value; }
}
protected override void OnContentChanged() {
base.OnContentChanged();
this.UpdateContent();
}
protected override void SetEnabledStateOfControls() {
base.SetEnabledStateOfControls();
DeleteTool deleteTool = (DeleteTool)this.Controller.Tools.Where(t => t.Name == ControllerBase.DeleteToolName).FirstOrDefault();
HeuristicLab.Netron.Controller controller = this.Controller as HeuristicLab.Netron.Controller;
if (Content == null && deleteTool != null && controller != null)
controller.RemoveTool(deleteTool);
else {
if ((ReadOnly || Locked) && deleteTool != null && controller != null)
controller.RemoveTool(deleteTool);
else if ((!ReadOnly && !Locked) && deleteTool == null)
this.Controller.AddTool(new DeleteTool(ControllerBase.DeleteToolName));
}
}
private void UpdateContent() {
foreach (IConnectionInfo connectionInfo in this.connectionInfoConnectionMapping.FirstKeys.ToList())
this.RemoveConnectionInfo(connectionInfo);
this.connectionInfoConnectionMapping.Clear();
foreach (IShapeInfo shapeInfo in this.shapeInfoShapeMapping.FirstKeys.ToList())
this.RemoveShapeInfo(shapeInfo);
this.shapeInfoShapeMapping.Clear();
if (Content != null) {
foreach (IShapeInfo shapeInfo in this.Content.ShapeInfos)
this.AddShapeInfo(shapeInfo);
foreach (IConnectionInfo connectionInfo in this.Content.ConnectionInfos)
this.AddConnectionInfo(connectionInfo);
this.UpdateLayoutRoot();
}
}
private void UpdateLayoutRoot() {
IShapeInfo shapeInfo = this.Content.InitialShape;
if (shapeInfo != null)
this.graphVisualization.Controller.Model.LayoutRoot = this.shapeInfoShapeMapping.GetByFirst(shapeInfo);
else
this.graphVisualization.Controller.Model.LayoutRoot = null;
}
private void VisualizationInfo_InitialShapeChanged(object sender, EventArgs e) {
this.UpdateLayoutRoot();
}
protected override void RegisterContentEvents() {
base.RegisterContentEvents();
this.Content.InitialShapeChanged += new EventHandler(VisualizationInfo_InitialShapeChanged);
this.Content.ObserveableShapeInfos.ItemsAdded += new CollectionItemsChangedEventHandler(ShapeInfos_ItemsAdded);
this.Content.ObserveableShapeInfos.ItemsRemoved += new CollectionItemsChangedEventHandler(ShapeInfos_ItemsRemoved);
this.Content.ObserveableShapeInfos.CollectionReset += new CollectionItemsChangedEventHandler(ShapeInfos_CollectionReset);
this.Content.ObservableConnectionInfos.ItemsAdded += new CollectionItemsChangedEventHandler(ConnectionInfos_ItemsAdded);
this.Content.ObservableConnectionInfos.ItemsRemoved += new CollectionItemsChangedEventHandler(ConnectionInfos_ItemsRemoved);
this.Content.ObservableConnectionInfos.CollectionReset += new CollectionItemsChangedEventHandler(ConnectionInfos_CollectionReset);
}
protected override void DeregisterContentEvents() {
base.DeregisterContentEvents();
this.Content.InitialShapeChanged -= new EventHandler(VisualizationInfo_InitialShapeChanged);
this.Content.ObserveableShapeInfos.ItemsAdded -= new CollectionItemsChangedEventHandler(ShapeInfos_ItemsAdded);
this.Content.ObserveableShapeInfos.ItemsRemoved -= new CollectionItemsChangedEventHandler(ShapeInfos_ItemsRemoved);
this.Content.ObserveableShapeInfos.CollectionReset -= new CollectionItemsChangedEventHandler(ShapeInfos_CollectionReset);
this.Content.ObservableConnectionInfos.ItemsAdded -= new CollectionItemsChangedEventHandler(ConnectionInfos_ItemsAdded);
this.Content.ObservableConnectionInfos.ItemsRemoved -= new CollectionItemsChangedEventHandler(ConnectionInfos_ItemsRemoved);
this.Content.ObservableConnectionInfos.CollectionReset -= new CollectionItemsChangedEventHandler(ConnectionInfos_CollectionReset);
}
#region ShapeInfos
private void ShapeInfos_CollectionReset(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs e) {
foreach (IShapeInfo shapeInfo in e.OldItems)
this.RemoveShapeInfo(shapeInfo);
foreach (IShapeInfo shapeInfo in e.Items)
this.AddShapeInfo(shapeInfo);
}
private void ShapeInfos_ItemsAdded(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs e) {
foreach (IShapeInfo shapeInfo in e.Items)
this.AddShapeInfo(shapeInfo);
}
private void ShapeInfos_ItemsRemoved(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs e) {
foreach (IShapeInfo shapeInfo in e.Items)
this.RemoveShapeInfo(shapeInfo);
}
private void AddShapeInfo(IShapeInfo shapeInfo) {
this.RegisterShapeInfoEvents(shapeInfo);
IShape shape = shapeInfo.CreateShape();
this.RegisterShapeEvents(shape);
this.shapeInfoShapeMapping.Add(shapeInfo, shape);
this.graphVisualization.Controller.Model.AddShape(shape);
this.graphVisualization.Invalidate();
}
private void RemoveShapeInfo(IShapeInfo shapeInfo) {
this.DeregisterShapeInfoEvents(shapeInfo);
IShape shape = this.shapeInfoShapeMapping.GetByFirst(shapeInfo);
this.DeregisterShapeEvents(shape);
this.shapeInfoShapeMapping.RemoveByFirst(shapeInfo);
if (this.graphVisualization.Controller.Model.Shapes.Contains(shape)) {
this.graphVisualization.Controller.Model.RemoveShape(shape);
this.graphVisualization.Controller.Model.Selection.Clear();
this.graphVisualization.Invalidate();
}
}
private void RegisterShapeInfoEvents(IShapeInfo shapeInfo) {
shapeInfo.Changed += new EventHandler(shapeInfo_Changed);
}
private void DeregisterShapeInfoEvents(IShapeInfo shapeInfo) {
shapeInfo.Changed -= new EventHandler(shapeInfo_Changed);
}
private void shapeInfo_Changed(object sender, EventArgs e) {
IShapeInfo shapeInfo = (IShapeInfo)sender;
IShape shape = this.shapeInfoShapeMapping.GetByFirst(shapeInfo);
this.DeregisterShapeEvents(shape);
shapeInfo.UpdateShape(shape);
shape.Invalidate();
this.RegisterShapeEvents(shape);
}
#endregion
#region ConnectionInfos
private void ConnectionInfos_CollectionReset(object sender, CollectionItemsChangedEventArgs e) {
foreach (IConnectionInfo connectionInfo in e.Items)
this.RemoveConnectionInfo(connectionInfo);
foreach (IConnectionInfo connectionInfo in e.Items)
this.AddConnectionInfo(connectionInfo);
}
private void ConnectionInfos_ItemsAdded(object sender, CollectionItemsChangedEventArgs e) {
foreach (IConnectionInfo connectionInfo in e.Items)
this.AddConnectionInfo(connectionInfo);
}
private void ConnectionInfos_ItemsRemoved(object sender, CollectionItemsChangedEventArgs e) {
foreach (IConnectionInfo connectionInfo in e.Items)
this.RemoveConnectionInfo(connectionInfo);
}
private void AddConnectionInfo(IConnectionInfo connectionInfo) {
this.RegisterConnectionInfoEvents(connectionInfo);
IShape shapeFrom = this.shapeInfoShapeMapping.GetByFirst(connectionInfo.From);
IShape shapeTo = this.shapeInfoShapeMapping.GetByFirst(connectionInfo.To);
IConnector connectorFrom = shapeFrom.Connectors.Where(c => c.Name == connectionInfo.ConnectorFrom).FirstOrDefault();
IConnector connectorTo = shapeTo.Connectors.Where(c => c.Name == connectionInfo.ConnectorTo).FirstOrDefault();
if (connectorFrom != null && connectorTo != null) {
Connection connection = new Connection(connectorFrom.Point, connectorTo.Point);
connection.From.AllowMove = false;
connection.To.AllowMove = false;
connectorFrom.AttachConnector(connection.From);
connectorTo.AttachConnector(connection.To);
connection.PenStyle = this.connectionPenStyle;
this.connectionInfoConnectionMapping.Add(connectionInfo, connection);
this.graphVisualization.Controller.Model.AddConnection(connection);
this.graphVisualization.Invalidate();
}
}
private void RemoveConnectionInfo(IConnectionInfo connectionInfo) {
DeregisterConnectionInfoEvents(connectionInfo);
IConnection connection = this.connectionInfoConnectionMapping.GetByFirst(connectionInfo);
this.connectionInfoConnectionMapping.RemoveByFirst(connectionInfo);
this.RemoveConnection(connection);
}
private void RemoveConnection(IConnection connection) {
if (connection.From.AttachedTo != null)
connection.From.DetachFromParent();
if (connection.To.AttachedTo != null)
connection.To.DetachFromParent();
if (this.Controller.Model.Connections.Contains(connection)) {
this.graphVisualization.Controller.Model.Remove(connection);
this.graphVisualization.Invalidate();
}
}
private void RegisterConnectionInfoEvents(IConnectionInfo connectionInfo) {
connectionInfo.Changed += new EventHandler(connectionInfo_Changed);
}
private void DeregisterConnectionInfoEvents(IConnectionInfo connectionInfo) {
connectionInfo.Changed -= new EventHandler(connectionInfo_Changed);
}
private void connectionInfo_Changed(object sender, EventArgs e) {
IConnectionInfo connectionInfo = (IConnectionInfo)sender;
IConnection connection = this.connectionInfoConnectionMapping.GetByFirst(connectionInfo);
this.RemoveConnectionInfo(connectionInfo);
this.AddConnectionInfo(connectionInfo);
}
#endregion
#region netron events - shapes, graphvisualization
private void RegisterShapeEvents(IShape shape) {
shape.OnEntityChange += new EventHandler(shape_OnEntityChange);
shape.OnMouseEnter += new EventHandler(shape_OnMouseEnter);
shape.OnMouseLeave += new EventHandler(shape_OnMouseLeave);
}
private void DeregisterShapeEvents(IShape shape) {
shape.OnEntityChange -= new EventHandler(shape_OnEntityChange);
shape.OnMouseEnter -= new EventHandler(shape_OnMouseEnter);
shape.OnMouseLeave -= new EventHandler(shape_OnMouseLeave);
}
private Cursor oldCursor;
private void shape_OnMouseEnter(object sender, EntityMouseEventArgs e) {
this.oldCursor = this.Cursor;
this.Controller.View.CurrentCursor = CursorPalette.Move;
}
private void shape_OnMouseLeave(object sender, EntityMouseEventArgs e) {
this.Controller.View.CurrentCursor = this.oldCursor;
this.oldCursor = null;
}
private void shape_OnEntityChange(object sender, EntityEventArgs e) {
IShape shape = e.Entity as IShape;
IShapeInfo shapeInfo = this.shapeInfoShapeMapping.GetBySecond(shape);
this.DeregisterShapeInfoEvents(shapeInfo);
shapeInfo.UpdateShapeInfo(shape);
this.RegisterShapeInfoEvents(shapeInfo);
this.graphVisualization.Invalidate();
}
private void graphVisualization_OnEntityAdded(object sender, EntityEventArgs e) {
IConnection connection = e.Entity as IConnection;
if (connection != null && !this.connectionInfoConnectionMapping.ContainsSecond(connection)) {
IConnector connectorFrom = connection.From.AttachedTo;
IConnector connectorTo = connection.To.AttachedTo;
this.RemoveConnection(connection); //is added again by the model events
if (connectorFrom != null && connectorTo != null) {
IShape shapeFrom = (IShape)connectorFrom.Parent;
IShape shapeTo = (IShape)connectorTo.Parent;
IShapeInfo shapeInfoFrom = this.shapeInfoShapeMapping.GetBySecond(shapeFrom);
IShapeInfo shapeInfoTo = this.shapeInfoShapeMapping.GetBySecond(shapeTo);
string connectorFromName = connectorFrom.Name;
string connectorToName = connectorTo.Name;
if (shapeInfoFrom != shapeInfoTo) //avoid self references
this.Content.AddConnectionInfo(new ConnectionInfo(shapeInfoFrom, connectorFromName, shapeInfoTo, connectorToName));
}
}
}
private void graphVisualization_OnEntityRemoved(object sender, EntityEventArgs e) {
IShape shape = e.Entity as IShape;
if (shape != null && this.shapeInfoShapeMapping.ContainsSecond(shape)) {
IShapeInfo shapeInfo = this.shapeInfoShapeMapping.GetBySecond(shape);
this.Content.RemoveShapeInfo(shapeInfo);
}
IConnection connection = e.Entity as IConnection;
if (connection != null && this.connectionInfoConnectionMapping.ContainsSecond(connection)) {
IConnectionInfo connectionInfo = connectionInfoConnectionMapping.GetBySecond(connection);
this.Content.RemoveConnectionInfo(connectionInfo);
}
}
#endregion
public void RelayoutGraph() {
if (this.shapeInfoShapeMapping.Count > 0
&& this.connectionInfoConnectionMapping.Count > 0
&& this.Content.InitialShape != null) { //otherwise the layout does not work
string layoutName = "Standard TreeLayout";
this.graphVisualization.Controller.RunActivity(layoutName);
this.graphVisualization.Invalidate();
//fix to avoid negative shape positions after layouting
Thread.Sleep(300);
int minX = this.graphVisualization.Controller.Model.Shapes.Min(s => s.Location.X);
int shiftX = minX < 0 ? Math.Abs(minX) + 50 : 0;
int minY = this.graphVisualization.Controller.Model.Shapes.Min(s => s.Location.Y);
int shiftY = minY < 0 ? Math.Abs(minY) + 50 : 0;
if (minX < 0 || minY < 0) {
foreach (IShape s in this.Controller.Model.Shapes)
s.MoveBy(new Point(shiftX, shiftY));
}
}
}
}
}