context: // EnClass - Free class 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.Drawing;
  using EnClass.Core;
  using EnClass.DiagramEditor.ClassDiagram.Dialogs;
  using EnClass.DiagramEditor.ClassDiagram.Editors;
  
  namespace EnClass.DiagramEditor.ClassDiagram.Shapes
  {
  	public abstract class CompositeTypeShape : TypeShape
  	{
  		const int AccessSpacing = 12;
  
  		static CompositeTypeEditor typeEditor = new CompositeTypeEditor();
  		static MemberEditor memberEditor = new MemberEditor();
  		static MembersDialog membersDialog = new MembersDialog();
  		static SolidBrush memberBrush = new SolidBrush(Color.Black);
  		static StringFormat accessFormat = new StringFormat(StringFormat.GenericTypographic);
  		static Pen selectionPen = new Pen(Color.Black);
  
  		static CompositeTypeShape()
  		{
  			accessFormat.Alignment = StringAlignment.Center;
  			accessFormat.LineAlignment = StringAlignment.Center;
  			selectionPen.DashPattern = new float[] { 2, 4 };
  		}
  
  		/// <exception cref="ArgumentNullException">
  		/// <paramref name="compositeType"/> is null.
  		/// </exception>
  		protected CompositeTypeShape(CompositeType compositeType)
  			: base(compositeType)
  		{
  		}
  
  		public sealed override TypeBase TypeBase
  		{
  			get { return CompositeType; }
  		}
  
  		public abstract CompositeType CompositeType { get; }
  
  		protected override TypeEditor HeaderEditor
  		{
  			get { return typeEditor; }
  		}
  
  		protected override EditorWindow ContentEditor
  		{
  			get { return memberEditor; }
  		}
  
  		protected internal Member ActiveMember
  		{
  			get
  			{
  				if (ActiveMemberIndex >= 0 && ActiveMemberIndex < CompositeType.FieldCount)
  				{
  					return CompositeType.GetField(ActiveMemberIndex);
  				}
  				else if (ActiveMemberIndex >= CompositeType.FieldCount)
  				{
  					return CompositeType.GetOperation(ActiveMemberIndex - CompositeType.FieldCount);
  				}
  				else
  				{
  					return null;
  				}
  			}
  		}
  
  		protected internal override int ActiveMemberIndex
  		{
  			get
  			{
  				return base.ActiveMemberIndex;
  			}
  			set
  			{
  				Member oldMember = ActiveMember;
  
  				if (value < CompositeType.MemberCount)
  					base.ActiveMemberIndex = value;
  				else
  					base.ActiveMemberIndex = CompositeType.MemberCount - 1;
  
  				if (oldMember != ActiveMember)
  					OnActiveMemberChanged(EventArgs.Empty);
  			}
  		}
  
  		public override void MoveUp()
  		{
  			if (ActiveMember != null && CompositeType.MoveUpItem(ActiveMember))
  			{
  				ActiveMemberIndex--;
  			}
  		}
  
  		public override void MoveDown()
  		{
  			if (ActiveMember != null && CompositeType.MoveDownItem(ActiveMember))
  			{
  				ActiveMemberIndex++;
  			}
  		}
  
  		protected internal override void EditMembers()
  		{
  			membersDialog.ShowDialog(CompositeType);
  		}
  
  		protected override EditorWindow GetEditorWindow()
  		{
  			if (IsActive)
  			{
  				if (ActiveMember == null)
  					return HeaderEditor;
  				else
  					return memberEditor;
  			}
  			else
  			{
  				return null;
  			}
  		}
  
  		protected internal sealed override bool DeleteSelectedMember(bool showConfirmation)
  		{
  			if (IsActive && ActiveMember != null)
  			{
  				if (!showConfirmation || ConfirmMemberDelete())
  					DeleteActiveMember();
  				return true;
  			}
  			else
  			{
  				return false;
  			}
  		}
  
  		protected override void OnMouseDown(AbsoluteMouseEventArgs e)
  		{
  			base.OnMouseDown(e);
  			SelectMember(e.Location);
  		}
  
  		internal Rectangle GetMemberRectangle(int memberIndex)
  		{
  			Rectangle record = new Rectangle(
  				Left + MarginSize, Top + HeaderHeight + MarginSize,
  				Width - MarginSize * 2, MemberHeight);
  
  			record.Y += memberIndex * MemberHeight;
  			if (CompositeType.SupportsFields && memberIndex >= CompositeType.FieldCount)
  			{
  				record.Y += MarginSize * 2;
  			}
  			return record;
  		}
  
  		private void SelectMember(PointF location)
  		{
  			if (Contains(location))
  			{
  				int index;
  				int y = (int) location.Y;
  				int top = Top + HeaderHeight + MarginSize;
  
  				if (top <= y)
  				{
  					if (CompositeType.SupportsFields)
  					{
  						index = (y - top) / MemberHeight;
  						if (index < CompositeType.FieldCount)
  						{
  							ActiveMemberIndex = index;
  							return;
  						}
  						top += MarginSize * 2;
  					}
  
  					int operationTop = top + CompositeType.FieldCount * MemberHeight;
  					if (operationTop <= y)
  					{
  						index = (y - top) / MemberHeight;
  						if (index < CompositeType.MemberCount)
  						{
  							ActiveMemberIndex = index;
  							return;
  						}
  					}
  				}
  				ActiveMemberIndex = -1;
  			}
  		}
  
  		internal void DeleteActiveMember()
  		{
  			if (ActiveMemberIndex >= 0)
  			{
  				int newIndex;
  				int fieldCount = CompositeType.FieldCount;
  				int memberCount = CompositeType.MemberCount;
  
  				if (ActiveMemberIndex == fieldCount - 1 && fieldCount >= 2) // Last field
  				{
  					newIndex = fieldCount - 2;
  				}
  				else if (ActiveMemberIndex == memberCount - 1) // Last member
  				{
  					newIndex = ActiveMemberIndex - 1;
  				}
  				else
  				{
  					newIndex = ActiveMemberIndex;
  				}
  				
  				CompositeType.RemoveMember(ActiveMember);
  				ActiveMemberIndex = newIndex;
  				OnActiveMemberChanged(EventArgs.Empty);
  			}
  		}
  
  		internal void InsertNewMember(MemberType type)
  		{
  			int fieldCount = CompositeType.FieldCount;
  			switch (type)
  			{
  				case MemberType.Field:
  					if (CompositeType.SupportsFields)
  					{
  						int index = Math.Min(ActiveMemberIndex + 1, fieldCount);
  						bool changing = (index == fieldCount &&
  							ActiveMember.MemberType != MemberType.Field);
  
  						CompositeType.InsertMember(MemberType.Field, index);
  						ActiveMemberIndex = index;
  					}
  					break;
  
  				case MemberType.Method:
  					if (CompositeType.SupportsMethods)
  					{
  						int index = Math.Max(ActiveMemberIndex + 1, fieldCount);
  						CompositeType.InsertMember(MemberType.Method, index);
  						ActiveMemberIndex = index;
  					}
  					break;
  
  				case MemberType.Constructor:
  					if (CompositeType.SupportsConstuctors)
  					{
  						int index = Math.Max(ActiveMemberIndex + 1, fieldCount);
  						CompositeType.InsertMember(MemberType.Constructor, index);
  						ActiveMemberIndex = index;
  					}
  					break;
  
  				case MemberType.Destructor:
  					if (CompositeType.SupportsDestructors)
  					{
  						int index = Math.Max(ActiveMemberIndex + 1, fieldCount);
  						CompositeType.InsertMember(MemberType.Destructor, index);
  						ActiveMemberIndex = index;
  					}
  					break;
  
  				case MemberType.Property:
  					if (CompositeType.SupportsProperties)
  					{
  						int index = Math.Max(ActiveMemberIndex + 1, fieldCount);
  						CompositeType.InsertMember(MemberType.Property, index);
  						ActiveMemberIndex = index;
  					}
  					break;
  
  				case MemberType.Event:
  					if (CompositeType.SupportsEvents)
  					{
  						int index = Math.Max(ActiveMemberIndex + 1, fieldCount);
  						CompositeType.InsertMember(MemberType.Event, index);
  						ActiveMemberIndex = index;
  					}
  					break;
  			}
  		}
  
  		private static string GetAccessString(Member member)
  		{
  			switch (member.Access)
  			{
  				case AccessModifier.Public:
  					return "+";
  
  				case AccessModifier.Internal:
  					return "~";
  
  				case AccessModifier.ProtectedInternal:
  				case AccessModifier.Protected:
  					return "#";
  
  				case AccessModifier.Private:
  				default:
  					return "-";
  			}
  		}
  
  		private static string GetMemberString(Member member)
  		{
  			return member.GetUmlDescription(
  				Settings.Default.ShowType,
  				Settings.Default.ShowParameters,
  				Settings.Default.ShowParameterNames,
  				Settings.Default.ShowInitialValue);
  		}
  
  		private Font GetMemberFont(Member member, Style style)
  		{
  			Font memberFont;
  			if (member.IsStatic)
  			{
  				memberFont = style.StaticMemberFont;
  			}
  			else if (member is Operation &&
  				(((Operation) member).IsAbstract || member.Parent is InterfaceType))
  			{
  				memberFont = style.AbstractMemberFont;
  			}
  			else
  			{
  				memberFont = GetFont(style);
  			}
  
  			return memberFont;
  		}
  
  		private void DrawMember(IGraphics g, Member member, Rectangle record, Style style)
  		{
  			Font memberFont = GetMemberFont(member, style);
  
  			if (member is Field)
  				memberBrush.Color = style.AttributeColor;
  			else
  				memberBrush.Color = style.OperationColor;
  
  			if (style.UseIcons)
  			{
  				Image icon = Icons.GetImage(member);
  				g.DrawImage(icon, record.X, record.Y);
  
  				Rectangle textBounds = new Rectangle(
  					record.X + IconSpacing, record.Y,
  					record.Width - IconSpacing, record.Height);
  
  				string memberString = GetMemberString(member);
  				g.DrawString(memberString, memberFont, memberBrush, textBounds, memberFormat);
  			}
  			else
  			{
  				Rectangle accessBounds = new Rectangle(
  					record.X, record.Y, AccessSpacing, record.Height);
  				Rectangle textBounds = new Rectangle(
  					record.X + AccessSpacing, record.Y,
  					record.Width - AccessSpacing, record.Height);
  
  				g.DrawString(GetAccessString(member), GetFont(style),
  					memberBrush, accessBounds, accessFormat);
  				g.DrawString(GetMemberString(member), memberFont,
  					memberBrush, textBounds, memberFormat);
  			}
  		}
  
  		protected internal override void DrawSelectionLines(Graphics g, float zoom, Point offset)
  		{
  			base.DrawSelectionLines(g, zoom, offset);
  			
  			// Draw selected member rectangle
  			if (IsActive && ActiveMember != null)
  			{
  				Rectangle record = GetMemberRectangle(ActiveMemberIndex);
  				record = TransformRelativeToAbsolute(record, zoom, offset);
  				record.Inflate(2, 0);
  				g.DrawRectangle(Diagram.SelectionPen, record);
  			}
  		}
  
  		protected override void DrawContent(IGraphics g, Style style)
  		{
  			Rectangle record = new Rectangle(
  				Left + MarginSize, Top + HeaderHeight + MarginSize,
  				Width - MarginSize * 2, MemberHeight);
  
  			// Draw fields
  			foreach (Field field in CompositeType.Fields)
  			{
  				DrawMember(g, field, record, style);
  				record.Y += MemberHeight;
  			}
  
  			//Draw separator line 
  			if (CompositeType.SupportsFields)
  			{
  				DrawSeparatorLine(g, record.Top + MarginSize);
  				record.Y += MarginSize * 2;
  			}
  
  			// Draw operations
  			foreach (Operation operation in CompositeType.Operations)
  			{
  				DrawMember(g, operation, record, style);
  				record.Y += MemberHeight;
  			}
  		}
  
  		protected override float GetRequiredWidth(Graphics g, Style style)
  		{
  			float requiredWidth = 0;
  
  			foreach (Field field in CompositeType.Fields)
  			{
  				float fieldWidth = g.MeasureString(GetMemberString(field),
  					GetMemberFont(field, style), PointF.Empty, memberFormat).Width;
  				requiredWidth = Math.Max(requiredWidth, fieldWidth);
  			}
  			foreach (Operation operation in CompositeType.Operations)
  			{
  				float operationWidth = g.MeasureString(GetMemberString(operation),
  					GetMemberFont(operation, style), PointF.Empty, memberFormat).Width;
  				requiredWidth = Math.Max(requiredWidth, operationWidth);
  			}
  			requiredWidth += (style.UseIcons) ? IconSpacing : AccessSpacing;
  			requiredWidth += MarginSize * 2;
  
  			return Math.Max(requiredWidth, base.GetRequiredWidth(g, style));
  		}
  
  		protected override int GetRequiredHeight()
  		{
  			int memberCount = 0;
  			int spacingHeight = 0;
  
  			if (CompositeType.SupportsFields)
  			{
  				memberCount += CompositeType.FieldCount;
  
gt: 				spacingHeight += MarginSize * 2;