1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
5
using System.Collections.Generic;
6
using System.ComponentModel;
9
using System.Drawing.Drawing2D;
10
using System.Windows.Forms;
13
using System.Xml.XPath;
15
using ICSharpCode.SharpDevelop;
16
using ICSharpCode.SharpDevelop.Dom;
17
using ICSharpCode.SharpDevelop.Project;
21
namespace ClassDiagram
23
public partial class ClassCanvas
25
private class CanvasItemData : IDisposable
27
public CanvasItemData (CanvasItem item,
28
EventHandler<SizeGripEventArgs> SizeGripMouseEntered,
29
EventHandler<SizeGripEventArgs> SizeGripMouseLeft)
33
focusDecorator = new FocusDecorator(item);
34
sizeGripDecorator = new SizeGripDecorator(item);
36
sizeGripDecorator.SizeGripMouseEnter += SizeGripMouseEntered;
37
sizeGripDecorator.SizeGripMouseLeave += SizeGripMouseLeft;
39
item.AddDecorator(focusDecorator);
40
item.AddDecorator(sizeGripDecorator);
45
public CanvasItem Item
52
get { return focusDecorator.Active; }
55
focusDecorator.Active = value;
56
sizeGripDecorator.Active = value;
61
public bool JustGainedFocus
63
get { return justGainedFocus; }
64
set { justGainedFocus = value; }
67
FocusDecorator focusDecorator;
68
SizeGripDecorator sizeGripDecorator;
72
item.RemoveDecorator(focusDecorator);
73
item.RemoveDecorator(sizeGripDecorator);
77
LinkedListNode<CanvasItemData> dragItemNode;
78
LinkedListNode<CanvasItemData> hoverItemNode;
79
LinkedList<CanvasItemData> itemsList = new LinkedList<CanvasItemData>();
80
Dictionary<CanvasItem, CanvasItemData> itemsData = new Dictionary<CanvasItem, CanvasItemData>();
81
Dictionary<IClass, CanvasItemData> classesToData = new Dictionary<IClass, CanvasItemData>();
83
DiagramRouter diagramRouter = new DiagramRouter();
85
public event EventHandler ZoomChanged = delegate { };
91
PointF lastMouseClickPosition;
96
// The InitializeComponent() call is required for Windows Forms designer support.
98
InitializeComponent();
101
#region Diagram Activities
109
pictureBox1.Invalidate();
110
LayoutChanged (this, EventArgs.Empty);
111
ZoomChanged(this, EventArgs.Empty);
115
public void CollapseAll ()
117
foreach (CanvasItemData item in itemsList)
119
ClassCanvasItem classitem = item.Item as ClassCanvasItem;
120
if (classitem != null)
121
classitem.Collapsed = true;
123
LayoutChanged (this, EventArgs.Empty);
126
public void ExpandAll ()
128
foreach (CanvasItemData item in itemsList)
130
ClassCanvasItem classitem = item.Item as ClassCanvasItem;
131
if (classitem != null)
132
classitem.Collapsed = false;
134
LayoutChanged (this, EventArgs.Empty);
137
public void MatchAllWidths ()
139
foreach (CanvasItemData item in itemsList)
141
ClassCanvasItem classitem = item.Item as ClassCanvasItem;
142
if (classitem != null)
143
classitem.Width = classitem.GetAbsoluteContentWidth();
145
LayoutChanged (this, EventArgs.Empty);
148
public void ShrinkAllWidths ()
150
foreach (CanvasItemData item in itemsList)
152
ClassCanvasItem classitem = item.Item as ClassCanvasItem;
153
if (classitem != null)
156
LayoutChanged (this, EventArgs.Empty);
161
public SizeF GetDiagramLogicalSize ()
164
foreach (CanvasItemData item in itemsList)
166
w = Math.Max(w, item.Item.X + item.Item.ActualWidth + item.Item.Border);
167
h = Math.Max(h, item.Item.Y + item.Item.ActualHeight + item.Item.Border);
169
return new SizeF(w + 50, h + 50);
172
public Size GetDiagramPixelSize ()
174
float zoom = Math.Max(this.zoom, 0.1f);
175
SizeF size = GetDiagramLogicalSize();
176
return new Size((int)(size.Width * zoom), (int)(size.Height * zoom));
179
public void SetRecommendedGraphicsAttributes (Graphics graphics)
181
if (graphics == null) return;
182
graphics.CompositingQuality = CompositingQuality.HighSpeed;
183
//graphics.SmoothingMode = SmoothingMode.AntiAlias;
184
graphics.SmoothingMode = SmoothingMode.HighQuality;
185
graphics.PageUnit = GraphicsUnit.Pixel;
186
graphics.PixelOffsetMode = PixelOffsetMode.Half;
187
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
190
public void DrawToGraphics(Graphics graphics)
192
foreach (CanvasItemData item in itemsList)
193
item.Item.DrawToGraphics(graphics);
195
DrawRoutes(graphics);
198
private void PictureBox1Paint (object sender, PaintEventArgs e)
200
// System.Diagnostics.Debug.WriteLine("ClassCanvas.PictureBox1Paint");
201
Size bbox = GetDiagramPixelSize();
203
pictureBox1.Width = Math.Min(10000, bbox.Width + 100);
204
pictureBox1.Height = Math.Min(10000, bbox.Height + 100);
206
e.Graphics.PageScale = zoom;
207
SetRecommendedGraphicsAttributes(e.Graphics);
208
DrawToGraphics(e.Graphics);
211
private void DrawRoutes (Graphics g)
213
foreach (Route route in diagramRouter.Routes)
215
route.Recalc(itemsList as IEnumerable<IRectangle>);
216
PointF origin = route.GetStartPoint();
217
RouteSegment[] segments = route.RouteSegments;
218
foreach (RouteSegment rs in segments)
220
PointF dest = rs.CreateDestinationPoint(origin);
221
g.DrawLine(Pens.Black, origin, dest);
225
if (route.EndShape != null)
226
((RouteShape)route.EndShape).Draw(g, route, true);
228
if (route.StartShape != null)
229
((RouteShape)route.StartShape).Draw(g, route, false);
233
private LinkedListNode<CanvasItemData> FindCanvasItemNode (PointF pos)
235
LinkedListNode<CanvasItemData> itemNode = itemsList.Last;
236
while (itemNode != null)
238
if (itemNode.Value.Item.HitTest(pos))
242
itemNode = itemNode.Previous;
247
#region Diagram Items Drag and Selection
249
private void PictureBox1MouseClick (object sender, MouseEventArgs e)
251
PointF pos = new PointF(e.X / zoom, e.Y / zoom);
252
lastMouseClickPosition = pos;
253
LinkedListNode<CanvasItemData> itemNode = FindCanvasItemNode(pos);
254
if (itemNode != null)
256
itemNode.Value.Item.HandleMouseClick(pos);
257
if (itemNode.Value.Focused)
259
if (itemNode.Value.JustGainedFocus)
261
itemNode.Value.JustGainedFocus = false;
263
else if (itemNode.Value.Item.StartEditing())
265
Control ec = itemNode.Value.Item.GetEditingControl();
268
//TODO - refactor this damn thing... why couldn't they make the "Scale" scale the font as well?
269
ec.Scale(new SizeF(zoom, zoom));
271
ec.Font = new Font(ecf.FontFamily,
273
ecf.Style, ec.Font.Unit,
274
ecf.GdiCharSet, ec.Font.GdiVerticalFont);
276
ec.VisibleChanged += delegate { if (!ec.Visible) ec.Font = ecf; };
277
panel1.Controls.Add(ec);
278
ec.Top -= panel1.VerticalScroll.Value;
279
ec.Left -= panel1.HorizontalScroll.Value;
281
panel1.Controls.SetChildIndex(ec, 0);
282
this.ActiveControl = ec;
290
private void PictureBox1MouseDown (object sender, MouseEventArgs e)
293
PointF pos = new PointF(e.X / zoom, e.Y / zoom);
294
LinkedListNode<CanvasItemData> itemNode = FindCanvasItemNode(pos);
295
dragItemNode = itemNode;
299
foreach (CanvasItemData item in itemsList)
301
item.Item.StopEditing();
302
if (itemNode == null || item != itemNode.Value)
303
item.Focused = false;
307
if (itemNode != null)
309
if (!itemNode.Value.Focused)
311
itemNode.Value.JustGainedFocus = true;
312
itemNode.Value.Focused = true;
313
itemsList.Remove(itemNode);
314
itemsList.AddLast(itemNode);
315
CanvasItemSelected (this, new CanvasItemEventArgs (itemNode.Value.Item));
317
itemNode.Value.Item.HandleMouseDown(pos);
322
private void PictureBox1MouseMove (object sender, MouseEventArgs e)
325
PointF pos = new PointF(e.X / zoom, e.Y / zoom);
326
if (dragItemNode != null)
327
dragItemNode.Value.Item.HandleMouseMove(pos);
330
LinkedListNode<CanvasItemData> itemNode = FindCanvasItemNode(pos);
331
if (hoverItemNode != itemNode)
333
if (hoverItemNode != null && hoverItemNode.Value != null)
334
hoverItemNode.Value.Item.HandleMouseLeave();
335
hoverItemNode = itemNode;
338
if (itemNode != null)
339
itemNode.Value.Item.HandleMouseMove(pos);
344
private void PictureBox1MouseUp (object sender, MouseEventArgs e)
346
PointF pos = new PointF(e.X / zoom, e.Y / zoom);
348
if (dragItemNode != null)
349
dragItemNode.Value.Item.HandleMouseUp(pos);
352
LinkedListNode<CanvasItemData> itemNode = FindCanvasItemNode(pos);
353
if (itemNode != null)
354
itemNode.Value.Item.HandleMouseUp(pos);
359
private bool HoldRedraw
361
get { return holdRedraw; }
365
if (!value && redrawNeeded)
367
redrawNeeded = false;
368
HandleRedraw (this, EventArgs.Empty);
373
private void HandleItemLayoutChange (object sender, EventArgs args)
375
LayoutChanged (this, args);
379
HandleRedraw (sender, args);
382
private void HandleRedraw (object sender, EventArgs args)
389
// System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace();
390
// System.Diagnostics.Debug.WriteLine(st.ToString());
391
this.Invalidate(true);
394
private void HandleItemPositionChange (object sender, ValueChangingEventArgs<PointF> args)
396
PointF pos = new PointF(args.Value.X, args.Value.Y);
398
pos.X = Math.Max ((float) Math.Round(pos.X / 10.0f) * 10.0f, 50.0f);
399
pos.Y = Math.Max ((float) Math.Round(pos.Y / 10.0f) * 10.0f, 50.0f);
401
args.Cancel = (pos.X == args.Value.X) && (pos.Y == args.Value.Y);
405
private void HandleItemSizeChange (object sender, ValueChangingEventArgs<SizeF> args)
407
SizeF size = new SizeF(args.Value);
409
size.Width = (float) Math.Round(size.Width / 10.0f) * 10.0f;
410
size.Height = (float) Math.Round(size.Height / 10.0f) * 10.0f;
412
args.Cancel = (size.Width == args.Value.Width) && (size.Height == args.Value.Height);
416
private void SizeGripMouseEntered (object sender, SizeGripEventArgs e)
418
if ((e.GripPosition & SizeGripPositions.EastWest) != SizeGripPositions.None)
420
pictureBox1.Cursor = Cursors.SizeWE;
422
else if ((e.GripPosition & SizeGripPositions.NorthSouth) != SizeGripPositions.None)
424
pictureBox1.Cursor = Cursors.SizeNS;
428
private void SizeGripMouseLeft (object sender, SizeGripEventArgs e)
430
pictureBox1.Cursor = Cursors.Default;
433
public void AddCanvasItem (CanvasItem item)
435
diagramRouter.AddItem(item);
436
CanvasItemData itemData = new CanvasItemData(item, SizeGripMouseEntered, SizeGripMouseLeft);
437
itemsData[item] = itemData;
439
ClassCanvasItem classItem = item as ClassCanvasItem;
440
if (classItem != null)
442
classesToData.Add(classItem.RepresentedClassType, itemData);
443
foreach (CanvasItemData ci in itemsList)
445
ClassCanvasItem cci = ci.Item as ClassCanvasItem;
449
if (cci.RepresentedClassType == classItem.RepresentedClassType.BaseClass)
450
r = diagramRouter.AddRoute(item, cci);
451
else if (classItem.RepresentedClassType == cci.RepresentedClassType.BaseClass)
452
r = diagramRouter.AddRoute(cci, classItem);
455
r.EndShape = new RouteInheritanceShape();
460
itemsList.AddLast(itemData);
461
item.RedrawNeeded += HandleRedraw;
462
item.LayoutChanged += HandleItemLayoutChange;
463
item.PositionChanging += HandleItemPositionChange;
464
item.SizeChanging += HandleItemSizeChange;
467
public void RemoveCanvasItem (CanvasItem item)
469
itemsList.Remove(itemsData[item]);
470
Stack<Route> routesToRemove = new Stack<Route>();
471
foreach (Route r in diagramRouter.Routes)
473
if (r.From == item || r.To == item)
474
routesToRemove.Push(r);
477
foreach (Route r in routesToRemove)
478
diagramRouter.RemoveRoute(r);
480
diagramRouter.RemoveItem (item);
482
ClassCanvasItem classItem = item as ClassCanvasItem;
483
if (classItem != null)
485
classesToData.Remove (classItem.RepresentedClassType);
488
LayoutChanged(this, EventArgs.Empty);
491
public void ClearCanvas()
494
classesToData.Clear();
497
hoverItemNode = null;
498
diagramRouter.Clear();
502
/// Retruns a copy of the the canvas items list as an array.
504
public CanvasItem[] GetCanvasItems()
506
CanvasItem[] items = new CanvasItem[itemsList.Count];
508
foreach (CanvasItemData item in itemsList)
509
items[i++] = item.Item;
513
public bool Contains (IClass ct)
515
return classesToData.ContainsKey(ct);
517
foreach (CanvasItemData ci in itemsList)
519
ClassCanvasItem cci = ci.Item as ClassCanvasItem;
521
if (cci.RepresentedClassType.Equals(ct)) return true;
527
public void AutoArrange ()
529
diagramRouter.RecalcPositions();
532
public static ClassCanvasItem CreateItemFromType (IClass ct)
534
if (ct == null) return null;
535
ClassCanvasItem item = null;
536
if (ct.ClassType == ClassType.Interface)
537
item = new InterfaceCanvasItem(ct);
538
else if (ct.ClassType == ClassType.Enum)
539
item = new EnumCanvasItem(ct);
540
else if (ct.ClassType == ClassType.Struct)
541
item = new StructCanvasItem(ct);
542
else if (ct.ClassType == ClassType.Delegate)
543
item = new DelegateCanvasItem(ct);
545
item = new ClassCanvasItem(ct);
550
#region File Save/Load
552
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes", MessageId = "System.Xml.XmlNode")]
553
public XmlDocument WriteToXml ()
555
XmlDocument doc = new XmlDocument();
556
doc.LoadXml("<ClassDiagram/>");
558
XmlDeclaration decl = doc.CreateXmlDeclaration("1.0", "utf-8", "yes");
559
doc.InsertBefore(decl, doc.FirstChild);
561
XmlAttribute zoom = doc.CreateAttribute("Zoom"); // Non-Standard attribute
562
zoom.Value = Zoom.ToString(System.Globalization.CultureInfo.InvariantCulture);
563
doc.DocumentElement.Attributes.Append(zoom);
565
#region unsupported attributes - added for compatability
566
// FIXME - Attribute not yet supported
567
XmlAttribute majorVersion = doc.CreateAttribute("MajorVersion");
568
majorVersion.Value = "1";
569
doc.DocumentElement.Attributes.Append(majorVersion);
571
// FIXME - Attribute not yet supported
572
XmlAttribute minorVersion = doc.CreateAttribute("MinorVersion");
573
minorVersion.Value = "1";
574
doc.DocumentElement.Attributes.Append(minorVersion);
576
// FIXME - Attribute not yet supported
577
XmlAttribute membersFormat = doc.CreateAttribute("MembersFormat");
578
membersFormat.Value = "FullSignature";
579
doc.DocumentElement.Attributes.Append(membersFormat);
581
// FIXME - Element not yet supported
582
XmlAttribute fontName = doc.CreateAttribute("Name");
583
fontName.Value = "Tahoma";
585
XmlAttribute fontSize = doc.CreateAttribute("Size");
586
fontSize.Value = "8.25";
588
XmlElement fontElement = doc.CreateElement("Font");
589
fontElement.Attributes.Append(fontName);
590
fontElement.Attributes.Append(fontSize);
593
foreach (CanvasItemData item in itemsList)
595
item.Item.WriteToXml(doc);
600
public void LoadFromXml (IXPathNavigable doc, IProjectContent pc)
602
if (pc == null) return;
603
if (doc == null) return;
606
XPathNavigator nav = doc.CreateNavigator();
607
XPathNodeIterator ni = nav.Select(@"/ClassDiagram/Class | /ClassDiagram/Struct | /ClassDiagram/Enum | /ClassDiagram/Interface | /ClassDiagram/Delegate");
608
while (ni.MoveNext())
610
string typeName = ni.Current.GetAttribute("Name", "");
611
IClass ct = pc.GetClass(typeName, 0);
612
ClassCanvasItem canvasitem = ClassCanvas.CreateItemFromType(ct);
613
if (canvasitem != null)
615
canvasitem.LoadFromXml (ni.Current);
616
AddCanvasItem(canvasitem);
619
ni = nav.Select(@"/ClassDiagram/Comment");
620
while (ni.MoveNext())
622
NoteCanvasItem note = new NoteCanvasItem();
623
note.LoadFromXml(ni.Current);
630
public event EventHandler LayoutChanged = delegate {};
631
public event EventHandler<CanvasItemEventArgs> CanvasItemSelected = delegate {};
633
public Bitmap GetAsBitmap ()
635
Size bbox = GetDiagramPixelSize();
636
Bitmap bitmap = new Bitmap(bbox.Width, bbox.Height);
637
Graphics g = Graphics.FromImage(bitmap);
639
SetRecommendedGraphicsAttributes(g);
644
public void SaveToImage (string filename)
646
GetAsBitmap().Save(filename);
649
public PointF LastMouseClickPosition
651
get { return lastMouseClickPosition; }
654
#region Drag/Drop from Class Browser Handling
656
private void ClassCanvasDragOver(object sender, DragEventArgs e)
658
e.Effect = DragDropEffects.Link;
661
private void ClassCanvasDragDrop(object sender, DragEventArgs e)
668
void ClassCanvasKeyDown(object sender, KeyEventArgs e)
670
if (e.KeyCode == Keys.Control)
674
void ClassCanvasKeyUp(object sender, KeyEventArgs e)
676
if (e.KeyCode == Keys.Control)
681
public class CanvasItemEventArgs : EventArgs
683
public CanvasItemEventArgs (CanvasItem canvasItem)
685
this.canvasItem = canvasItem;
688
private CanvasItem canvasItem;
690
public CanvasItem CanvasItem
692
get { return canvasItem; }
696
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
697
public enum TestEnum {Dog, Cat, Fish};