private void AddChildNodes(ShapeBase currentShape, Parse[] childParses) { foreach (Parse childParse in childParses) { // if this is not a token node (token node = one of the words of the sentence) if (childParse.Type != MaximumEntropyParser.TokenNode) { ShapeBase childShape = currentShape.AddChild(childParse.Type); if (childParse.IsPosTag) { childShape.ShapeColor = Color.DarkGoldenrod; } else { childShape.ShapeColor = Color.SteelBlue; } AddChildNodes(childShape, childParse.GetChildren()); childShape.Expand(); } else { Span parseSpan = childParse.Span; string token = childParse.Text.Substring(parseSpan.Start, (parseSpan.End) - (parseSpan.Start)); ShapeBase childShape = currentShape.AddChild(token); childShape.ShapeColor = Color.Ivory; } } }
/// <summary> /// Adds a shape to the collection /// </summary> /// <param name="shape">a ShapeBase object</param> /// <returns>the index of the added object in the collection</returns> public int Add(ShapeBase shape) { int newid = this.InnerList.Add(shape); if(OnShapeAdded!=null) OnShapeAdded(shape); return newid; }
/// <summary> /// Default ctor /// </summary> /// <param name="child"></param> /// <param name="parent"></param> public ParentChild( ShapeBase child, string parent) { this.Parent = parent; this.ChildShape = child; //this.ParentShape = null; //this.ChildShape = null; }
/// <summary> /// Visits the shape /// </summary> /// <param name="sh"></param> public void Visit(ShapeBase sh) { //remove from the shape from the Shapes if(sh==null) return; //strictly speaking we should remove the shape from its parent's childNodes collection first graphAbstract.Connections.Remove(sh.connection); graphAbstract.Shapes.Remove(sh); if(OnDelete!=null) OnDelete(sh); }
/// <summary> /// Removes a connection from the collection /// </summary> /// <param name="one">the 'from' or 'to' (ShapeBase) part of the connection</param> /// <param name="two">the complementary 'from' or 'to' (ShapeBase) part of the connection</param> public void Remove(ShapeBase one, ShapeBase two) { for(int k=0; k<InnerList.Count; k++) { if((this[k].From ==one && this[k].To==two) || (this[k].From ==two && this[k].To==one) ) { this.InnerList.RemoveAt(k); break; } } }
/// <summary> /// Constructor /// </summary> /// <param name="from">the shape where the connection starts</param> /// <param name="to">the shape where the connection ends</param> public Connection(ShapeBase from, ShapeBase to) { this.from = from; this.to = to; currentPen = blackPen; }
private void Pickup(ShapeBase shape) { shape.pickup=true; for(int k =0; k< shape.childNodes.Count; k++) { shape.childNodes[k].pickup = true; if(shape.childNodes[k].childNodes.Count>0) Pickup(shape.childNodes[k]); } }
/// <summary> /// Handles the mouse-down event /// </summary> /// <param name="e"></param> protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); Point p = new Point(e.X, e.Y); Rectangle r; #region SHIFT if (Control.ModifierKeys == Keys.Shift) { globalMove = true; refp = p; //useful for all kind of things return; } #endregion ShapeBase sh; #region LMB & RMB //test for shapes for (int k = 0; k < Shapes.Count; k++) { sh = Shapes[k]; if (sh.childNodes.Count > 0) //has a [+/-] { if (layoutDirection == TreeDirection.Vertical) { r = new Rectangle(sh.Left + sh.Width / 2 - 5, sh.Bottom, 10, 10); } else { r = new Rectangle(sh.Right, sh.Y + sh.Height / 2 - 5, 10, 10); } if (r.Contains(p)) { if (sh.expanded) { sh.Collapse(true); } else { sh.Expand(); } DrawTree(); } } if (Shapes[k].Hit(p)) { //shapes[k].ShapeColor = Color.WhiteSmoke; if (selectedEntity != null) { selectedEntity.IsSelected = false; } selectedEntity = Shapes[k]; selectedEntity.IsSelected = true; sh = selectedEntity as ShapeBase; #region CONTROL if (Control.ModifierKeys == Keys.Control && !sh.IsRoot) { tracking = true; //remove from parent sh.parentNode.childNodes.Remove(sh); Connections.Remove(sh, sh.parentNode); //...but keep the reference in case the user didn't find a new location memChild = sh; memParent = sh.parentNode; //now remove the reference sh.parentNode = null; } #endregion //set the point for the next round refp = p; #region Double-click if (e.Button == MouseButtons.Left && e.Clicks == 2) { if (sh.expanded) { sh.Collapse(true); } else { sh.Expand(); } DrawTree(); } #endregion #region ALT if (Control.ModifierKeys == Keys.Alt) { sh.AddChild("New"); DrawTree(); } #endregion if (OnShowProps != null) { OnShowProps(Shapes[k]); } return; } } if (selectedEntity != null) { selectedEntity.IsSelected = false; } selectedEntity = null; Invalidate(); refp = p; //useful for all kind of things //nothing was selected but we'll show the props of the control in this case if (OnShowProps != null) { OnShowProps(this.proxy); } #endregion }
/// <summary> /// Adds a shape to the canvas or diagram /// </summary> /// <param name="shape"></param> public ShapeBase AddShape(ShapeBase shape) { Shapes.Add(shape); shape.Site = this; this.Invalidate(); return shape; }
/// <summary> /// Find a new parent for the given shape. This creates a new volatile connection which will be solidified /// in the MouseUp handler. /// </summary> /// <param name="shape">the shape being moved around by the user</param> private void SeekNewParent(ShapeBase shape) { /* this is the fast way but gives problems when the shape is surrounded by other shapes * which makes it difficult to attach it to one you want for(int k=0;k<Shapes.Count; k++) { if(Shapes[k]!=shape && Environment(Shapes[k],shape) && Shapes[k].parentNode!=shape && !Shapes[k].pickup) { neoCon = new Connection(shape, Shapes[k], Color.Red,2f); neoCon.visible = true; Invalidate(); return; } } */ double best = 10000d; int chosen = -1; double dist; ShapeBase other; for(int k=0;k<Shapes.Count; k++) { other = Shapes[k]; if(other!=shape && other.visible && other.parentNode!=shape && !other.pickup) { dist = Math.Sqrt((other.X-shape.X)*(other.X-shape.X)+(other.Y-shape.Y)*(other.Y-shape.Y)); if(dist<best && dist< 120) chosen = k; } } if(chosen>-1) { neoCon = new Connection(shape, Shapes[chosen], Color.Red,2f); neoCon.visible = true; neoCon.site = this; return; } neoCon = null; }
/// <summary> /// Resizes the shape to fit the text /// </summary> /// <param name="shape"></param> public void Fit(ShapeBase shape) { Graphics g = Graphics.FromHwnd(this.Handle); Size s = Size.Round(g.MeasureString(shape.Text,Font)); shape.Width =s.Width +20; shape.Height = s.Height+8; }
private void visitor_OnDelete(ShapeBase shape) { if(OnDeleteNode!=null) OnDeleteNode(shape); }
/// <summary> /// Handles the mouse-move event /// </summary> /// <param name="e"></param> protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); Point p = new Point(e.X - this.AutoScrollPosition.X, e.Y - this.AutoScrollPosition.Y); //move the whole diagram if (globalMove) { foreach (ShapeBase shape in Shapes) { shape.Move(new Point(p.X - refp.X, p.Y - refp.Y)); Invalidate(); } refp = p; return; } //move just one and its kids if (tracking) { ShapeBase sh = selectedEntity as ShapeBase; ResetPickup(); //forget about what happened before Pickup(sh); //pickup the shape hanging underneath the shape to move next foreach (ShapeBase shape in Shapes) { if (!shape.pickup) { continue; } shape.Move(new Point(p.X - refp.X, p.Y - refp.Y)); Invalidate(); } refp = p; //try to find the new parent SeekNewParent(sh); Invalidate(); return; } //hovering stuff for (int k = 0; k < Shapes.Count; k++) { if (Shapes[k].Hit(p)) { if (hoveredEntity != null) { hoveredEntity.hovered = false; } Shapes[k].hovered = true; hoveredEntity = Shapes[k]; //hoveredEntity.Invalidate(); Invalidate(); return; } } for (int k = 0; k < Connections.Count; k++) { if (Connections[k].Hit(p)) { if (hoveredEntity != null) { hoveredEntity.hovered = false; } Connections[k].hovered = true; hoveredEntity = Connections[k]; hoveredEntity.Invalidate(); Invalidate(); return; } } //reset the whole process if nothing happened above HoverNone(); Invalidate(); }
private bool Environment(ShapeBase shape1, ShapeBase shape2) { return(Math.Sqrt((shape1.X - shape2.X) * (shape1.X - shape2.X) + (shape1.Y - shape2.Y) * (shape1.Y - shape2.Y)) < 100); }
/// <summary> /// Positions everything underneath the node and returns the total width of the kids /// </summary> /// <param name="containerNode"></param> /// <param name="first"></param> /// <param name="shiftLeft"></param> /// <param name="shiftTop"></param> /// <returns></returns> private int VerticalDrawTree(ShapeBase containerNode, bool first, int shiftLeft, int shiftTop) { bool isFirst = false; bool isParent = containerNode.childNodes.Count > 0? true: false; int childrenWidth = 0; int thisX, thisY; int returned = 0; int verticalDelta = branchHeight; //the applied vertical shift of the child depends on the Height of the containerNode #region Children width for (int i = 0; i < containerNode.childNodes.Count; i++) { //determine the width of the label if (i == 0) { isFirst = true; } else { isFirst = false; } if (containerNode.childNodes[i].visible) { if ((branchHeight - containerNode.Height) < 30) //if too close to the child, shift it with 40 units { verticalDelta = containerNode.Height + 40; } returned = VerticalDrawTree(containerNode.childNodes[i], isFirst, shiftLeft + childrenWidth, shiftTop + verticalDelta); childrenWidth += returned; } } if (childrenWidth > 0 && containerNode.expanded) { childrenWidth = Math.Max(Convert.ToInt32(childrenWidth + (containerNode.Width - childrenWidth) / 2), childrenWidth); //in case the length of the containerNode is bigger than the total length of the children } #endregion if (childrenWidth == 0) //there are no children; this is the branch end { childrenWidth = containerNode.Width + wordSpacing; } #region Positioning thisY = shiftTop; if (containerNode.childNodes.Count > 0 && containerNode.expanded) { if (containerNode.childNodes.Count == 1) { thisX = Convert.ToInt32(containerNode.childNodes[0].X + containerNode.childNodes[0].Width / 2 - containerNode.Width / 2); } else { float firstChild = containerNode.childNodes[0].Left + containerNode.childNodes[0].Width / 2; float lastChild = containerNode.childNodes[containerNode.childNodes.Count - 1].Left + containerNode.childNodes[containerNode.childNodes.Count - 1].Width / 2; //the following max in case the containerNode is larger than the childrenWidth thisX = Convert.ToInt32(Math.Max(firstChild + (lastChild - firstChild - containerNode.Width) / 2, firstChild)); } } else { thisX = shiftLeft; } containerNode.rectangle.X = thisX; containerNode.rectangle.Y = thisY; #endregion return(childrenWidth); }
/// <summary> /// BFT of the diagram with the given visitor, starting from the given shape /// </summary> /// <param name="visitor"></param> /// <param name="shape"></param> public void BreadthFirstTraversal(IVisitor visitor, ShapeBase shape) { graphAbstract.BreadthFirstTraversal(visitor, shape); }
/// <summary> /// BFT of the diagram with the given visitor, starting from the given shape /// </summary> /// <param name="visitor"></param> /// <param name="shape"></param> public void BreadthFirstTraversal(IVisitor visitor, ShapeBase shape) { for (int i = 0; i < shapes.Count; i++) { shapes[i].visited = false; } BFT(visitor, shape); }
/// <summary> /// DFT of the (sub)graph starting from the given shape /// </summary> /// <param name="visitor"></param> /// <param name="shape"></param> private void DFT(IVisitor visitor, ShapeBase shape) { if (!visitor.IsDone) { if(typeof(IPrePostVisitor).IsInstanceOfType(visitor)) (visitor as IPrePostVisitor).PreVisit(shape); visitor.Visit(shape); shape.visited = true; try { foreach(ShapeBase sh in shape.childNodes) { if (!sh.visited) { DepthFirstTraversal(visitor, sh); } } } catch(Exception exc) { Trace.WriteLine(exc.Message); } if(typeof(IPrePostVisitor).IsInstanceOfType(visitor)) (visitor as IPrePostVisitor).PostVisit(shape); } }
/// <summary> /// BFT of the diagram with the given visitor, starting from the given shape /// </summary> /// <param name="visitor"></param> /// <param name="shape"></param> private void BFT(IVisitor visitor, ShapeBase shape) { Queue queue = new Queue(); queue.Enqueue(shape); shape.visited = true; while (!(queue.Count==0 || visitor.IsDone)) { ShapeBase node1 = queue.Dequeue() as ShapeBase; visitor.Visit(node1); node1.visited = true; try { foreach(ShapeBase sh in node1.childNodes) { if (!sh.visited) { queue.Enqueue(sh); //visitor.Visit(sh); //sh.visited= true; } } } catch(Exception exc) { Trace.WriteLine(exc.Message); } //visitor.PostVisit(node1); } }
/// <summary> /// Positions everything underneath the node and returns the total width of the kids /// </summary> /// <param name="containerNode"></param> /// <param name="first"></param> /// <param name="shiftLeft"></param> /// <param name="shiftTop"></param> /// <returns></returns> private int VerticalDrawTree(ShapeBase containerNode, bool first, int shiftLeft, int shiftTop) { bool isFirst = false; bool isParent = containerNode.childNodes.Count>0? true: false; int childrenWidth = 0; int thisX, thisY; int returned = 0; int verticalDelta = branchHeight ; //the applied vertical shift of the child depends on the Height of the containerNode #region Children width for(int i =0; i<containerNode.childNodes.Count; i++) { //determine the width of the label if(i==0) isFirst = true; else isFirst = false; if(containerNode.childNodes[i].visible) { if((branchHeight - containerNode.Height) < 30) //if too close to the child, shift it with 40 units verticalDelta = containerNode.Height + 40; returned = VerticalDrawTree(containerNode.childNodes[i], isFirst, shiftLeft + childrenWidth, shiftTop + verticalDelta ); childrenWidth += returned; } } if(childrenWidth>0 && containerNode.expanded) childrenWidth=Math.Max(Convert.ToInt32(childrenWidth + (containerNode.Width-childrenWidth)/2), childrenWidth); //in case the length of the containerNode is bigger than the total length of the children #endregion if(childrenWidth==0) //there are no children; this is the branch end childrenWidth = containerNode.Width+wordSpacing; #region Positioning thisY = shiftTop; if(containerNode.childNodes.Count>0 && containerNode.expanded) { if(containerNode.childNodes.Count==1) { thisX = Convert.ToInt32(containerNode.childNodes[0].X+containerNode.childNodes[0].Width/2 - containerNode.Width/2); } else { float firstChild = containerNode.childNodes[0].Left+ containerNode.childNodes[0].Width/2; float lastChild = containerNode.childNodes[containerNode.childNodes.Count-1].Left + containerNode.childNodes[containerNode.childNodes.Count-1].Width/2; //the following max in case the containerNode is larger than the childrenWidth thisX = Convert.ToInt32(Math.Max(firstChild + (lastChild -firstChild - containerNode.Width)/2, firstChild)); } } else { thisX = shiftLeft; } containerNode.rectangle.X = thisX; containerNode.rectangle.Y = thisY; #endregion return childrenWidth; }
/// <summary> /// Visits the shape /// </summary> /// <param name="sh"></param> public void Visit(ShapeBase sh) { sh.Expand(); }
/// <summary> /// Horizontal layout algorithm /// </summary> /// <param name="containerNode"></param> /// <param name="first"></param> /// <param name="shiftLeft"></param> /// <param name="shiftTop"></param> /// <returns></returns> private int HorizontalDrawTree(ShapeBase containerNode, bool first, int shiftLeft, int shiftTop) { bool isFirst = false; bool isParent = containerNode.childNodes.Count>0? true: false; int childrenHeight = 0; int thisX, thisY; int returned = 0; int horizontalDelta = branchHeight; #region Children width for(int i =0; i<containerNode.childNodes.Count; i++) { //determine the width of the label if(i==0) isFirst = true; else isFirst = false; if(containerNode.childNodes[i].visible) { if((branchHeight - containerNode.Width) < 30) //if too close to the child, shift it with 40 units horizontalDelta = containerNode.Width + 40; returned = HorizontalDrawTree(containerNode.childNodes[i], isFirst, shiftLeft + horizontalDelta , shiftTop + childrenHeight ); childrenHeight += returned; } } #endregion if(childrenHeight==0) //there are no children; this is the branch end childrenHeight = containerNode.Height+wordSpacing; #region Positioning thisX = shiftLeft; if(containerNode.childNodes.Count>0 && containerNode.expanded) { int firstChild = containerNode.childNodes[0].Y; int lastChild = containerNode.childNodes[containerNode.childNodes.Count-1].Y; thisY = Convert.ToInt32(firstChild + (lastChild - firstChild)/2); } else { thisY = Convert.ToInt32(shiftTop); } containerNode.rectangle.X = thisX; containerNode.rectangle.Y = thisY; #endregion return childrenHeight; }
/// <summary> /// Deserializes the graphtype /// </summary> /// <param name="g">the graphtype which acts as an intermediate storage between XML and the GraphAbstract /// </param> /// <returns></returns> private GraphAbstract Deserialize(GraphType g) { GraphAbstract abs = new GraphAbstract(); abs.Description = g.Description; ShapeBase shape = null, from = null, to = null; NodeType node; DataType dt; Connection con; ParentChildCollection pcs = new ParentChildCollection(); //temporary store for parent-child relations #region Load the nodes for (int k = 0; k < g.Nodes.Count; k++) //loop over all serialized nodes { try { #region find out which type of shape needs to be instantiated node = g.Nodes[k] as NodeType; Type shapeType = Type.GetType(node.Type); if (shapeType != null) { object[] args = { this.site }; shape = Activator.CreateInstance(shapeType, args) as ShapeBase; } #endregion #region use the attribs again to reconstruct the props for (int m = 0; m < node.Items.Count; m++) //loop over the serialized data { dt = node.Items[m] as DataType; if (dt.Key == "ParentNode") { //forget the parenting, it's done in a separate loop to be sure all shapes are loaded if (dt.Text.Count > 0) //could be if the shape is the root { pcs.Add(new ParentChild(shape, dt.Text[0].ToString())); } else { shape.IsRoot = true; abs.Root = shape; } continue; } foreach (PropertyInfo pi in shape.GetType().GetProperties()) { if (Attribute.IsDefined(pi, typeof(GraphDataAttribute))) { if (pi.Name == dt.Key) { if (pi.GetIndexParameters().Length == 0) { if (pi.PropertyType.Equals(typeof(int))) { pi.SetValue(shape, Convert.ToInt32(dt.Text[0]), null); } else if (pi.PropertyType.Equals(typeof(Color))) //Color is stored as an integer { pi.SetValue(shape, Color.FromArgb(int.Parse(dt.Text[0].ToString())), null); } else if (pi.PropertyType.Equals(typeof(string))) { pi.SetValue(shape, (string)(dt.Text[0]), null); } else if (pi.PropertyType.Equals(typeof(bool))) { pi.SetValue(shape, Convert.ToBoolean(dt.Text[0]), null); } else if (pi.PropertyType.Equals(typeof(Guid))) { pi.SetValue(shape, new Guid((string)dt.Text[0]), null); } } else { pi.SetValue(shape, dt.Text, null); } break; } } } } #endregion shape.Font = site.Font; shape.Fit(); abs.Shapes.Add(shape); } catch (Exception exc) { Trace.WriteLine(exc.Message); continue; } } //loop over nodes #endregion //now for the edges; //every node has precisely one parent and one connection to it, unless it's the root for (int n = 0; n < pcs.Count; n++) { from = pcs[n].ChildShape; to = abs.Shapes[pcs[n].Parent]; con = new Connection(from, to); abs.Connections.Add(con); con.site = site; if (pcs[n].ChildShape.visible) { con.visible = true; } from.connection = con; //a lot of crossing...to make life easy really from.parentNode = to; to.childNodes.Add(from); } return(abs); }
/// <summary> /// Occurs when a node is removed from the diagram /// </summary> /// <param name="shape"></param> private void lithiumControl_OnDeleteNode(Netron.Lithium.ShapeBase shape) { Output("Shape '" + shape.Text + "' deleted"); }
/// <summary> /// Passes the event from the abstracts shape collection to the outside. /// Having the event in the GraphAbstract being raised centralizes it, /// otherwise the event should be raise in various places /// </summary> /// <param name="shape"></param> private void OnShapeAdded(ShapeBase shape) { if(this.OnNewNode!=null) OnNewNode(shape); }
/// <summary> /// Occurs when a new node is added to the diagram /// </summary> /// <param name="shape"></param> private void lithiumControl_OnNewNode(Netron.Lithium.ShapeBase shape) { Output("New node: " + shape.Text); }
/// <summary> /// Handles the mouse-up event /// </summary> /// <param name="e"></param> protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp (e); globalMove = false; Connection con = null; //test if we connected a connection if(tracking) { tracking = false; //make the volatile solid if(neoCon!=null) { con = new Connection(neoCon.To, neoCon.From); con.site = this; Connections.Add(con); //the From is the shape seeking a parent neoCon.To.childNodes.Add(neoCon.From); neoCon.From.parentNode = neoCon.To; con.visible = true; neoCon.To.Expand(); neoCon.From.connection = con; } else //the user hasn't released near anything, so reset to the original situation { con = new Connection(memChild, memParent); con.site = this; Connections.Add(con); memParent.childNodes.Add(memChild); memChild.parentNode = memParent; con.visible = true; memChild.connection = con; } //either case, restart the process next neoCon = null; memChild = null; memParent = null; DrawTree(); } }
/// <summary> /// Constructor /// </summary> /// <param name="from">the shape where the connection starts</param> /// <param name="to">the shape where the connection ends</param> /// <param name="color">the color of the connection</param> /// <param name="width">the (float) width of the connection (in pixels)</param> public Connection(ShapeBase from, ShapeBase to, Color color, float width) : this(from, to, color) { currentPen = new Pen(color, width); }
private bool Environment(ShapeBase shape1, ShapeBase shape2) { return Math.Sqrt((shape1.X-shape2.X)*(shape1.X-shape2.X)+(shape1.Y-shape2.Y)*(shape1.Y-shape2.Y))<100; }
/// <summary> /// Serializes the given shape to xml /// </summary> /// <param name="s"></param> /// <returns></returns> private NodeType SerializeNode(ShapeBase s) { Hashtable attributes = GraphDataAttribute.GetValuesOfTaggedFields(s); NodeType node = new NodeType(); //node.ID = FormatID(s); //node.Items.Add(DataTypeFromEntity(s)); if(typeof(OvalShape).IsInstanceOfType(s)) node.Type = "Netron.Lithium.OvalShape"; else if(typeof(SimpleRectangle).IsInstanceOfType(s)) node.Type = "Netron.Lithium.SimpleRectangle"; else if(typeof(TextLabel).IsInstanceOfType(s)) node.Type = "Netron.Lithium.TextLabel"; foreach(DataType data in DataTypesFromAttributes(attributes)) { node.Items.Add(data); } return node; }
/// <summary> /// Removes the connection from the collection /// </summary> /// <param name="shape">a ShapeBase object</param> public void Remove(ShapeBase shape) { this.InnerList.Remove(shape); }
/// <summary> /// Constructor /// </summary> /// <param name="from">the shape where the connection starts</param> /// <param name="to">the shape where the connection ends</param> /// <param name="color">the color of the connection</param> public Connection(ShapeBase from, ShapeBase to, Color color) : this(from, to) { currentPen = new Pen(color, 1f); }