/// <summary> /// Returns the closest common ancestor object /// of this object and <paramref name="other"/>, /// or null if <paramref name="other"/> is null, /// or if there is no common ancestor. /// </summary> /// <param name="other">The other object to test.</param> /// <returns>The closest common ancestor object /// of this object and <paramref name="other"/></returns> public GraphElement CommonAncestor(GraphElement other) { if (other == null) { return(null); } if (other == this) { return(this); } GraphElement thisw = this; GraphElement otherw = other; int thisDepth = this.Depth; int otherDepth = other.Depth; while (thisDepth > otherDepth) { thisw = thisw.parent; --thisDepth; } while (otherDepth > thisDepth) { otherw = other.parent; --otherDepth; } while (thisw != null && thisw != otherw) { thisw = thisw.parent; otherw = otherw.parent; } return(thisw); }
private void AddChild(GraphElement child) { // Remove all holes from the sibling index list. Now the max index // number is equal to the size of the children list. this.EnsureSequentialSiblingIndex(); this.needSortChildren = true; // maybe false child.siblingIndex = this.childCount; //this.children.Add(child); if (this.childCount == this.children.Length) { GraphElement[] childs; if (this.childCount == 0) { childs = new GraphElement[4]; } else { childs = new GraphElement[2 * this.childCount]; Array.Copy(this.children, 0, childs, 0, this.childCount); } this.children = childs; } this.children[this.childCount++] = child; this.OnChildAdded(child); System.Drawing.RectangleF invalid = child.ChildrenBoundingBox(); invalid.Offset(child.X, child.Y); this.Invalidate(invalid); }
/// <summary> /// Tests whether or not <paramref name="item1"/> /// is on top of or stacked closer to the top /// of the display than <paramref name="item2"/>, /// in terms of Z-values and parent stacking rules. /// </summary> /// <param name="item1">The first specified item.</param> /// <param name="item2">The second specified item.</param> /// <returns>true if <paramref name="item1"/> is closer top /// than <paramref name="item2"/>, false otherwise.</returns> public static bool ClosestItemFirst(GraphElement item1, GraphElement item2) { // Siblings? Just check their z-values. if (item1.parent == item2.parent) { return(ClosestLeaf(item1, item2)); } // Find common ancestor, and each item's ancestor closest // to the common ancestor. int item1Depth = item1.Depth; int item2Depth = item2.Depth; GraphElement p = item1.parent; GraphElement t1 = item1; while (item1Depth > item2Depth && p != null) { if (p == item2) { // item2 is one of item1's ancestors; item1 is on top return(t1.bStacksBehindParent); } t1 = p; --item1Depth; p = p.parent; } p = item2.parent; GraphElement t2 = item2; while (item2Depth > item1Depth && p != null) { if (p == item1) { // item1 is one of item2's ancestors; item2 is on top return(t2.bStacksBehindParent); } t2 = p; --item2Depth; p = p.parent; } // item1Ancestor is now at the same level as item2Ancestor, but not the same. GraphElement p1 = t1; GraphElement p2 = t2; while (t1 != null && t1 != t2) { p1 = t1; p2 = t2; t1 = t1.parent; t2 = t2.parent; } // in case we have a common ancestor, // we compare the immediate children in the ancestor's path. // otherwise we compare the respective items' TopLevelItems directly. return(ClosestLeaf(p1, p2)); }
/// <summary> /// Determines whether or not <paramref name="item1"/> is on top of /// its sibling, <paramref name="item2"/>, /// based on their stacking properties, Z values, and sibling indexes. /// </summary> /// <param name="item1">The first sibling to compare.</param> /// <param name="item2">The second sibling to compare.</param> /// <returns>true if <paramref name="item1"/> is on top of /// <paramref name="item2"/>, false otherwise.</returns> public static bool ClosestLeaf(GraphElement item1, GraphElement item2) { if (item1.bStacksBehindParent != item2.bStacksBehindParent) { return(item2.bStacksBehindParent); } if (item1.z != item2.z) { return(item1.z > item2.z); } return(item1.siblingIndex > item2.siblingIndex); }
private void RemoveChild(GraphElement child) { // When removing elements in the middle of the children list, // there will be a "gap" in the list of sibling indexes (0,1,3,4). if (!this.holesInSiblingIndex) { this.holesInSiblingIndex = child.siblingIndex != this.childCount - 1; } if (this.sequentialOrdering && !this.holesInSiblingIndex) { //this.children.RemoveAt(child.siblingIndex); this.childCount--; Array.Copy(this.children, child.siblingIndex + 1, this.children, child.siblingIndex, this.childCount - child.siblingIndex); this.children[this.childCount] = null; } else { //this.children.Remove(child); int i; for (i = 0; i < this.childCount; i++) { if (this.children[i] == child) { break; } } if (i < this.childCount) { this.childCount--; Array.Copy(this.children, i + 1, this.children, i, this.childCount - i); this.children[this.childCount] = null; } } // NB! Do not use children.RemoveAt(child.siblingIndex) because // the child is not guaranteed to be at the index after the list is sorted. // (see ensureSortedChildren()). child.siblingIndex = -1; this.OnChildRemoved(child); System.Drawing.RectangleF invalid = child.ChildrenBoundingBox(); invalid.Offset(child.X, child.Y); this.Invalidate(invalid); }
/// <summary> /// Sets this element's <see cref="Parent"/> element. If /// <paramref name="parent"/> is null, this element gains /// special meaning as a "scene". </summary> /// <param name="parent">The new parent of this element.</param> /// <returns>True if this element's <see cref="Parent"/> was /// successfully set to <paramref name="parent"/>, false otherwise. /// </returns> public bool SetParent(GraphElement parent) { // TODO: Insert pre-notification (with possible adjustment?) if (parent == this) { Debug.WriteLine("Warning: Cannot assign object as a parent of itself"); return(false); } if (parent == this.parent) { return(false); } this.OnParentChanging(parent); // Remove from current parent if (this.parent != null) { this.parent.RemoveChild(this); } // Resolve depth. InvalidateDepthRecursively(); GraphElement oldParent = this.parent; this.parent = parent; if (this.parent != null) { this.parent.AddChild(this); } this.OnParentChanged(oldParent); // TODO: Insert post-notification return(true); }
/// <summary> /// Tests if this object is an ancestor of <paramref name="child"/> /// (i.e., if this object is <paramref name="child"/>'s parent, /// or one of <paramref name="child"/>'s parent's ancestors). /// </summary> /// <param name="child">The potential descendant to test.</param> /// <returns>true if this object is an ancestor of /// <paramref name="child"/>, false otherwise.</returns> public bool IsAncestorOf(GraphElement child) { if (child == null || child == this) { return(false); } if (child.Depth < this.Depth) { return(false); } GraphElement ancestor = child.parent; while (ancestor != null) { if (ancestor == this) { return(true); } ancestor = ancestor.parent; } return(false); }
/// <summary> /// Reimplement this function to trigger events and other reactions /// that occur after this element's <see cref="Parent"/> has changed /// and after it has been removed from the /// <paramref name="oldParent"/>'s <see cref="Children"/> list. /// </summary> /// <param name="oldParent">The old <see cref="Parent"/> /// of this element.</param> protected virtual void OnParentChanged(GraphElement oldParent) { }
/// <summary> /// Reimplement this function to trigger events and other reactions /// that occur before this element's <see cref="Parent"/> changes to /// <paramref name="newParent"/> and before it's removed from its /// <see cref="Parent"/>'s <see cref="Children"/> list. /// </summary> /// <param name="newParent">The new <see cref="Parent"/> /// of this element.</param> protected virtual void OnParentChanging(GraphElement newParent) { }
/// <summary> /// Reimplement this function to trigger events and other reactions /// that occur after the given <paramref name="child"/> has been removed /// from this element's <see cref="Children"/> and before this element /// is invalidated over the <paramref name="child"/>'s /// <see cref="BoundingBox"/> offset by its <see cref="Position"/>. /// </summary> /// <param name="child">The <see cref="GraphElement"/> that has just /// been removed from this element's <see cref="Children"/>. </param> protected virtual void OnChildRemoved(GraphElement child) { }
/// <summary> /// Reimplement this function to trigger events and other reactions /// that occur after the given <paramref name="child"/> has been added /// to this element's <see cref="Children"/> and before this element /// is invalidated over the <paramref name="child"/>'s /// <see cref="BoundingBox"/> offset by its <see cref="Position"/>. /// </summary> /// <param name="child">The <see cref="GraphElement"/> that has just /// been added to this element's <see cref="Children"/>. </param> protected virtual void OnChildAdded(GraphElement child) { }
/// <summary> /// Stacks this object before <paramref name="sibling"/>, which must be a sibling object /// (i.e., the two objects must share the same parent, or must both be toplevel objects). /// The <paramref name="sibling"/> must have the same Z value as this object, /// otherwise calling this function will have no effect. /// </summary> /// <param name="sibling">The sibling behind which this object is stacked.</param> /// <returns>false if <paramref name="sibling"/> is not actually a sibling /// of this object, true otherwise.</returns> /// <remarks> /// By default, all sibling items are stacked by insertion order /// (i.e., the first item you add is drawn before the next item you add). /// If two items' Z values are different, then the item with the highest Z value is drawn on top. /// When the Z values are the same, the insertion order will decide the stacking order. /// </remarks> public bool StackBefore(GraphElement sibling) { if (sibling == this) { return(false); } if (sibling == null || this.parent != sibling.parent) { Debug.WriteLine("Warning: Cannot stack under given object, which must be a sibling"); return(false); } int siblingCount = this.parent != null ? this.parent.childCount : -1; GraphElement[] siblings = this.parent != null ? this.parent.children : null; //(this.scene != null ? this.scene.TopLevelItems : null); if (siblings == null) { Debug.WriteLine("Warning: Cannot stack under given object, which must be a sibling"); return(false); } // First, make sure that the sibling indexes have no holes. // This also marks the children list for sorting. if (this.parent != null) { this.parent.EnsureSequentialSiblingIndex(); } //else // this.scene.ensureSequentialTopLevelSiblingIndexes(); int i, index; // Only move items with the same Z value, and that need moving. int siblingIndex = sibling.siblingIndex; int myIndex = this.siblingIndex; if (myIndex >= siblingIndex) { //siblings.RemoveAt(myIndex); //siblings.Insert(siblingIndex, this); for (i = myIndex; i > siblingIndex; i--) { siblings[i] = siblings[i - 1]; } siblings[siblingIndex] = this; // Fixup the insertion ordering. for (i = 0; i < siblingCount; i++) { index = siblings[i].siblingIndex; if (i != siblingIndex && index >= siblingIndex && index <= myIndex) { siblings[i].siblingIndex = ++index; } } this.siblingIndex = siblingIndex; /*for (i = 0; i < siblings.Count; i++) * { * index = siblings[i].siblingIndex; * if (i != siblingIndex && index >= siblingIndex && index <= myIndex) * siblings[i].siblingOrderChange(); * } * this.siblingOrderChange();/**/ } return(true); }
/// <summary> /// Removes the given <paramref name="item"/> and all its children /// from this scene by setting the <paramref name="item"/>'s /// <see cref="GraphElement.Parent"/> to null (and thereby making /// that item its own scene in certain aspects). /// </summary> /// <param name="item">The <see cref="GraphElement"/> to remove from /// this scene by setting its parent to null.</param> public void RemoveItem(GraphElement item) { item.SetParent(null); }
/// <summary> /// Adds or moves the given <paramref name="item"/> and all its /// children to this scene by setting this scene as the /// <paramref name="item"/>'s <see cref="GraphElement.Parent"/>. /// </summary> /// <param name="item">The <see cref="GraphElement"/> to add to /// this scene by making this scene its parent.</param> public void AddItem(GraphElement item) { item.SetParent(this); }