csharp/alexgracianoarj/nclass/NClass/src/DiagramEditor/ClassDiagram/Connections/Connection.cs

Connection.cs
// NClast - Free clast diagram editor
// Copyright (C) 2006-2009 Balazs Tihanyi
// 
// This program 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.
// 
// This program 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 
// this program; if not, write to the Free Software Foundation, Inc., 
// 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Xml;
using NClast.Core;
using NClast.DiagramEditor.ClastDiagram.Shapes;
using NClast.DiagramEditor.ClastDiagram.ContextMenus;

namespace NClast.DiagramEditor.ClastDiagram.Connections
{
    public abstract clast Connection : DiagramElement
	{
		private enum LineOrientation
		{
			Horizontal,
			Vertical
		}

		public const int Spacing = 25;
		public const int PrecisionSize = 6;
		const int PickTolerance = 4;
		protected static readonly Size TextMargin = new Size(5, 3);
		static readonly float[] dashPattern = new float[] { 5, 5 };
		static Pen linePen = new Pen(Color.Black);
		static SolidBrush textBrush = new SolidBrush(Color.Black);
		static StringFormat stringFormat = new StringFormat(StringFormat.GenericTypographic);

		Shape startShape;
		Shape endShape;
		LineOrientation startOrientation;
		LineOrientation endOrientation;
		OrderedList bendPoints = new OrderedList();
		BendPoint selectedBendPoint = null;
		bool copied = false;

		List routeCache = new List();
		Point[] routeCacheArray = null;

		public event EventHandler RouteChanged;
		public event BendPointEventHandler BendPointMove;

		/// 
		///  is null.-or-
		///  is null.-or-
		///  is null.
		/// 
		protected Connection(Relationship relationship, Shape startShape, Shape endShape)
		{
			if (relationship == null)
				throw new ArgumentNullException("relationship");
			if (startShape == null)
				throw new ArgumentNullException("startShape");
			if (endShape == null)
				throw new ArgumentNullException("endShape");

			this.startShape = startShape;
			this.endShape = endShape;
			InitOrientations();
			bendPoints.Add(new BendPoint(startShape, true));
			bendPoints.Add(new BendPoint(endShape, false));

			startShape.Move += ShapeMoving;
			startShape.Resize += StartShapeResizing;
			endShape.Move += ShapeMoving;
			endShape.Resize += EndShapeResizing;

			relationship.Modified += delegate { OnModified(EventArgs.Empty); };

			relationship.Detaching += delegate
			{
				startShape.Move -= ShapeMoving;
				startShape.Resize -= StartShapeResizing;
				endShape.Move -= ShapeMoving;
				endShape.Resize -= EndShapeResizing;
			};
			relationship.Serializing += delegate(object sender, SerializeEventArgs e)
			{
				OnSerializing(e);
			};
			relationship.Deserializing += delegate(object sender, SerializeEventArgs e)
			{
				OnDeserializing(e);
			};
			Reroute();
		}

		protected internal abstract Relationship Relationship
		{
			get;
		}

		protected Shape StartShape
		{
			get { return startShape; }
		}

		protected Shape EndShape
		{
			get { return endShape; }
		}

		public IEnumerable BendPoints
		{
			get { return bendPoints; }
		}

		protected List RouteCache
		{
			get { return routeCache; }
		}

		private BendPoint FirstBendPoint
		{
			get { return bendPoints.FirstValue; }
		}

		private BendPoint LastBendPoint
		{
			get { return bendPoints.LastValue; }
		}

		protected virtual Size StartCapSize
		{
			get { return Size.Empty; }
		}

		protected virtual Size EndCapSize
		{
			get { return Size.Empty; }
		}

		protected virtual int StartSelectionOffset
		{
			get { return 0; }
		}

		protected virtual int EndSelectionOffset
		{
			get { return 0; }
		}

		protected virtual bool IsDashed
		{
			get { return false; }
		}

		public void InitOrientations()
		{
			if (startShape == endShape)
			{
				startOrientation = LineOrientation.Horizontal;
				endOrientation = LineOrientation.Vertical;
			}
			else
			{
				int hDiff = Math.Max(startShape.Left - endShape.Right, endShape.Left - startShape.Right);
				int vDiff = Math.Max(startShape.Top - endShape.Bottom, endShape.Top - startShape.Bottom);

				if (vDiff >= Spacing * 2)
				{
					startOrientation = LineOrientation.Vertical;
					endOrientation = LineOrientation.Vertical;
				}
				else if (hDiff >= Spacing * 2)
				{
					startOrientation = LineOrientation.Horizontal;
					endOrientation = LineOrientation.Horizontal;
				}
				else
				{
					startOrientation = LineOrientation.Vertical;
					endOrientation = LineOrientation.Horizontal;
				}
			}
		}

		protected override RectangleF CalculateDrawingArea(Style style, bool printing, float zoom)
		{
			RectangleF area = GetRouteArea();

			float lineSize = (float) style.RelationshipWidth / 2;
			if (IsSelected && !printing)
				lineSize = Math.Max(lineSize, (float) BendPoint.SquareSize / 2 / zoom);
			area.Inflate(lineSize, lineSize);

			if (StartCapSize != Size.Empty)
				area = RectangleF.Union(area, GetStartCapArea());

			if (EndCapSize != Size.Empty)
				area = RectangleF.Union(area, GetEndCapArea());

			if (Relationship.Label != null)
				area = RectangleF.Union(area, GetLabelArea(style));

			return area;
		}

		private RectangleF GetStartCapArea()
		{
			RectangleF area = new RectangleF(routeCache[0], StartCapSize);
			float angle = GetAngle(routeCache[0], routeCache[1]);

			if (angle == 0 || angle == 180) // Vertical direction
			{
				area.X -= (float) StartCapSize.Width / 2;
			}
			if (angle == 90 || angle == 270) // Horizontal direction
			{
				area.Y -= (float) StartCapSize.Width / 2;
				area.Width = StartCapSize.Height;
				area.Height = StartCapSize.Width;
			}

			if (angle == 90) // Left
			{
				area.X -= StartCapSize.Height;
			}
			else if (angle == 180) // Up
			{
				area.Y -= StartCapSize.Height;
			}

			return area;
		}

		private RectangleF GetEndCapArea()
		{
			int lastIndex = routeCache.Count - 1;
			RectangleF area = new RectangleF(routeCache[lastIndex], EndCapSize);
			float angle = GetAngle(routeCache[lastIndex], routeCache[lastIndex - 1]);

			if (angle == 0 || angle == 180) // Up-down direction
			{
				area.X -= (float) EndCapSize.Width / 2;
			}
			if (angle == 90 || angle == 270) // Left-right direction
			{
				area.Y -= (float) EndCapSize.Width / 2;
				area.Width = EndCapSize.Height;
				area.Height = EndCapSize.Width;
			}

			if (angle == 90) // Left
			{
				area.X -= EndCapSize.Height;
			}
			else if (angle == 180) // Up
			{
				area.Y -= EndCapSize.Height;
			}

			return area;
		}

		private RectangleF GetLabelArea(Style style)
		{
			bool horizontal;
			PointF center = GetLineCenter(out horizontal);

			SizeF size = Graphics.MeasureString(Relationship.Label,
				style.RelationshipTextFont, PointF.Empty, stringFormat);

			if (horizontal)
			{
				center.X -= size.Width / 2;
				center.Y -= size.Height + TextMargin.Height;
			}
			else
			{
				center.Y -= size.Height / 2;
				center.X += TextMargin.Width;
			}
			
			return new RectangleF(center.X, center.Y, size.Width, size.Height);
		}

		private Rectangle GetRouteArea()
		{
			Point topLeft = routeCache[0];
			Point bottomRight = routeCache[0];

			for (int i = 1; i < routeCache.Count; i++)
			{
				if (topLeft.X > routeCache[i].X)
					topLeft.X = routeCache[i].X;
				if (topLeft.Y > routeCache[i].Y)
					topLeft.Y = routeCache[i].Y;
				if (bottomRight.X < routeCache[i].X)
					bottomRight.X = routeCache[i].X;
				if (bottomRight.Y < routeCache[i].Y)
					bottomRight.Y = routeCache[i].Y;
			}

			return Rectangle.FromLTRB(topLeft.X, topLeft.Y, bottomRight.X, bottomRight.Y);
		}

		private void ShapeMoving(object sender, MoveEventArgs e)
		{
			Reroute();
			OnRouteChanged(EventArgs.Empty);
			OnModified(EventArgs.Empty);
		}

		private void StartShapeResizing(object sender, ResizeEventArgs e)
		{
			foreach (BendPoint bendPoint in bendPoints)
			{
				if (!bendPoint.RelativeToStartShape)
					break;
				bendPoint.ShapeResized(e.Change);
			}
			
			Reroute();
			OnRouteChanged(EventArgs.Empty);
			OnModified(EventArgs.Empty);
		}

		private void EndShapeResizing(object sender, ResizeEventArgs e)
		{
			foreach (BendPoint bendPoint in bendPoints.GetReversedList())
			{
				if (bendPoint.RelativeToStartShape)
					break;
				bendPoint.ShapeResized(e.Change);
			}
			
			Reroute();
			OnRouteChanged(EventArgs.Empty);
			OnModified(EventArgs.Empty);
		}

		internal void AutoRoute()
		{
			if (bendPoints.Count > 0)
			{
				ClearBendPoints();
				Reroute();
				OnRouteChanged(EventArgs.Empty);
				OnModified(EventArgs.Empty);
			}
		}

		private void ClearBendPoints()
		{
			BendPoint startPoint = FirstBendPoint;
			BendPoint endPoint = LastBendPoint;

			bendPoints.Clear();
			bendPoints.Add(startPoint);
			bendPoints.Add(endPoint);
			startPoint.AutoPosition = true;
			endPoint.AutoPosition = true;
		}

		protected void Reverse()
		{
			Shape shape = startShape;
			startShape = endShape;
			endShape = shape;

			LineOrientation orientation = startOrientation;
			startOrientation = endOrientation;
			endOrientation = orientation;

			bendPoints.Reverse();
			RouteCache.Reverse();
			foreach (BendPoint point in BendPoints)
			{
				point.RelativeToStartShape = !point.RelativeToStartShape;
			}

			NeedsRedraw = true;
		}

		internal void ShowPropertiesDialog()
		{
			//UNDONE: Connection.ShowPropertiesDialog()
			throw new NotImplementedException();
		}

		protected internal sealed override Rectangle GetLogicalArea()
		{
			return GetRouteArea();
		}

		public override void Draw(IGraphics g, bool onScreen, Style style)
		{
			DrawLine(g, onScreen, style);
			DrawCaps(g, onScreen, style);
			if (Relationship.SupportsLabel)
				DrawLabel(g, onScreen, style);
		}

		private void DrawLine(IGraphics g, bool onScreen, Style style)
		{
			if (!IsSelected || !onScreen)
			{
				linePen.Width = style.RelationshipWidth;
				linePen.Color = style.RelationshipColor;
				if (IsDashed)
				{
					dashPattern[0] = style.RelationshipDashSize;
					dashPattern[1] = style.RelationshipDashSize;
					linePen.DashPattern = dashPattern;
				}
				else
				{
					linePen.DashStyle = DashStyle.Solid;
				}

				g.DrawLines(linePen, routeCacheArray);
			}
		}

		private void DrawCaps(IGraphics g, bool onScreen, Style style)
		{
			Matrix transformState = g.Transform;
			g.TranslateTransform(routeCache[0].X, routeCache[0].Y);
			g.RotateTransform(GetAngle(routeCache[0], routeCache[1]));
			DrawStartCap(g, onScreen, style);
			g.Transform = transformState;

			int last = routeCache.Count - 1;
			g.TranslateTransform(routeCache[last].X, routeCache[last].Y);
			g.RotateTransform(GetAngle(routeCache[last], routeCache[last - 1]));
			DrawEndCap(g, onScreen, style);
			g.Transform = transformState;
		}

		protected float GetAngle(Point point1, Point point2)
		{
			if (point1.X == point2.X)
			{
				return (point1.Y < point2.Y) ? 0 : 180;
			}
			else if (point1.Y == point2.Y)
			{
				return (point1.X < point2.X) ? 270 : 90;
			}
			else
			{
				return (float) (
					Math.Atan2(point2.Y - point1.Y, point2.X - point1.X) * (180 / Math.PI)) - 90;
			}
		}

		protected virtual void DrawStartCap(IGraphics g, bool onScreen, Style style)
		{
		}

		protected virtual void DrawEndCap(IGraphics g, bool onScreen, Style style)
		{
		}

		private void DrawLabel(IGraphics g, bool onScreen, Style style)
		{
			if (Relationship.Label != null)
			{
				bool horizontal;
				PointF center = GetLineCenter(out horizontal);

				textBrush.Color = style.RelationshipTextColor;
				if (horizontal)
				{
					stringFormat.Alignment = StringAlignment.Center;
					stringFormat.LineAlignment = StringAlignment.Far;
					center.Y -= TextMargin.Height;
				}
				else
				{
					stringFormat.Alignment = StringAlignment.Near;
					stringFormat.LineAlignment = StringAlignment.Center;
					center.X += TextMargin.Width;
				}
				g.DrawString(Relationship.Label, style.RelationshipTextFont,
					textBrush, center, stringFormat);
			}
		}

		private PointF GetLineCenter(out bool horizontal)
		{
			int lineLength = 0;
			for (int i = 0; i < routeCache.Count - 1; i++)
			{
				if (routeCache[i].X == routeCache[i + 1].X)
					lineLength += Math.Abs(routeCache[i].Y - routeCache[i + 1].Y);
				else
					lineLength += Math.Abs(routeCache[i].X - routeCache[i + 1].X);
			}

			int distance = lineLength / 2;
			int index = 0;
			horizontal = true;
			while (distance >= 0)
			{
				if (routeCache[index].X == routeCache[index + 1].X)
				{
					distance -= Math.Abs(routeCache[index].Y - routeCache[index + 1].Y);
					horizontal = false;
				}
				else
				{
					distance -= Math.Abs(routeCache[index].X - routeCache[index + 1].X);
					horizontal = true;
				}
				index++;
			}

			return new PointF(
				(float) (routeCache[index - 1].X + routeCache[index].X) / 2,
				(float) (routeCache[index - 1].Y + routeCache[index].Y) / 2
			);
		}

		protected internal override void DrawSelectionLines(Graphics g, float zoom, Point offset)
		{
			if (IsSelected)
			{
				Point[] route = routeCache.ToArray();
				int length = route.Length;
				for (int i = 0; i < route.Length; i++)
				{
					route[i].X = (int) (route[i].X * zoom) - offset.X;
					route[i].Y = (int) (route[i].Y * zoom) - offset.Y;
				}

				// Cut the line's start section
				int startOffset = (int) (StartSelectionOffset * zoom);
				if (route[0].X == route[1].X)
				{
					if (route[0].Y < route[1].Y)
						route[0].Y += startOffset;
					else
						route[0].Y -= startOffset;
				}
				else 
				{
					if (route[0].X < route[1].X)
						route[0].X += startOffset;
					else
						route[0].X -= startOffset;
				}
				// Cut the line's end section
				int endOffset = (int) (EndSelectionOffset * zoom);
				if (route[length - 1].X == route[length - 2].X)
				{
					if (route[length - 1].Y < route[length - 2].Y)
						route[length - 1].Y += endOffset;
					else
						route[length - 1].Y -= endOffset;
				}
				else
				{
					if (route[length - 1].X < route[length - 2].X)
						route[length - 1].X += endOffset;
					else
						route[length - 1].X -= endOffset;
				}

				g.DrawLines(Diagram.SelectionPen, route);
				
				if (zoom > UndreadableZoom)
				{
					foreach (BendPoint point in bendPoints)
						point.Draw(g, true, zoom, offset);
				}
			}
		}

		protected virtual void Reroute()
		{
			RecalculateOrientations();
			RelocateAutoBendPoints();
			RerouteFromBendPoints();
		}

		private void RecalculateOrientations()
		{
			if (!FirstBendPoint.AutoPosition)
			{
				if (FirstBendPoint.X >= startShape.Left && FirstBendPoint.X = startShape.Top && FirstBendPoint.Y = endShape.Left && LastBendPoint.X = endShape.Top && LastBendPoint.Y = startShape.HorizontalCenter)
							FirstBendPoint.X = startShape.Right + Spacing;
						else
							FirstBendPoint.X = startShape.Left - Spacing;

						if (FirstBendPoint.Y >= endShape.VerticalCenter)
							LastBendPoint.Y = endShape.Bottom + Spacing;
						else
							LastBendPoint.Y = endShape.Top - Spacing;
					}
					else
					{
						FirstBendPoint.X = startShape.HorizontalCenter;
						LastBendPoint.Y = endShape.VerticalCenter;

						if (LastBendPoint.Y >= startShape.VerticalCenter)
							FirstBendPoint.Y = startShape.Bottom + Spacing;
						else
							FirstBendPoint.Y = startShape.Top - Spacing;

						if (FirstBendPoint.X >= endShape.HorizontalCenter)
							LastBendPoint.X = endShape.Right + Spacing;
						else
							LastBendPoint.X = endShape.Left - Spacing;
					}
				}
			}
			else if (FirstBendPoint.AutoPosition)
			{
				if (startOrientation == LineOrientation.Horizontal)
				{
					if (bendPoints.SecondValue.X < startShape.HorizontalCenter)
						FirstBendPoint.X = startShape.Left - Spacing;
					else
						FirstBendPoint.X = startShape.Right + Spacing;

					if (bendPoints.SecondValue.Y >= startShape.Top &&
					    bendPoints.SecondValue.Y = startShape.Left &&
						bendPoints.SecondValue.X = endShape.Top &&
						bendPoints.SecondLastValue.Y = endShape.Left &&
						bendPoints.SecondLastValue.X = nextPoint.Y)
					{
						int center = (nextPoint.Y + activePoint.Y) / 2;
						routeCache.Add(new Point(activePoint.X, center));
						routeCache.Add(new Point(nextPoint.X, center));
						routeCache.Add(nextPoint.Location);
						return FlowDirection.TopDown;
					}
					else if (nextPoint.X < activePoint.X)
					{
						if (nextNextPoint.X >= activePoint.X ||
							(nextNextPoint.Y >= nextPoint.Y &&
							 nextNextPoint.X > nextPoint.X))
						{
							routeCache.Add(new Point(nextPoint.X, activePoint.Y));
							routeCache.Add(nextPoint.Location);
							return FlowDirection.TopDown;
						}
						else
						{
							routeCache.Add(new Point(activePoint.X, nextPoint.Y));
							routeCache.Add(nextPoint.Location);
							return FlowDirection.RightToLeft;
						}
					}
					else
					{
						if (nextNextPoint.X = nextPoint.Y &&
							nextNextPoint.X < nextPoint.X))
						{
							routeCache.Add(new Point(nextPoint.X, activePoint.Y));
							routeCache.Add(nextPoint.Location);
							return FlowDirection.TopDown;
						}
						else
						{
							routeCache.Add(new Point(activePoint.X, nextPoint.Y));
							routeCache.Add(nextPoint.Location);
							return FlowDirection.LeftToRight;
						}
					}
				}
			}
			else if (direction == FlowDirection.BottomUp)
			{
				if (nextPoint.Y > activePoint.Y)
				{
					routeCache.Add(new Point(nextPoint.X, activePoint.Y));
					routeCache.Add(nextPoint.Location);
					return FlowDirection.TopDown;
				}
				else
				{
					Point nextNextPoint = GetNextNextPoint(current);

					if (current.Next.Next == null &&
						nextNextPoint.X == nextPoint.X &&
						nextNextPoint.Y  activePoint.X)
					{

						if (nextNextPoint.X = nextPoint.X)
					{
						int center = (nextPoint.X + activePoint.X) / 2;
						routeCache.Add(new Point(center, activePoint.Y));
						routeCache.Add(new Point(center, nextPoint.Y));
						routeCache.Add(nextPoint.Location);
						return FlowDirection.LeftToRight;
					}
					if (nextPoint.Y > activePoint.Y)
					{
						if (nextNextPoint.Y = nextPoint.X &&
							nextNextPoint.Y < nextPoint.Y))
						{
							routeCache.Add(new Point(activePoint.X, nextPoint.Y));
							routeCache.Add(nextPoint.Location);
							return FlowDirection.LeftToRight;
						}
						else
						{
							routeCache.Add(new Point(nextPoint.X, activePoint.Y));
							routeCache.Add(nextPoint.Location);
							return FlowDirection.TopDown;
						}
					}
					else
					{
						if (nextNextPoint.Y >= activePoint.Y ||
							(nextNextPoint.X >= nextPoint.X &&
							nextNextPoint.Y > nextPoint.Y))
						{
							routeCache.Add(new Point(activePoint.X, nextPoint.Y));
							routeCache.Add(nextPoint.Location);
							return FlowDirection.LeftToRight;
						}
						else
						{
							routeCache.Add(new Point(nextPoint.X, activePoint.Y));
							routeCache.Add(nextPoint.Location);
							return FlowDirection.BottomUp;
						}
					}
				}
			}
			else if (direction == FlowDirection.RightToLeft)
			{
				if (nextPoint.X > activePoint.X)
				{
					routeCache.Add(new Point(activePoint.X, nextPoint.Y));
					routeCache.Add(nextPoint.Location);
					return FlowDirection.LeftToRight;
				}
				else
				{
					Point nextNextPoint = GetNextNextPoint(current);

					if (current.Next.Next == null &&
						nextNextPoint.Y == nextPoint.Y &&
						nextNextPoint.X = activePoint.Y ||
							(nextNextPoint.X  nextPoint.Y))
						{
							routeCache.Add(new Point(activePoint.X, nextPoint.Y));
							routeCache.Add(nextPoint.Location);
							return FlowDirection.RightToLeft;
						}
						else
						{
							routeCache.Add(new Point(nextPoint.X, activePoint.Y));
							routeCache.Add(nextPoint.Location);
							return FlowDirection.BottomUp;
						}
					}
					else
					{
						if (nextNextPoint.Y  endShape.Bottom)
					nextNextPoint.Y = endShape.Bottom;

				return nextNextPoint;
			}
		}

		private FlowDirection AddStartSegment()
		{
			if (startOrientation == LineOrientation.Horizontal)
			{
				int startX, startY;

				if (FirstBendPoint.X < startShape.HorizontalCenter)
					startX = startShape.Left;
				else
					startX = startShape.Right;

				if (FirstBendPoint.Y >= startShape.Top &&
					FirstBendPoint.Y = startShape.Left &&
					FirstBendPoint.X = endShape.Top &&
					LastBendPoint.Y = endShape.Left &&
					LastBendPoint.X  rectangle.Bottom))
					{
						return true;
					}
				}
				else // y1 == y2
				{
					if (y1 >= rectangle.Top && y1  rectangle.Right ||
						x2 < rectangle.Left && x1 > rectangle.Right))
					{
						return true;
					}
				}
			}

			return false;
		}

		internal override void MousePressed(AbsoluteMouseEventArgs e)
		{
			if (!e.Handled)
			{
				bool pressed = Picked(e.Location, e.Zoom);

				if (e.Button == MouseButtons.Left)
					pressed |= (IsSelected && BendPointPressed(e));

				if (pressed)
				{
					e.Handled = true;
					OnMouseDown(e);
				}
			}
		}

		internal override void MouseMoved(AbsoluteMouseEventArgs e)
		{
			if (!e.Handled)
			{
				bool moved = IsMousePressed;

				if (moved)
				{
					e.Handled = true;
					OnMouseMove(e);
				}
			}
		}

		internal override void MouseUpped(AbsoluteMouseEventArgs e)
		{
			if (!e.Handled)
			{
				bool upped = IsMousePressed;

				if (upped)
				{
					e.Handled = true;
					OnMouseUp(e);
				}
			}
		}

		internal override void DoubleClicked(AbsoluteMouseEventArgs e)
		{
			bool doubleClicked = Picked(e.Location, e.Zoom);

			if (e.Button == MouseButtons.Left)
				doubleClicked |= (IsSelected && BendPointDoubleClicked(e));

			if (doubleClicked)
			{
				OnDoubleClick(e);
				e.Handled = true;
			}
		}
		
		protected override void OnMouseDown(AbsoluteMouseEventArgs e)
		{
			if (e.Button == MouseButtons.Left)
			{
				IsActive = true;
			}
			base.OnMouseDown(e);
			copied = false;
		}

		protected override void OnMouseMove(AbsoluteMouseEventArgs e)
		{
			base.OnMouseMove(e);

			//TODO: szebb lenne külön eljárásba tenni
			if (e.Button == MouseButtons.Left && selectedBendPoint != null)
			{
				Point newLocation = Point.Truncate(e.Location);

				if (selectedBendPoint.Location != newLocation)
				{
					if (!copied && Control.ModifierKeys == Keys.Control)
					{
						BendPoint newPoint = (BendPoint) selectedBendPoint.Clone();
						newPoint.Location = newLocation;
						if (selectedBendPoint.RelativeToStartShape)
							bendPoints.AddAfter(bendPoints.Find(selectedBendPoint), newPoint);
						else
							bendPoints.AddBefore(bendPoints.Find(selectedBendPoint), newPoint);
						selectedBendPoint = newPoint;
						copied = true;
						OnBendPointMove(new BendPointEventArgs(selectedBendPoint));
					}
					else
					{
						selectedBendPoint.Location = newLocation;
						OnBendPointMove(new BendPointEventArgs(selectedBendPoint));
					}

					Reroute();
					OnRouteChanged(EventArgs.Empty);
					OnModified(EventArgs.Empty);
				}
			}
		}

		public void ValidatePosition(int padding)
		{
			LinkedListNode bendPoint = bendPoints.First;
			int index = 0;

			while (bendPoint != null && index < routeCache.Count - 1)
			{
				BendPoint point = bendPoint.Value;
				while (point.Location != routeCache[index])
					index++;

				if (point.X < padding)
				{
					if (point.RelativeToStartShape)
						startShape.X += (padding - point.X);
					else
						endShape.X += (padding - point.X);
					return;
				}
				if (point.Y < padding)
				{
					if (point.RelativeToStartShape)
						startShape.Y += (padding - point.Y);
					else
						endShape.Y += (padding - point.Y);
					return;
				}

				bendPoint = bendPoint.Next;
			}
		}

		protected internal Connection Paste(Diagram diagram, Size offset,
			Shape first, Shape second)
		{
			if (CloneRelationship(diagram, first, second))
			{
				Connection connection = diagram.ConnectionList.FirstValue;
				connection.IsSelected = true;

				connection.startOrientation = this.startOrientation;
				connection.endOrientation = this.endOrientation;
				connection.bendPoints.Clear();
				foreach (BendPoint point in this.bendPoints)
				{
					Shape relativeShape = point.RelativeToStartShape ? first : second;
					BendPoint newPoint = new BendPoint(relativeShape,
						point.RelativeToStartShape, point.AutoPosition);
					newPoint.Location = point.Location + offset;
					connection.bendPoints.Add(newPoint);
				}
				connection.Reroute();

				return connection;
			}
			else
			{
				return null;
			}
		}

		protected abstract bool CloneRelationship(Diagram diagram, Shape first, Shape second);

		protected internal override IEnumerable GetContextMenuItems(Diagram diagram)
		{
			return ConnectionContextMenu.Default.GetMenuItems(diagram);
		}

		protected override void OnMouseUp(AbsoluteMouseEventArgs e)
		{
			base.OnMouseUp(e);
			selectedBendPoint = null;
		}

		protected virtual void OnRouteChanged(EventArgs e)
		{
			if (RouteChanged != null)
				RouteChanged(this, e);
		}

		protected virtual void OnBendPointMove(BendPointEventArgs e)
		{
			if (BendPointMove != null)
				BendPointMove(this, e);
		}

		protected virtual void OnSerializing(SerializeEventArgs e)
		{
			XmlDocameent docameent = e.Node.OwnerDocameent;

			XmlElement startNode = docameent.CreateElement("StartOrientation");
			startNode.InnerText = startOrientation.ToString();
			e.Node.AppendChild(startNode);

			XmlElement endNode = docameent.CreateElement("EndOrientation");
			endNode.InnerText = endOrientation.ToString();
			e.Node.AppendChild(endNode);

			foreach (BendPoint point in bendPoints)
			{
				if (!point.AutoPosition)
				{
					XmlElement node = docameent.CreateElement("BendPoint");
					node.SetAttribute("relativeToStartShape", point.RelativeToStartShape.ToString());
					point.Serialize(node);
					e.Node.AppendChild(node);
				}
			}
		}

		protected virtual void OnDeserializing(SerializeEventArgs e)
		{
			// Old file format
			XmlElement oldStartNode = e.Node["StartNode"];
			XmlElement oldEndNode = e.Node["EndNode"];
			if (oldStartNode != null && oldEndNode != null)
			{
				bool isHorizontal;
				bool.TryParse(oldStartNode.GetAttribute("isHorizontal"), out isHorizontal);
				startOrientation = (isHorizontal) ? LineOrientation.Horizontal : LineOrientation.Vertical;
				bool.TryParse(oldEndNode.GetAttribute("isHorizontal"), out isHorizontal);
				endOrientation = (isHorizontal) ? LineOrientation.Horizontal : LineOrientation.Vertical;

				int startLocation, endLocation;
				int.TryParse(oldStartNode.GetAttribute("location"), out startLocation);
				int.TryParse(oldEndNode.GetAttribute("location"), out endLocation);

				Reroute();
				if (startOrientation == LineOrientation.Vertical)
					FirstBendPoint.X = startShape.Left + startLocation;
				else
					FirstBendPoint.Y = startShape.Top + startLocation;

				if (endOrientation == LineOrientation.Vertical)
					LastBendPoint.X = endShape.Left + endLocation;
				else
					LastBendPoint.Y = endShape.Top + endLocation;

				FirstBendPoint.AutoPosition = false;
				LastBendPoint.AutoPosition = false;
				Reroute();
			}
			else
			{
				// New file format
				XmlElement startNode = e.Node["StartOrientation"];
				if (startNode != null)
				{
					if (startNode.InnerText == "Horizontal")
						startOrientation = LineOrientation.Horizontal;
					else
						startOrientation = LineOrientation.Vertical;
				}
				XmlElement endNode = e.Node["EndOrientation"];
				if (endNode != null)
				{
					if (endNode.InnerText == "Horizontal")
						endOrientation = LineOrientation.Horizontal;
					else
						endOrientation = LineOrientation.Vertical;
				}

				if (startNode != null && endNode != null) // To be sure it's the new file format
				{
					bendPoints.Clear();

					XmlNodeList nodes = e.Node.SelectNodes("child::BendPoint");
					foreach (XmlElement node in nodes)
					{
						bool relativeToStartShape;
						bool.TryParse(node.GetAttribute("relativeToStartShape"), out relativeToStartShape);
						Shape relativeShape = relativeToStartShape ? startShape : endShape;

						BendPoint point = new BendPoint(relativeShape, relativeToStartShape, false);
						point.Deserialize(node);
						bendPoints.Add(point);
					}
					if (bendPoints.Count == 0 || !FirstBendPoint.RelativeToStartShape)
						bendPoints.AddFirst(new BendPoint(startShape, true));
					if (LastBendPoint.RelativeToStartShape)
						bendPoints.Add(new BendPoint(endShape, false));
				}
				Reroute();
			}
		}

		public override string ToString()
		{
			return Relationship.ToString();
		}
	}
}