/// <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> /// 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> /// Removes a connection from the collection /// </summary> /// <param name="con">a connection object</param> public void Remove(Connection con) { this.InnerList.Remove(con); }
/// <summary> /// Adds a connection to the collection /// </summary> /// <param name="con">a connection</param> /// <returns>the index of the added element in the collection</returns> public int Add(Connection con) { return this.InnerList.Add(con); }
/// <summary> /// Adds a child to this shape /// </summary> /// <param name="text">the text of the newly created shape</param> /// <returns>the create shape</returns> public ShapeBase AddChild(string text) { SimpleRectangle shape = new SimpleRectangle(site); shape.Location = new Point(Width/2+50,Height/2+50); shape.Width = 50; shape.Height = 25; shape.Text = text; shape.ShapeColor = Color.Linen; shape.IsRoot = false; shape.parentNode = this; shape.Font = font; shape.level = level+1; shape.Fit(); //fit the child //add to the collections site.graphAbstract.Shapes.Add(shape); this.childNodes.Add(shape); //add a connection; From=child, To=parent Connection con = new Connection(shape, this); site.Connections.Add(con); con.site = this.site; con.visible = true; shape.connection = con; if(visible) Expand(); else { shape.visible = false; con.visible = false; } Fit(); //the cild count added at the end will enlarge the rectangle, so we have to fit return shape; }
/// <summary> /// Serializes a diagram edge /// </summary> /// <param name="c"></param> /// <returns></returns> private EdgeType SerializeEdge(Connection c) { Hashtable attributes = GraphDataAttribute.GetValuesOfTaggedFields(c); EdgeType edge = new EdgeType(); //edge.Source = c.From.Text; //edge.Target = c.To.Text; edge.From = c.From.UID.ToString(); edge.To = c.To.UID.ToString(); //since there is only one type of edge we don't need the next one //edge.Data.Add(DataTypeFromEntity(c)); foreach(DataType dt in DataTypesFromAttributes(attributes)) { edge.Data.Add(dt); } return edge; }
/// <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; }