static void RebalanceUntilRoot(FileItemViewModel 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;
            }
            FileItemViewModel 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();
        }
        FileItemViewModel RotateLeft()
        {
            /* Rotate tree to the left
             *
             *       this               right
             *       /  \               /  \
             *      A   right   ===>  this  C
             *           / \          / \
             *          B   C        A   B
             */
            FileItemViewModel b      = right.left;
            FileItemViewModel 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);
        }
 void ReplaceWith(FileItemViewModel 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;
         }
     }
 }
        internal static FileItemViewModel GetNodeByVisibleIndex(FileItemViewModel root, int index)
        {
            root.GetTotalListLength();             // ensure all list lengths are calculated
            Debug.Assert(index >= 0);
            Debug.Assert(index < root.totalListLength);
            FileItemViewModel 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;
                }
            }
        }
        static void DeleteNode(FileItemViewModel node)
        {
            FileItemViewModel 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
            {
                FileItemViewModel 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);
            }
        }
Ejemplo n.º 6
0
 /// <summary>Gets the item at the specified index in the list.</summary>
 ///
 /// <param name="index">The index of the item.</param>
 public object this[int index] {
     get {
         if (index < 0 || index >= this.Count)
         {
             throw new ArgumentOutOfRangeException();
         }
         return(FileItemViewModel.GetNodeByVisibleIndex(root, includeRoot ? index : index + 1));
     }
     set => throw new NotSupportedException();
        internal FileItemViewModel GetListRoot()
        {
            FileItemViewModel node = this;

            while (node.listParent != null)
            {
                node = node.listParent;
            }
            return(node);
        }
Ejemplo n.º 8
0
 /// <summary>Constructs the <see cref="FileTreeFlattener"/>.</summary>
 ///
 /// <param name="modelRoot">The root item of the list.</param>
 /// <param name="includeRoot">True if the root is visible in the list.</param>
 public FileTreeFlattener(FileItemViewModel modelRoot, bool includeRoot)
 {
     this.root = modelRoot;
     while (root.listParent != null)
     {
         root = root.listParent;
     }
     root.treeFlattener = this;
     this.includeRoot   = includeRoot;
 }
        static FileItemViewModel ConcatTrees(FileItemViewModel first, FileItemViewModel second)
        {
            FileItemViewModel tmp = first;

            while (tmp.right != null)
            {
                tmp = tmp.right;
            }
            InsertNodeAfter(tmp, second);
            return(tmp.GetListRoot());
        }
        void RemoveNodes(FileItemViewModel start, FileItemViewModel 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 <FileItemViewModel> removedSubtrees = new List <FileItemViewModel>();
            FileItemViewModel        oldPos;
            FileItemViewModel        pos = start;

            do
            {
                // recalculate the endAncestors every time, because the tree might have been rebalanced
                HashSet <FileItemViewModel> endAncestors = new HashSet <FileItemViewModel>();
                for (FileItemViewModel 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;
                    }
                }
                FileItemViewModel 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:
            FileItemViewModel removed = removedSubtrees[0];

            for (int i = 1; i < removedSubtrees.Count; i++)
            {
                removed = ConcatTrees(removed, removedSubtrees[i]);
            }
        }
        internal static int GetVisibleIndexForNode(FileItemViewModel 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);
        }
 /// <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 FileItemViewModel Rebalance(FileItemViewModel 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(FileItemViewModel pos, FileItemViewModel 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);
 }
        FileItemViewModel RotateRight()
        {
            /* Rotate tree to the right
             *
             *       this             left
             *       /  \             /  \
             *     left  C   ===>    A   this
             *     / \                   /  \
             *    A   B                 B    C
             */
            FileItemViewModel b      = left.right;
            FileItemViewModel 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);
        }
 FileItemViewModel Successor()
 {
     if (right != null)
     {
         FileItemViewModel node = right;
         while (node.left != null)
         {
             node = node.left;
         }
         return(node);
     }
     else
     {
         FileItemViewModel node = this;
         FileItemViewModel 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);
     }
 }
Ejemplo n.º 16
0
        /*private bool isHidden;
         * private bool isSelected;
         * private bool isExpanded;
         * private bool isEditing;*/

        /*private BitField16 flags = new BitField16 {
         *      //[0] = true,
         * };
         *
         #pragma warning disable 0649, IDE1006
         * private bool isVisible {
         *      get => flags[0];
         *      set => flags[0] = value;
         * }
         * private bool isHidden {
         *      get => flags[1];
         *      set => flags[1] = value;
         * }
         * private bool isSelected {
         *      get => flags[2];
         *      set => flags[2] = value;
         * }
         * private bool isExpanded {
         *      get => flags[3];
         *      set => flags[3] = value;
         * }
         * private bool isEditing {
         *      get => flags[4];
         *      set => flags[4] = value;
         * }
         * private bool isEventsHooked {
         *      get => flags[5];
         *      set => flags[5] = value;
         * }
         #pragma warning restore 0649, IDE1006*/

        //private bool canExpandRecursively = true;
        //private bool lazyLoading;
        //private bool? isChecked;

        private void UpdateIsVisible(bool parentIsVisible, bool updateFlattener)
        {
            bool newIsVisible = parentIsVisible && !isHidden;

            if (isVisible != newIsVisible)
            {
                isVisible = newIsVisible;

                // invalidate the augmented data
                FileItemViewModel node = this;
                while (node != null && node.totalListLength >= 0)
                {
                    node.totalListLength = -1;
                    node = node.listParent;
                }
                // Remember the removed nodes:
                List <FileItemViewModel> removedNodes = null;
                if (updateFlattener && !newIsVisible)
                {
                    removedNodes = VisibleDescendantsAndSelf().ToList();
                }
                // also update the model children:
                UpdateChildIsVisible(false);

                // Validate our invariants:
                if (updateFlattener)
                {
                    CheckRootInvariants();
                }

                // Tell the flattener about the removed nodes:
                if (removedNodes != null)
                {
                    var flattener = GetListRoot().treeFlattener;
                    if (flattener != null)
                    {
                        if (!SuppressRefresh)
                        {
                            flattener.NodesRemoved(GetVisibleIndexForNode(this), removedNodes);
                        }
                        foreach (var n in removedNodes)
                        {
                            n.OnIsVisibleChanged();
                        }
                    }
                }
                // Tell the flattener about the new nodes:
                if (updateFlattener && newIsVisible)
                {
                    var flattener = GetListRoot().treeFlattener;
                    if (flattener != null)
                    {
                        if (!SuppressRefresh)
                        {
                            flattener.NodesInserted(GetVisibleIndexForNode(this), VisibleDescendantsAndSelf());
                        }
                        foreach (var n in VisibleDescendantsAndSelf())
                        {
                            n.OnIsVisibleChanged();
                        }
                    }
                }
            }
        }
 static void DumpTree(FileItemViewModel node)
 {
     node.GetListRoot().DumpTree();
 }
 static int Height(FileItemViewModel node)
 {
     return(node != null ? node.height : 0);
 }