#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

662 lines
18 KiB

/*
* Created by SharpDevelop.
* User: itai
* Date: 28/09/2006
* Time: 19:03
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Xml;
using System.Xml.XPath;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Project;
using System.Globalization;
using Tools.Diagrams;
using Tools.Diagrams.Drawables;
namespace ClassDiagram
{
public class ClassCanvasItem : CanvasItem, IDisposable
{
IClass classtype;
string typeclass;
InteractiveHeaderedItem classItemHeaderedContent;
DrawableItemsStack classItemContainer = new DrawableItemsStack();
#region Graphics related variables
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
public static readonly Font TitleFont = new Font (FontFamily.GenericSansSerif, 11, FontStyle.Bold, GraphicsUnit.Pixel);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
public static readonly Font SubtextFont = new Font (FontFamily.GenericSansSerif, 11, FontStyle.Regular, GraphicsUnit.Pixel);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
public static readonly Font GroupTitleFont = new Font (FontFamily.GenericSansSerif, 11, FontStyle.Regular, GraphicsUnit.Pixel);
LinearGradientBrush grad;
GraphicsPath shadowpath;
DrawableRectangle containingShape;
#endregion
CollapseExpandShape collapseExpandShape = new CollapseExpandShape();
DrawableItemsStack titles = new DrawableItemsStack();
DrawableItemsStack titlesCollapsed = new DrawableItemsStack();
DrawableItemsStack titlesExpanded = new DrawableItemsStack();
DrawableItemsStack<InteractiveHeaderedItem> groups = new DrawableItemsStack<InteractiveHeaderedItem>();
Dictionary<InteractiveHeaderedItem, string> groupNames = new Dictionary<InteractiveHeaderedItem, string>(); // TODO - this is really an ugly patch
Dictionary<string, InteractiveHeaderedItem> groupsByName = new Dictionary<string, InteractiveHeaderedItem>(); // TODO - this is really an ugly patch
DrawableItemsStack<TextSegment> interfaces = new DrawableItemsStack<TextSegment>();
DrawableRectangle titlesBackgroundCollapsed;
DrawableRectangle titlesBackgroundExpanded;
protected override bool AllowHeightModifications()
{
return false;
}
public override float Width
{
set
{
base.Width = Math.Max (value, 100.0f);
PrepareFrame();
}
}
public override float GetAbsoluteContentWidth()
{
return classItemHeaderedContent.GetAbsoluteContentWidth();
}
public override float GetAbsoluteContentHeight()
{
return classItemHeaderedContent.GetAbsoluteContentHeight();
}
public IClass RepresentedClassType
{
get { return classtype; }
}
#region Constructors
public ClassCanvasItem (IClass ct)
{
classtype = ct;
grad = new LinearGradientBrush(
new PointF(0, 0), new PointF(1, 0),
TitleBackground, Color.White);
classItemHeaderedContent = new InteractiveHeaderedItem(titlesCollapsed, titlesExpanded, InitContentContainer(InitContent()));
classItemContainer.Container = this;
classItemContainer.Add(classItemHeaderedContent);
Pen outlinePen = GetClassOutlinePen();
if (RoundedCorners)
{
int radius = CornerRadius;
containingShape = new DrawableRectangle(null, outlinePen, radius, radius, radius, radius);
}
else
containingShape = new DrawableRectangle(null, outlinePen, 0, 0, 0, 0);
classItemContainer.Add(containingShape);
classItemContainer.OrientationAxis = Axis.Z;
if (RoundedCorners)
{
int radius = CornerRadius;
titlesBackgroundCollapsed = new DrawableRectangle(grad, null, radius, radius, radius, radius);
titlesBackgroundExpanded = new DrawableRectangle(grad, null, radius, radius, 0, 0);
}
else
{
titlesBackgroundCollapsed = new DrawableRectangle(grad, null, 0, 0, 0, 0);
titlesBackgroundExpanded = new DrawableRectangle(grad, null, 0, 0, 0, 0);
}
titles.Border = 5;
titlesCollapsed.Add(titlesBackgroundCollapsed);
titlesCollapsed.Add(titles);
titlesCollapsed.OrientationAxis = Axis.Z;
titlesExpanded.Add(titlesBackgroundExpanded);
titlesExpanded.Add(titles);
titlesExpanded.OrientationAxis = Axis.Z;
if (classtype != null)
{
typeclass = classtype.Modifiers.ToString();
typeclass += " " + classtype.ClassType.ToString();
}
}
#endregion
private Pen GetClassOutlinePen()
{
Pen pen = new Pen(Color.Gray);
if (classtype.IsAbstract)
{
pen.DashStyle = DashStyle.Dash;
}
if (classtype.IsSealed)
{
pen.Width = 3;
}
if (classtype.IsStatic)
{
pen.DashStyle = DashStyle.Dash;
pen.Width = 3;
}
return pen;
}
public override bool IsVResizable
{
get { return false; }
}
protected virtual DrawableRectangle InitContentBackground()
{
if (RoundedCorners)
{
int radius = CornerRadius;
return new DrawableRectangle(ContentBG, null, 0, 0, radius, radius);
}
else
return new DrawableRectangle(ContentBG, null, 0, 0, 0, 0);
}
protected virtual DrawableItemsStack InitContentContainer(params IDrawableRectangle[] items)
{
DrawableItemsStack content = new DrawableItemsStack();
content.OrientationAxis = Axis.Z;
content.Add(InitContentBackground());
foreach (IDrawableRectangle item in items)
content.Add(item);
return content;
}
protected virtual IDrawableRectangle InitContent ()
{
groups.MinWidth = 80;
groups.Spacing = 5;
groups.Border = 5;
return groups;
}
public void Initialize ()
{
PrepareMembersContent();
PrepareTitles();
Width = GetAbsoluteContentWidth();
}
#region Graphics related members
static Color titlesBG = Color.FromArgb(255,217, 225, 241);
protected virtual Color TitleBackground
{
get { return titlesBG; }
}
protected virtual LinearGradientBrush TitleBG
{
get { return grad; }
}
static Brush innerTitlesBG = new SolidBrush(Color.FromArgb(255, 240, 242, 249));
protected virtual Brush InnerTitlesBackground
{
get { return innerTitlesBG; }
}
static Brush contentBG = new SolidBrush(Color.FromArgb(255, 255, 255, 255));
protected virtual Brush ContentBG
{
get { return contentBG; }
}
protected virtual bool RoundedCorners
{
get { return true; }
}
protected virtual int CornerRadius
{
get { return 15; }
}
#endregion
#region Preparations
protected IAmbience GetAmbience()
{
IAmbience ambience = AmbienceService.CurrentAmbience;
ambience.ConversionFlags = ConversionFlags.None;
return ambience;
}
protected virtual void PrepareTitles ()
{
if (classtype == null) return;
IAmbience ambience = GetAmbience();
DrawableItemsStack title = new DrawableItemsStack();
title.OrientationAxis = Axis.X;
TextSegment titleString = new TextSegment(base.Graphics, classtype.Name, TitleFont, true);
title.Add(titleString);
title.Add(collapseExpandShape);
collapseExpandShape.Collapsed = Collapsed;
titles.OrientationAxis = Axis.Y;
titles.Add(title);
titles.Add(new TextSegment(base.Graphics, typeclass, SubtextFont, true));
if (classtype.BaseClass != null)
{
DrawableItemsStack inherits = new DrawableItemsStack();
inherits.OrientationAxis = Axis.X;
inherits.Add(new InheritanceShape());
inherits.Add(new TextSegment(base.Graphics, classtype.BaseClass.Name, SubtextFont, true));
titles.Add(inherits);
}
foreach (IReturnType rt in classtype.BaseTypes)
{
IClass ct = rt.GetUnderlyingClass();
if (ct != null && ct.ClassType == ClassType.Interface)
interfaces.Add(new TextSegment(base.Graphics, ambience.Convert(rt), SubtextFont, true));
}
}
protected class MemberData : IComparable<MemberData>
{
public MemberData (IMember member, IAmbience ambience, Graphics graphics, Font font)
{
IMethod methodMember = member as IMethod;
IEvent eventMember = member as IEvent;
IProperty propertyMember = member as IProperty;
IField fieldMember = member as IField;
DrawableItemsStack<VectorShape> image = new DrawableItemsStack<VectorShape>();
image.OrientationAxis = Axis.Z; // stack image components one on top of the other
image.KeepAspectRatio = true;
if (methodMember != null)
{
memberString = ambience.Convert(methodMember) + " : " + ambience.Convert(member.ReturnType);
image.Add(new MethodShape());
}
else if (eventMember != null)
{
memberString = ambience.Convert(eventMember) + " : " + ambience.Convert(member.ReturnType);
image.Add(new EventShape());
}
else if (fieldMember != null)
{
memberString = ambience.Convert(fieldMember) + " : " + ambience.Convert(member.ReturnType);
image.Add(new FieldShape());
}
else if (propertyMember != null)
{
memberString = ambience.Convert(propertyMember) + " : " + ambience.Convert(member.ReturnType);
image.Add(new PropertyShape());
}
memberItem.OrientationAxis = Axis.X;
memberItem.Add(image);
memberItem.Add(new TextSegment(graphics, memberString, font, true));
image.Border = 1;
}
InteractiveItemsStack memberItem = new InteractiveItemsStack(false);
string memberString;
public string MemberString
{
get { return memberString; }
}
public int CompareTo(MemberData other)
{
return memberString.CompareTo(other.MemberString);
}
public InteractiveItemsStack Item
{
get { return memberItem; }
}
}
protected InteractiveHeaderedItem PrepareGroup (string title, IDrawableRectangle content)
{
#region Prepare Group Container
DrawableItemsStack headerPlus = new DrawableItemsStack();
DrawableItemsStack headerMinus = new DrawableItemsStack();
headerPlus.OrientationAxis = Axis.X;
headerMinus.OrientationAxis = Axis.X;
#endregion
#region Create Header
TextSegment titleSegment = new TextSegment(Graphics, title, GroupTitleFont, true);
PlusShape plus = new PlusShape();
plus.Border = 3;
headerPlus.Add(plus);
headerPlus.Add(titleSegment);
MinusShape minus = new MinusShape();
minus.Border = 3;
headerMinus.Add(minus);
headerMinus.Add(titleSegment);
DrawableItemsStack headerCollapsed = new DrawableItemsStack();
DrawableItemsStack headerExpanded = new DrawableItemsStack();
headerCollapsed.OrientationAxis = Axis.Z;
headerExpanded.OrientationAxis = Axis.Z;
headerCollapsed.Add (new DrawableRectangle(InnerTitlesBackground, null));
headerCollapsed.Add (headerPlus);
headerExpanded.Add (new DrawableRectangle(InnerTitlesBackground, null));
headerExpanded.Add (headerMinus);
#endregion
InteractiveHeaderedItem tg = new InteractiveHeaderedItem(headerCollapsed, headerExpanded, content);
tg.HeaderClicked += delegate { tg.Collapsed = !tg.Collapsed; };
IMouseInteractable interactive = content as IMouseInteractable;
if (interactive != null)
tg.ContentClicked += delegate (object sender, PointF pos) { interactive.HandleMouseClick(pos); };
tg.RedrawNeeded += HandleRedraw;
return tg;
}
protected virtual InteractiveItemsStack PrepareMembersContent <MT> (ICollection<MT> members) where MT : IMember
{
if (members == null) return null;
if (members.Count == 0) return null;
InteractiveItemsStack content = new InteractiveItemsStack();
content.OrientationAxis = Axis.Y;
PrepareMembersContent <MT> (members, content);
return content;
}
private InteractiveItemsStack PrepareNestedTypesContent()
{
InteractiveItemsStack innerItems = new InteractiveItemsStack();
innerItems.OrientationAxis = Axis.Y;
innerItems.Spacing = 10;
innerItems.Padding = 10;
foreach (IClass ct in classtype.InnerClasses)
{
ClassCanvasItem innerItem = ClassCanvas.CreateItemFromType(ct);
innerItems.Add(innerItem);
innerItem.LayoutChanged += HandleRedraw;
}
return innerItems;
}
protected virtual void PrepareMembersContent <MT> (ICollection<MT> members, InteractiveItemsStack content) where MT : IMember
{
if (members == null) return;
if (members.Count == 0) return;
IAmbience ambience = GetAmbience();
#region Prepare Group Members
List<MemberData> membersData = new List<MemberData>();
foreach (MT member in members)
{
membersData.Add(new MemberData(member, ambience, Graphics, MemberFont));
}
membersData.Sort();
#endregion
#region Add Members To Group
foreach (MemberData memberData in membersData)
{
content.Add(memberData.Item);
}
#endregion
}
private void AddGroupToContent(string title, InteractiveItemsStack groupContent)
{
if (groupContent != null)
{
InteractiveHeaderedItem tg = PrepareGroup (title, groupContent);
groupNames.Add(tg, title);
groupsByName.Add(title, tg);
groups.Add(tg);
}
}
protected virtual void PrepareMembersContent ()
{
if (classtype == null) return;
groups.Clear();
InteractiveItemsStack propertiesContent = PrepareMembersContent <IProperty> (classtype.Properties);
InteractiveItemsStack methodsContent = PrepareMembersContent <IMethod> (classtype.Methods);
InteractiveItemsStack fieldsContent = PrepareMembersContent <IField> (classtype.Fields);
InteractiveItemsStack eventsContent = PrepareMembersContent <IEvent> (classtype.Events);
AddGroupToContent("Properties", propertiesContent);
AddGroupToContent("Methods", methodsContent);
AddGroupToContent("Fields", fieldsContent);
AddGroupToContent("Events", eventsContent);
if (classtype.InnerClasses.Count > 0)
{
InteractiveItemsStack nestedTypesContent = PrepareNestedTypesContent();
AddGroupToContent("Nested Types", nestedTypesContent);
}
}
protected virtual void PrepareFrame ()
{
ActualHeight = classItemContainer.GetAbsoluteContentHeight();
if (Container != null) return;
shadowpath = new GraphicsPath();
if (RoundedCorners)
{
int radius = CornerRadius;
shadowpath.AddArc(ActualWidth-radius + 4, 3, radius, radius, 300, 60);
shadowpath.AddArc(ActualWidth-radius + 4, ActualHeight - radius + 3, radius, radius, 0, 90);
shadowpath.AddArc(4, ActualHeight-radius + 3, radius, radius, 90, 45);
shadowpath.AddArc(ActualWidth-radius, ActualHeight - radius, radius, radius, 90, -90);
}
else
{
shadowpath.AddPolygon(new PointF[] {
new PointF(ActualWidth, 3),
new PointF(ActualWidth + 4, 3),
new PointF(ActualWidth + 4, ActualHeight + 3),
new PointF(4, ActualHeight + 3),
new PointF(4, ActualHeight),
new PointF(ActualWidth, ActualHeight)
});
}
shadowpath.CloseFigure();
}
#endregion
public override void DrawToGraphics (Graphics graphics)
{
grad.ResetTransform();
grad.TranslateTransform(AbsoluteX, AbsoluteY);
grad.ScaleTransform(ActualWidth, 1);
GraphicsState state = graphics.Save();
graphics.TranslateTransform (AbsoluteX, AbsoluteY);
if (Container == null)
{
//Draw Shadow
graphics.FillPath(CanvasItem.ShadowBrush, shadowpath);
}
classItemContainer.Width = Width;
classItemContainer.Height = Height;
graphics.Restore(state);
classItemContainer.DrawToGraphics(graphics);
//Draw interfaces lollipops
//TODO - should be converted to an headered item.
if (interfaces.Count > 0)
{
interfaces.X = AbsoluteX + 15;
interfaces.Y = AbsoluteY - interfaces.ActualHeight - 1;
interfaces.DrawToGraphics(graphics);
graphics.DrawEllipse(Pens.Black, AbsoluteX + 9, AbsoluteY - interfaces.ActualHeight - 11, 10, 10);
graphics.DrawLine(Pens.Black, AbsoluteX + 14, AbsoluteY - interfaces.ActualHeight - 1, AbsoluteX + 14, AbsoluteY);
}
base.DrawToGraphics(graphics);
}
public bool Collapsed
{
get { return classItemHeaderedContent.Collapsed; }
set
{
classItemHeaderedContent.Collapsed = value;
collapseExpandShape.Collapsed = value;
PrepareFrame();
EmitLayoutUpdate();
}
}
private void HandleRedraw (object sender, EventArgs args)
{
PrepareFrame();
EmitLayoutUpdate();
}
#region Behaviour
public override void HandleMouseClick (PointF pos)
{
base.HandleMouseClick(pos);
if (collapseExpandShape.IsInside(pos.X, pos.Y))
{
Collapsed = !Collapsed;
}
else
{
foreach (InteractiveHeaderedItem tg in groups)
{
if (tg.HitTest(pos))
{
tg.HandleMouseClick(pos);
}
}
}
}
#endregion
#region Storage
protected override XmlElement CreateXmlElement(XmlDocument doc)
{
return doc.CreateElement("Class");
}
protected override void FillXmlElement(XmlElement element, XmlDocument document)
{
base.FillXmlElement(element, document);
element.SetAttribute("Name", RepresentedClassType.FullyQualifiedName);
element.SetAttribute("Collapsed", Collapsed.ToString());
//<Compartments>
XmlElement compartments = document.CreateElement("Compartments");
foreach (InteractiveHeaderedItem tg in groups)
{
XmlElement grp = document.CreateElement("Compartment");
grp.SetAttribute("Name", groupNames[tg]);
grp.SetAttribute("Collapsed", tg.Collapsed.ToString());
compartments.AppendChild(grp);
}
element.AppendChild(compartments);
}
public override void LoadFromXml (XPathNavigator navigator)
{
base.LoadFromXml(navigator);
Collapsed = bool.Parse(navigator.GetAttribute("Collapsed", ""));
XPathNodeIterator compNI = navigator.Select("Compartments/Compartment");
while (compNI.MoveNext())
{
XPathNavigator compNav = compNI.Current;
InteractiveHeaderedItem grp;
if (groupsByName.TryGetValue(compNav.GetAttribute("Name", ""), out grp))
{
grp.Collapsed = bool.Parse(compNav.GetAttribute("Collapsed", ""));
}
}
}
#endregion
public void Dispose()
{
grad.Dispose();
if (shadowpath != null)
shadowpath.Dispose();
}
public override string ToString()
{
return "ClasCanvasItem: " + classtype.Name;
}
}
}