public TreeFlattener(SharpTreeNode modelRoot, bool includeRoot)
 {
     this.root = modelRoot;
     while (root.listParent != null)
         root = root.listParent;
     root.treeFlattener = this;
     this.includeRoot = includeRoot;
 }
 internal static int GetVisibleIndexForNode(SharpTreeNode node)
 {
     int index = node.left != null ? node.left.GetTotalListLength() : 0;
     while (node.listParent != null) {
         if (node == node.listParent.right) {
             if (node.listParent.left != null)
                 index += node.listParent.left.GetTotalListLength();
             if (node.listParent.isVisible)
                 index++;
         }
         node = node.listParent;
     }
     return index;
 }
Beispiel #3
0
        public int IndexOf(object item)
        {
            SharpTreeNode node = item as SharpTreeNode;

            if (node != null && node.IsVisible && node.GetListRoot() == root)
            {
                if (includeRoot)
                {
                    return(SharpTreeNode.GetVisibleIndexForNode(node));
                }
                else
                {
                    return(SharpTreeNode.GetVisibleIndexForNode(node) - 1);
                }
            }
            else
            {
                return(-1);
            }
        }
        internal static int GetVisibleIndexForNode(SharpTreeNode node)
        {
            int index = node.left != null?node.left.GetTotalListLength() : 0;

            while (node.listParent != null)
            {
                if (node == node.listParent.right)
                {
                    if (node.listParent.left != null)
                    {
                        index += node.listParent.left.GetTotalListLength();
                    }
                    if (node.listParent.isVisible)
                    {
                        index++;
                    }
                }
                node = node.listParent;
            }
            return(index);
        }
 internal static SharpTreeNode GetNodeByVisibleIndex(SharpTreeNode root, int index)
 {
     root.GetTotalListLength(); // ensure all list lengths are calculated
     Debug.Assert(index >= 0);
     Debug.Assert(index < root.totalListLength);
     SharpTreeNode node = root;
     while (true) {
         if (node.left != null && index < node.left.totalListLength) {
             node = node.left;
         } else {
             if (node.left != null) {
                 index -= node.left.totalListLength;
             }
             if (node.isVisible) {
                 if (index == 0)
                     return node;
                 index--;
             }
             node = node.right;
         }
     }
 }
 /// <summary>
 /// Balances the subtree rooted in <paramref name="node"/> and recomputes the 'height' field.
 /// This method assumes that the children of this node are already balanced and have an up-to-date 'height' value.
 /// </summary>
 /// <returns>The new root node</returns>
 static SharpTreeNode Rebalance(SharpTreeNode node)
 {
     Debug.Assert(node.left == null || Math.Abs(node.left.Balance) <= 1);
     Debug.Assert(node.right == null || Math.Abs(node.right.Balance) <= 1);
     // Keep looping until it's balanced. Not sure if this is stricly required; this is based on
     // the Rope code where node merging made this necessary.
     while (Math.Abs(node.Balance) > 1)
     {
         // AVL balancing
         // note: because we don't care about the identity of concat nodes, this works a little different than usual
         // tree rotations: in our implementation, the "this" node will stay at the top, only its children are rearranged
         if (node.Balance > 1)
         {
             if (node.right.Balance < 0)
             {
                 node.right = node.right.RotateRight();
             }
             node = node.RotateLeft();
             // If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the left node; so rebalance that.
             node.left = Rebalance(node.left);
         }
         else if (node.Balance < -1)
         {
             if (node.left.Balance > 0)
             {
                 node.left = node.left.RotateLeft();
             }
             node = node.RotateRight();
             // If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the right node; so rebalance that.
             node.right = Rebalance(node.right);
         }
     }
     Debug.Assert(Math.Abs(node.Balance) <= 1);
     node.height          = (byte)(1 + Math.Max(Height(node.left), Height(node.right)));
     node.totalListLength = -1;             // mark for recalculation
     // since balancing checks the whole tree up to the root, the whole path will get marked as invalid
     return(node);
 }
 static void InsertNodeAfter(SharpTreeNode pos, SharpTreeNode newNode)
 {
     // newNode might be the model root of a whole subtree, so go to the list root of that subtree:
     newNode = newNode.GetListRoot();
     if (pos.right == null)
     {
         pos.right          = newNode;
         newNode.listParent = pos;
     }
     else
     {
         // insert before pos.right's leftmost:
         pos = pos.right;
         while (pos.left != null)
         {
             pos = pos.left;
         }
         Debug.Assert(pos.left == null);
         pos.left           = newNode;
         newNode.listParent = pos;
     }
     RebalanceUntilRoot(pos);
 }
 SharpTreeNode Successor()
 {
     if (right != null)
     {
         SharpTreeNode node = right;
         while (node.left != null)
         {
             node = node.left;
         }
         return(node);
     }
     else
     {
         SharpTreeNode node = this;
         SharpTreeNode oldNode;
         do
         {
             oldNode = node;
             node    = node.listParent;
             // loop while we are on the way up from the right part
         } while (node != null && node.right == oldNode);
         return(node);
     }
 }
        SharpTreeNode RotateRight()
        {
            /* Rotate tree to the right
             *
             *       this             left
             *       /  \             /  \
             *     left  C   ===>    A   this
             *     / \                   /  \
             *    A   B                 B    C
             */
            SharpTreeNode b      = left.right;
            SharpTreeNode newTop = left;

            if (b != null)
            {
                b.listParent = this;
            }
            this.left         = b;
            newTop.right      = this;
            newTop.listParent = this.listParent;
            this.listParent   = newTop;
            newTop.right      = Rebalance(this);
            return(newTop);
        }
 /// <summary>
 /// Balances the subtree rooted in <paramref name="node"/> and recomputes the 'height' field.
 /// This method assumes that the children of this node are already balanced and have an up-to-date 'height' value.
 /// </summary>
 /// <returns>The new root node</returns>
 static SharpTreeNode Rebalance(SharpTreeNode node)
 {
     Debug.Assert(node.left == null || Math.Abs(node.left.Balance) <= 1);
     Debug.Assert(node.right == null || Math.Abs(node.right.Balance) <= 1);
     // Keep looping until it's balanced. Not sure if this is stricly required; this is based on
     // the Rope code where node merging made this necessary.
     while (Math.Abs(node.Balance) > 1) {
         // AVL balancing
         // note: because we don't care about the identity of concat nodes, this works a little different than usual
         // tree rotations: in our implementation, the "this" node will stay at the top, only its children are rearranged
         if (node.Balance > 1) {
             if (node.right.Balance < 0) {
                 node.right = node.right.RotateRight();
             }
             node = node.RotateLeft();
             // If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the left node; so rebalance that.
             node.left = Rebalance(node.left);
         } else if (node.Balance < -1) {
             if (node.left.Balance > 0) {
                 node.left = node.left.RotateLeft();
             }
             node = node.RotateRight();
             // If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the right node; so rebalance that.
             node.right = Rebalance(node.right);
         }
     }
     Debug.Assert(Math.Abs(node.Balance) <= 1);
     node.height = (byte)(1 + Math.Max(Height(node.left), Height(node.right)));
     node.totalListLength = -1; // mark for recalculation
     // since balancing checks the whole tree up to the root, the whole path will get marked as invalid
     return node;
 }
 static void InsertNodeAfter(SharpTreeNode pos, SharpTreeNode newNode)
 {
     // newNode might be the model root of a whole subtree, so go to the list root of that subtree:
     newNode = newNode.GetListRoot();
     if (pos.right == null) {
         pos.right = newNode;
         newNode.listParent = pos;
     } else {
         // insert before pos.right's leftmost:
         pos = pos.right;
         while (pos.left != null)
             pos = pos.left;
         Debug.Assert(pos.left == null);
         pos.left = newNode;
         newNode.listParent = pos;
     }
     RebalanceUntilRoot(pos);
 }
 static int Height(SharpTreeNode node)
 {
     return node != null ? node.height : 0;
 }
 static void DumpTree(SharpTreeNode node)
 {
     node.GetListRoot().DumpTree();
 }
 static void DeleteNode(SharpTreeNode node)
 {
     SharpTreeNode balancingNode;
     if (node.left == null) {
         balancingNode = node.listParent;
         node.ReplaceWith(node.right);
         node.right = null;
     } else if (node.right == null) {
         balancingNode = node.listParent;
         node.ReplaceWith(node.left);
         node.left = null;
     } else {
         SharpTreeNode tmp = node.right;
         while (tmp.left != null)
             tmp = tmp.left;
         // First replace tmp with tmp.right
         balancingNode = tmp.listParent;
         tmp.ReplaceWith(tmp.right);
         tmp.right = null;
         Debug.Assert(tmp.left == null);
         Debug.Assert(tmp.listParent == null);
         // Now move node's children to tmp:
         tmp.left = node.left; node.left = null;
         tmp.right = node.right; node.right = null;
         if (tmp.left != null) tmp.left.listParent = tmp;
         if (tmp.right != null) tmp.right.listParent = tmp;
         // Then replace node with tmp
         node.ReplaceWith(tmp);
         if (balancingNode == node)
             balancingNode = tmp;
     }
     Debug.Assert(node.listParent == null);
     Debug.Assert(node.left == null);
     Debug.Assert(node.right == null);
     node.height = 1;
     node.totalListLength = -1;
     if (balancingNode != null)
         RebalanceUntilRoot(balancingNode);
 }
 public virtual IDataObject Copy(SharpTreeNode[] nodes)
 {
     throw new NotSupportedException(GetType().Name + " does not support copy/paste or drag'n'drop");
 }
        SharpTreeNode RotateRight()
        {
            /* Rotate tree to the right
             *
             *       this             left
             *       /  \             /  \
             *     left  C   ===>    A   this
             *     / \                   /  \
             *    A   B                 B    C
             */
            SharpTreeNode b = left.right;
            SharpTreeNode newTop = left;

            if (b != null) b.listParent = this;
            this.left = b;
            newTop.right = this;
            newTop.listParent = this.listParent;
            this.listParent = newTop;
            newTop.right = Rebalance(this);
            return newTop;
        }
 void ReplaceWith(SharpTreeNode node)
 {
     if (listParent != null) {
         if (listParent.left == this) {
             listParent.left = node;
         } else {
             Debug.Assert(listParent.right == this);
             listParent.right = node;
         }
         if (node != null)
             node.listParent = listParent;
         listParent = null;
     } else {
         // this was a root node
         Debug.Assert(node != null); // cannot delete the only node in the tree
         node.listParent = null;
         if (treeFlattener != null) {
             Debug.Assert(node.treeFlattener == null);
             node.treeFlattener = this.treeFlattener;
             this.treeFlattener = null;
             node.treeFlattener.root = node;
         }
     }
 }
 static void RebalanceUntilRoot(SharpTreeNode pos)
 {
     while (pos.listParent != null) {
         if (pos == pos.listParent.left) {
             pos = pos.listParent.left = Rebalance(pos);
         } else {
             Debug.Assert(pos == pos.listParent.right);
             pos = pos.listParent.right = Rebalance(pos);
         }
         pos = pos.listParent;
     }
     SharpTreeNode newRoot = Rebalance(pos);
     if (newRoot != pos && pos.treeFlattener != null) {
         Debug.Assert(newRoot.treeFlattener == null);
         newRoot.treeFlattener = pos.treeFlattener;
         pos.treeFlattener = null;
         newRoot.treeFlattener.root = newRoot;
     }
     Debug.Assert(newRoot.listParent == null);
     newRoot.CheckInvariants();
 }
 static int Height(SharpTreeNode node)
 {
     return(node != null ? node.height : 0);
 }
 public virtual void StartDrag(DependencyObject dragSource, SharpTreeNode[] nodes)
 {
     DragDropEffects effects = DragDropEffects.All;
     if (!nodes.All(n => n.CanDelete()))
         effects &= ~DragDropEffects.Move;
     DragDropEffects result = DragDrop.DoDragDrop(dragSource, Copy(nodes), effects);
     if (result == DragDropEffects.Move) {
         foreach (SharpTreeNode node in nodes)
             node.DeleteCore();
     }
 }
        void GetNodeAndIndex(SharpTreeViewItem item, DropPlace place, out SharpTreeNode node, out int index)
        {
            node = null;
            index = 0;

            if (place == DropPlace.Inside) {
                node = item.Node;
                index = node.Children.Count;
            }
            else if (place == DropPlace.Before) {
                if (item.Node.Parent != null) {
                    node = item.Node.Parent;
                    index = node.Children.IndexOf(item.Node);
                }
            }
            else {
                if (item.Node.Parent != null) {
                    node = item.Node.Parent;
                    index = node.Children.IndexOf(item.Node) + 1;
                }
            }
        }
 void UpdateDataContext(SharpTreeNode oldNode, SharpTreeNode newNode)
 {
     if (newNode != null) {
         newNode.PropertyChanged += Node_PropertyChanged;
         if (Template != null) {
             UpdateTemplate();
         }
     }
     if (oldNode != null) {
         oldNode.PropertyChanged -= Node_PropertyChanged;
     }
 }
        void RemoveNodes(SharpTreeNode start, SharpTreeNode end)
        {
            // Removes all nodes from start to end (inclusive)
            // All removed nodes will be reorganized in a separate tree, do not delete
            // regions that don't belong together in the tree model!

            List<SharpTreeNode> removedSubtrees = new List<SharpTreeNode>();
            SharpTreeNode oldPos;
            SharpTreeNode pos = start;
            do {
                // recalculate the endAncestors every time, because the tree might have been rebalanced
                HashSet<SharpTreeNode> endAncestors = new HashSet<SharpTreeNode>();
                for (SharpTreeNode tmp = end; tmp != null; tmp = tmp.listParent)
                    endAncestors.Add(tmp);

                removedSubtrees.Add(pos);
                if (!endAncestors.Contains(pos)) {
                    // we can remove pos' right subtree in a single step:
                    if (pos.right != null) {
                        removedSubtrees.Add(pos.right);
                        pos.right.listParent = null;
                        pos.right = null;
                    }
                }
                SharpTreeNode succ = pos.Successor();
                DeleteNode(pos); // this will also rebalance out the deletion of the right subtree

                oldPos = pos;
                pos = succ;
            } while (oldPos != end);

            // merge back together the removed subtrees:
            SharpTreeNode removed = removedSubtrees[0];
            for (int i = 1; i < removedSubtrees.Count; i++) {
                removed = ConcatTrees(removed, removedSubtrees[i]);
            }
        }
 void ExpandRecursively(SharpTreeNode node)
 {
     if (node.CanExpandRecursively) {
         node.IsExpanded = true;
         foreach (SharpTreeNode child in node.Children) {
             ExpandRecursively(child);
         }
     }
 }
        SharpTreeNode RotateLeft()
        {
            /* Rotate tree to the left
             *
             *       this               right
             *       /  \               /  \
             *      A   right   ===>  this  C
             *           / \          / \
             *          B   C        A   B
             */
            SharpTreeNode b = right.left;
            SharpTreeNode newTop = right;

            if (b != null) b.listParent = this;
            this.right = b;
            newTop.left = this;
            newTop.listParent = this.listParent;
            this.listParent = newTop;
            // rebalance the 'this' node - this is necessary in some bulk insertion cases:
            newTop.left = Rebalance(this);
            return newTop;
        }
 /// <summary>
 /// Handles the node expanding event in the tree view.
 /// This method gets called only if the node is in the visible region (a SharpTreeNodeView exists).
 /// </summary>
 internal void HandleExpanding(SharpTreeNode node)
 {
     if (doNotScrollOnExpanding)
         return;
     SharpTreeNode lastVisibleChild = node;
     while (true) {
         SharpTreeNode tmp = lastVisibleChild.Children.LastOrDefault(c => c.IsVisible);
         if (tmp != null) {
             lastVisibleChild = tmp;
         } else {
             break;
         }
     }
     if (lastVisibleChild != node) {
         // Make the the expanded children are visible; but don't scroll down
         // to much (keep node itself visible)
         base.ScrollIntoView(lastVisibleChild);
         // For some reason, this only works properly when delaying it...
         Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(
             delegate {
                 base.ScrollIntoView(node);
             }));
     }
 }
 public void ScrollIntoView(SharpTreeNode node)
 {
     if (node == null)
         throw new ArgumentNullException("node");
     doNotScrollOnExpanding = true;
     foreach (SharpTreeNode ancestor in node.Ancestors())
         ancestor.IsExpanded = true;
     doNotScrollOnExpanding = false;
     base.ScrollIntoView(node);
 }
 public virtual bool CanDrag(SharpTreeNode[] nodes)
 {
     return false;
 }
        internal protected virtual void OnChildrenChanged(NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
            {
                foreach (SharpTreeNode node in e.OldItems)
                {
                    Debug.Assert(node.modelParent == this);
                    node.modelParent = null;
                    Debug.WriteLine("Removing {0} from {1}", node, this);
                    SharpTreeNode removeEnd = node;
                    while (removeEnd.modelChildren != null && removeEnd.modelChildren.Count > 0)
                    {
                        removeEnd = removeEnd.modelChildren.Last();
                    }

                    List <SharpTreeNode> removedNodes = null;
                    int visibleIndexOfRemoval         = 0;
                    if (node.isVisible)
                    {
                        visibleIndexOfRemoval = GetVisibleIndexForNode(node);
                        removedNodes          = node.VisibleDescendantsAndSelf().ToList();
                    }

                    RemoveNodes(node, removeEnd);

                    if (removedNodes != null)
                    {
                        var flattener = GetListRoot().treeFlattener;
                        if (flattener != null)
                        {
                            flattener.NodesRemoved(visibleIndexOfRemoval, removedNodes);
                        }
                    }
                }
            }
            if (e.NewItems != null)
            {
                SharpTreeNode insertionPos;
                if (e.NewStartingIndex == 0)
                {
                    insertionPos = null;
                }
                else
                {
                    insertionPos = modelChildren[e.NewStartingIndex - 1];
                }

                foreach (SharpTreeNode node in e.NewItems)
                {
                    Debug.Assert(node.modelParent == null);
                    node.modelParent = this;
                    node.UpdateIsVisible(isVisible && isExpanded, false);
                    //Debug.WriteLine("Inserting {0} after {1}", node, insertionPos);

                    while (insertionPos != null && insertionPos.modelChildren != null && insertionPos.modelChildren.Count > 0)
                    {
                        insertionPos = insertionPos.modelChildren.Last();
                    }
                    InsertNodeAfter(insertionPos ?? this, node);

                    insertionPos = node;
                    if (node.isVisible)
                    {
                        var flattener = GetListRoot().treeFlattener;
                        if (flattener != null)
                        {
                            flattener.NodesInserted(GetVisibleIndexForNode(node), node.VisibleDescendantsAndSelf());
                        }
                    }
                }
            }

            RaisePropertyChanged("ShowExpander");
            RaiseIsLastChangedIfNeeded(e);
        }
 static void DumpTree(SharpTreeNode node)
 {
     node.GetListRoot().DumpTree();
 }
 static SharpTreeNode ConcatTrees(SharpTreeNode first, SharpTreeNode second)
 {
     SharpTreeNode tmp = first;
     while (tmp.right != null)
         tmp = tmp.right;
     InsertNodeAfter(tmp, second);
     return tmp.GetListRoot();
 }
 /// <summary>
 /// Scrolls the specified node in view and sets keyboard focus on it.
 /// </summary>
 public void FocusNode(SharpTreeNode node)
 {
     if (node == null)
         throw new ArgumentNullException("node");
     ScrollIntoView(node);
     // WPF's ScrollIntoView() uses the same if/dispatcher construct, so we call OnFocusItem() after the item was brought into view.
     if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) {
         OnFocusItem(node);
     } else {
         this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(this.OnFocusItem), node);
     }
 }