コード例 #1
0
ファイル: ProviderEvents.cs プロジェクト: dotnet/ef6tools
 public BranchModificationLevelShift(BranchModificationAction action, ref ShiftBranchLevelsData shiftData)
 {
     Action            = action;
     Branch            = shiftData.Branch;
     Flag              = false;
     Index             = shiftData.StartIndex;
     Count             = shiftData.Count;
     Depth             = shiftData.Depth;
     NewCount          = shiftData.NewCount;
     RemoveLevels      = shiftData.RemoveLevels;
     InsertLevels      = shiftData.InsertLevels;
     ReplacementBranch = shiftData.ReplacementBranch;
     BranchTester      = shiftData.BranchTester;
 }
コード例 #2
0
 /// <summary>
 ///     For all expansions of 'branch', first remove 'removeLevels' then insert 'insertLevels' branches from
 ///     all branches 'depth' levels below the current branch. Replace the tree's reference to the current
 ///     branch object with a reference to 'replacementBranch' (if set). The operation affects 'count' items
 ///     at position 'startIndex', which are replaced with 'newCount' new items. If depth>0, then a new VisibleItemCount
 ///     is retrieved from the branch and all sub branches are relocated. StartIndex/Count/NewCount affect only the passed in branch.
 ///     If Start/Count/NewCount are used with a replacement branch, then the replacement branch must be
 ///     consistent with the original. If 'branchTester' is not null, then it is used to adjust the operation
 ///     as described in the ILevelShiftAdjuster interface.
 /// </summary>
 /// <param name="branch"></param>
 /// <param name="removeLevels">The number of levels to remove</param>
 /// <param name="insertLevels">The number of levels to insert</param>
 /// <param name="depth">The number of levels below the starting branch to skip before removing and inserting levels</param>
 /// <param name="replacementBranch">Replace the starting branch with this one</param>
 /// <param name="branchTester">A callback interface for advanced level shifting</param>
 /// <param name="startIndex">The starting index of the items being adjusted in the starting branch</param>
 /// <param name="count">The original count for the number of items being adjusted in the starting branch</param>
 /// <param name="newCount">The replacement count for the number of items being adjusted in the starting branch</param>
 public ShiftBranchLevelsData(
     IBranch branch, int removeLevels, int insertLevels, int depth, IBranch replacementBranch, ILevelShiftAdjuster branchTester,
     int startIndex, int count, int newCount)
 {
     myBranch            = branch;
     myRemoveLevels      = removeLevels;
     myInsertLevels      = insertLevels;
     myDepth             = depth;
     myReplacementBranch = replacementBranch;
     myBranchTester      = branchTester;
     myStartIndex        = startIndex;
     myCount             = count;
     myNewCount          = newCount;
 }
コード例 #3
0
 public BranchModificationLevelShift(BranchModificationAction action, ref ShiftBranchLevelsData shiftData)
 {
     Action = action;
     Branch = shiftData.Branch;
     Flag = false;
     Index = shiftData.StartIndex;
     Count = shiftData.Count;
     Depth = shiftData.Depth;
     NewCount = shiftData.NewCount;
     RemoveLevels = shiftData.RemoveLevels;
     InsertLevels = shiftData.InsertLevels;
     ReplacementBranch = shiftData.ReplacementBranch;
     BranchTester = shiftData.BranchTester;
 }
コード例 #4
0
 /// <summary>
 ///     For all expansions of 'branch', first remove 'removeLevels' then insert 'insertLevels' branches from
 ///     all branches 'depth' levels below the current branch. Replace the tree's reference to the current
 ///     branch object with a reference to 'replacementBranch' (if set). If depth>0, then a new VisibleItemCount
 ///     is retrieved from the branch and all sub branches are relocated. If 'branchTester' is not null, then
 ///     it is used to adjust the operation as described in the ILevelShiftAdjuster interface.
 /// </summary>
 /// <param name="branch"></param>
 /// <param name="removeLevels">The number of levels to remove</param>
 /// <param name="insertLevels">The number of levels to insert</param>
 /// <param name="depth">The number of levels below the starting branch to skip before removing and inserting levels</param>
 /// <param name="replacementBranch">Replace the starting branch with this one</param>
 /// <param name="branchTester">A callback interface for advanced level shifting</param>
 public ShiftBranchLevelsData(
     IBranch branch, int removeLevels, int insertLevels, int depth, IBranch replacementBranch, ILevelShiftAdjuster branchTester)
     :
     this(branch, removeLevels, insertLevels, depth, replacementBranch, branchTester, -1, -1, -1)
 {
 }
コード例 #5
0
        // Helper function for RealignTreeNodeLevelShift. Collects sub branches into a single
        // list, detaches them from the parent nodes, and adjusts the Expanded setting on each.
        private void CollectChildBranches(
            ref TREENODE tnParent, ILevelShiftAdjuster branchTester, ref TREENODE tnAttach, ref TREENODE tnDummyHead,
            ref TREENODE tnAttachInKillZone, ref TREENODE tnDummyHeadInKillZone, ref NODEPOSITIONTRACKER_Dynamic detachedTrackers,
            int levelShift, bool parentExpanded)
        {
            // The ILevelShiftAdjuster mechanism allows us to keep this expansion for later attachment,
            // essentially making it a potential sibling of branches originally below this level.
            var tnProcessParent = tnParent;
            if (branchTester != null)
            {
                var testResult = branchTester.TestReattachBranch(tnParent.Branch);
                if (testResult != TestReattachBranchResult.Discard)
                {
                    tnParent.Expanded = tnParent.Expanded && parentExpanded;
                    if (tnAttachInKillZone == null)
                    {
                        if (tnDummyHeadInKillZone == null)
                        {
                            tnDummyHeadInKillZone = new TREENODE();
                        }
                        // Create a dummy head to attach to
                        tnAttachInKillZone = tnDummyHeadInKillZone;
                    }
                    tnAttachInKillZone.NextSibling = tnParent;
                    tnAttachInKillZone = tnParent;
                    tnParent.NextSibling = null;
                    tnParent = null;
                    switch (testResult)
                    {
                        case TestReattachBranchResult.ReattachIntact:
                            return;
                        case TestReattachBranchResult.Realign:
                            tnProcessParent.ImmedCount = tnProcessParent.Branch.VisibleItemCount;
                            if ((tnProcessParent.FirstPositionTracker != null)
                                && !tnProcessParent.DefaultTracking)
                            {
                                NODEPOSITIONTRACKER_Dynamic.DetachTrackers(ref tnProcessParent.FirstPositionTracker, ref detachedTrackers);
                            }
                            goto case TestReattachBranchResult.ReattachChildren;
                        case TestReattachBranchResult.ReattachChildren: // Allow processing to continue below
                            tnProcessParent.FullCount = tnProcessParent.ImmedCount;
                            tnProcessParent.ImmedSubItemGain = 0;
                            tnProcessParent.FullSubItemGain = 0;
                            break;
                    }
                }
            }

            var tnChild = tnProcessParent.FirstChild;
            if (tnChild == null)
            {
                // Not checked before calling CollectChildBranches so that TestReattachBranch has a chance
                return;
            }
            Debug.Assert((tnAttach == null) || (tnAttach.NextSibling == null)); // Bad attach point
            parentExpanded = parentExpanded && tnProcessParent.Expanded;
            if (levelShift <= 1)
            {
                // Walk the children to adjust the Expanded states and locate
                // the last child (for our next attach point).
                TREENODE tnPrev = null;
                while (tnChild != null)
                {
                    tnPrev = tnChild;
                    tnChild.Expanded = tnChild.Expanded && parentExpanded;
                    tnChild = tnChild.NextSibling;
                }

                // Detach the child list from the parent node and reattach it. Note
                // that this thrashes the FullCount of the parent node, but this is
                // harmless because we're about to blow the immediate parents away anyway.
                if (tnAttach == null)
                {
                    if (tnDummyHead == null)
                    {
                        tnDummyHead = new TREENODE();
                    }
                    // Create a dummy head to attach to
                    tnAttach = tnDummyHead;
                }
                tnAttach.NextSibling = tnProcessParent.FirstChild;
                tnProcessParent.FirstChild = null;
                tnAttach = tnPrev;
            }
            else
            {
                --levelShift;
                var tnNext = tnChild;
                while ((tnChild = tnNext) != null)
                {
                    // tnChild can come back null, get NextSibling first
                    tnNext = tnChild.NextSibling;
                    CollectChildBranches(
                        ref tnChild, branchTester, ref tnAttach, ref tnDummyHead, ref tnAttachInKillZone, ref tnDummyHeadInKillZone,
                        ref detachedTrackers, levelShift, parentExpanded);
                }
            }
        }
コード例 #6
0
        private void RealignTreeNodeLevelShift(
            TREENODE tn, int removeLevels, int insertLevels, int depth, ILevelShiftAdjuster branchTester, int start, int count, int newCount,
            bool firstLevel)
        {
            int maxIndex; //The highest index we care about, plus one

            // See if a new branch should be retrieved. The new branch at the
            // top level is passed in directly (required to support modifying
            // the root node). This enables the caller to provide new objects
            // for existing items instead of modifying existing ones.
            if (!firstLevel
                && branchTester != null)
            {
                if (branchTester.TestGetNewBranch(tn.Branch))
                {
                    var options = 0;
                    IBranch newBranch;
                    var wasDynamic = tn.Dynamic;
                    if (tn.SubItemRoot)
                    {
                        var tnParent = tn.Parent;
                        newBranch = tnParent.Parent.Branch.GetObject(
                            tnParent.Index,
                            tnParent.FirstSubItem.ColumnOfRootNode(tn),
                            tn.ComplexSubItem ? ObjectStyle.SubItemRootBranch : ObjectStyle.SubItemExpansion,
                            ref options) as IBranch;
                        // UNDONE_MC: Should we respect an option switch from SubItemRootBranch to SubItemExpansion here, or respect
                        // a SubItemCellStyles.Mixed setting on the parent branch and reattempt a SubItemRootBranch even if we
                        // currently have an expansion? These are both advanced scenarios. The code here will change the branch,
                        // but will not switch it between an expansion and a root branch, or vice versa.
                    }
                    else
                    {
                        newBranch = tn.Parent.Branch.GetObject(
                            tn.Index,
                            0,
                            ObjectStyle.ExpandedBranch,
                            ref options) as IBranch;
                    }
                    tn.SetFlags(newBranch.Features);
                    tn.Dynamic = wasDynamic; // Don't allow this one to change

                    Debug.Assert(newBranch != null);
                    if (tn.Dynamic)
                    {
                        myNodeTracker.ReBranchTreeNode(newBranch, tn);
                    }
                    else
                    {
                        tn.Branch = newBranch;
                    }
                }
            }
            if (start == -1)
            {
                count = tn.ImmedCount;
                maxIndex = count;
                newCount = tn.Branch.VisibleItemCount;
            }
            else
            {
                maxIndex = start + count;
            }

            // Get the first child node and adjust it past the
            // requested starting position.
            var tnChild = tn.FirstChild;
            TREENODE tnLastUntouched = null;
            if (start != -1)
            {
                while (tnChild != null)
                {
                    if (tnChild.Index >= start)
                    {
                        break;
                    }
                    tnLastUntouched = tnChild;
                    tnChild = tnChild.NextSibling;
                }
            }

            if (depth > 0)
            {
                // Walk all of the child nodes that are in range
                var depthAdjustment = 0;
                var continueDownBranch = true;
                while (tnChild != null)
                {
                    if (tnChild.Index >= maxIndex)
                    {
                        break;
                    }

                    if (branchTester != null)
                    {
                        var result = branchTester.ValidateAdjustDepth(tnChild.Branch);
                        continueDownBranch = result.Continue;
                        depthAdjustment = result.DepthAdjustment;
                    }

                    if (continueDownBranch)
                    {
                        // Recurse on this function for all applicable nodes
                        RealignTreeNodeLevelShift(
                            tnChild, removeLevels, insertLevels, depth + depthAdjustment - 1, branchTester, -1, -1, -1, false);
                    }
                    tnChild = tnChild.NextSibling;
                }
            }
            else
            {
                //
                // The expansions from levelShift levels away are moving up to this
                // level. There are several steps involved.
                // 1) Get the current node counts of the targeted items in the list
                //    and remove the expansion counts from the full count;
                // 2) Get all of the lists that are levelShift levels away and put them
                //    in a single list. Adjust the Expanded states along the way to
                //    reflect the expanded state of the parent lists.
                // 3) Adjust the indices of trailing items by count - newCount.
                // 4) Use LocateExpandedList to attach nodes to the new position and adjust
                //    counts accordingly.
                //   

                var changeCount = newCount - count;
                var subItemChange = 0;
                tn.ImmedCount += changeCount;
                var maxIndexReattach = maxIndex + changeCount;
                NODEPOSITIONTRACKER_Dynamic detachedTrackers = null;
                if ((tn.FirstPositionTracker != null)
                    && !tn.DefaultTracking)
                {
                    NODEPOSITIONTRACKER_Dynamic.DetachTrackers(ref tn.FirstPositionTracker, ref detachedTrackers);
                }
                if (tnChild != null)
                {
                    TREENODE tnDummyHead = null;
                    TREENODE tnAttach = null;
                    TREENODE tnDummyHeadInKillZone = null;
                    TREENODE tnAttachInKillZone = null;
                    var tnNext = tnChild;
                    if (removeLevels > 1)
                    {
                        var alwaysTossExpansions = tn.NoRelocate && (insertLevels == 0);
                        --removeLevels;
                        while (tnNext != null)
                        {
                            if (tnNext.Index >= maxIndex)
                            {
                                // Adjust indices
                                var adjust = newCount - count;
                                if (adjust != 0)
                                {
                                    // Keep the first untouched node (after the touched nodes) for later use
                                    tnChild = tnNext;
                                    while (tnChild != null)
                                    {
                                        tnChild.Index += adjust;
                                        tnChild = tnChild.NextSibling;
                                    }
                                }
                                // Get out of outer loop
                                break;
                            }

                            // Get the next node before deleting
                            tnChild = tnNext;
                            tnNext = tnChild.NextSibling;

                            // Make sure we're actually processing the right level.
                            if (branchTester != null)
                            {
                                var adjustResult = branchTester.ValidateAdjustDepth(tnChild.Branch);
                                var depthAdjustment = adjustResult.DepthAdjustment;
                                var keepWalkingBranch = adjustResult.Continue;
                                // Ignore any negative depth ajustment returned by the branchTester.
                                // We can't do anything with a negative adjustment here because we're 
                                // already at 0. Also, it is very likely that this same call in the 
                                // depth > 0 block is what put us here in the first place, so we should
                                // not reapply these results.
                                if (!keepWalkingBranch
                                    || depthAdjustment > 0)
                                {
                                    if (tnAttach == null)
                                    {
                                        if (tnDummyHead == null)
                                        {
                                            tnDummyHead = new TREENODE();
                                        }
                                        // Create a dummy head to attach to
                                        tnAttach = tnDummyHead;
                                    }
                                    tnAttach.NextSibling = tnChild;
                                    tnChild.NextSibling = null;
                                    tnAttach = tnChild;

                                    if (tnChild.Expanded)
                                    {
                                        // Drop change count here and add back later if
                                        // the branch successfully relocates.
                                        changeCount -= tnChild.FullCount;
                                        subItemChange -= tnChild.ExpandedSubItemGain;
                                    }
                                    subItemChange -= tnChild.ImmedSubItemGain;

                                    if (keepWalkingBranch && depthAdjustment > 0)
                                    {
                                        RealignTreeNodeLevelShift(
                                            tnChild, removeLevels + 1, insertLevels, depthAdjustment - 1, branchTester, -1, -1, -1, false);
                                    }
                                    else if (!alwaysTossExpansions)
                                    {
                                        // See comments on CollectChildBranches below
                                        CollectChildBranches(
                                            ref tnChild, branchTester, ref tnAttach, ref tnDummyHead, ref tnAttachInKillZone,
                                            ref tnDummyHeadInKillZone, ref detachedTrackers, removeLevels, true);
                                    }
                                    continue;
                                }
                            }

                            //
                            // Adjust the change count for this node and retrieve the child nodes
                            // to reattach later. Note that much of the item count will be recouped
                            // later on when the nodes are reattached.
                            //
                            if (tnChild.Expanded)
                            {
                                changeCount -= tnChild.FullCount;
                                subItemChange -= tnChild.ExpandedSubItemGain;
                            }
                            subItemChange -= tnChild.ImmedSubItemGain;

                            if (!alwaysTossExpansions)
                            {
                                // Get the branches from the child nodes. Note that the parent item
                                // is always treated as a root in and of itself, so it is always
                                // considered to be expanded (parentExpanded = true) even if it is
                                // collapsed in its parent branch. Note than tnChild will come
                                // back null if the branchTester decides that this is a branch
                                // that needs to be reattached. We've already retrieved the next
                                CollectChildBranches(
                                    ref tnChild, branchTester, ref tnAttach, ref tnDummyHead, ref tnAttachInKillZone,
                                    ref tnDummyHeadInKillZone, ref detachedTrackers, removeLevels, true);
                            }

                            // Free the child nodes. Note that CollectBranches detaches the
                            // branches it cares about, so this doesn't affect the branches that
                            // we might end up keeping.
                            if (tnChild != null)
                            {
                                FreeRecursive(ref tnChild, ref detachedTrackers);
                            }
                        }
                    }
                    else
                    {
                        // UNDONE: The original condition on the if was > 0, but that took out one too many levels.
                        // We need to figure out if this is the correct code for removeLevels == 0, or if
                        // we ever call with this value.
                        Debug.Assert(removeLevels == 1);
                        // No levels were removed. Explicitly pull in-range items from this list into the
                        // dummy list. We can't use CollectChildBranches here without making it understand
                        // the range issues, which isn't worth it.
                        if (tnChild.Index < maxIndex)
                        {
                            // Separate the children we care about into a separate list
                            tnDummyHead = new TREENODE();
                            tnDummyHead.NextSibling = tnChild; // Record our starting point
                            while (tnNext != null)
                            {
                                if (tnNext.Index < maxIndex)
                                {
                                    if (tnNext.Expanded)
                                    {
                                        changeCount -= tnNext.FullCount;
                                        subItemChange -= tnNext.ExpandedSubItemGain;
                                    }
                                    subItemChange -= tnNext.ImmedSubItemGain;
                                }
                                else
                                {
                                    // Leave tnChild alone set so we can reattach.
                                    // Adjust the indices on trailing nodes here.
                                    var adjust = newCount - count;
                                    if (adjust != 0)
                                    {
                                        tnChild = tnNext;
                                        while (tnChild != null)
                                        {
                                            tnChild.Index += adjust;
                                            tnChild = tnChild.NextSibling;
                                        }
                                    }
                                    break;
                                }
                                tnNext = tnNext.NextSibling;
                            }
                        }
                    }

                    //
                    // Relink untouched nodes
                    //
                    if (tnLastUntouched == null)
                    {
                        tn.FirstChild = tnNext;
                    }
                    else
                    {
                        tnLastUntouched.NextSibling = tnNext;
                    }

                    // Reattach any detached branches
                    // First reattach the branches that survived the kill zone (via ILevelShiftAdjuster.TestReattachBranch),
                    // then do a second pass to reattach the items below the kill zone. This enables the higher level nodes
                    // to reattach before we start creating replacement nodes for them.
                    if (tnDummyHeadInKillZone == null)
                    {
                        tnDummyHeadInKillZone = tnDummyHead;
                    }
                    while (tnDummyHeadInKillZone != null)
                    {
                        try
                        {
                            // Work on reattaching the nodes
                            TREENODE tnCurParent;
                            TREENODE tnNextParent;
                            var tnLastChildPrimary = tnLastUntouched;
                            TREENODE tnLastChildSecondary;
                            var tnPrev = tnDummyHeadInKillZone;
                            BranchLocationAction action;
                            int attachIndex;
                            int level;
                            bool expandBranch;
                            // UNDONE: There is a really bad issue here with initial subitem expansion. Initial subitem expansion is
                            // normally done when the list is first expanded. However, when items are shuffled, the subitem expansion
                            // state suddenly belongs to a different parent node. We need to wait until after all detached branches
                            // are reattached to be able to actually run the 'initial' subitem expansion, which now should only attempt
                            // to expand indices which do not yet have a corresponding treenode. For now, it is up to the providers to
                            // successfully locate the subitem expansions, or these items will be tossed. In the long run, we need
                            // to keep track of all of these objects and deal with after the main loop has complete.
                            bool dummyRequireSubItemExpansion;
                            int expansionCount;
                            int subItemIncr;
                            tnNext = tnPrev.NextSibling;
                            // UNDONE: Is this right? Another opinion below. If this is not done
                            // correctly, then we end up calling ExpandTreeNode below in the KeepBranch
                            // cases when we shouldn't and incorrectly create a new node/branch when
                            // the existing one is fine. This leads to a crash when tnChild is orphaned
                            // and its FirstPositionTracker is never cleared
                            var maxInsertLevel = insertLevels - 1;
                            while ((tnChild = tnNext) != null)
                            {
                                // Get the next node up front in case we delete this one
                                tnNext = tnChild.NextSibling;

                                // Expand nodes to the correct level
                                for (level = 0, tnCurParent = tn; level <= maxInsertLevel; ++level)
                                {
                                    if (tnCurParent.NoRelocate)
                                    {
                                        // Just move up the list, nodes we don't reattach
                                        // are deleted at the end.
                                        tnPrev = tnChild;
                                        break;
                                    }
                                    else
                                    {
                                        LocateObjectData locateData;
                                        if (tnChild.Branch == null)
                                        {
                                            expandBranch = tnCurParent.Expanded;
                                            var tnLocate = tnChild.FirstSubItem.RootNode;
                                            locateData = tnCurParent.Branch.LocateObject(
                                                tnLocate.Branch,
                                                tnLocate.ComplexSubItem ? ObjectStyle.SubItemRootBranch : ObjectStyle.SubItemExpansion,
                                                0);
                                            Debug.Assert(
                                                (BranchLocationAction)locateData.Options == BranchLocationAction.DiscardBranch
                                                || tnChild.FirstSubItem.ColumnOfRootNode(tnLocate) == locateData.Column);
                                        }
                                        else
                                        {
                                            expandBranch = tnChild.Expanded;
                                            locateData = tnCurParent.Branch.LocateObject(
                                                tnChild.Branch,
                                                ObjectStyle.ExpandedBranch,
                                                0);
                                        }
                                        attachIndex = locateData.Row;
                                        action = (BranchLocationAction)locateData.Options;
                                        switch (action)
                                        {
                                            case BranchLocationAction.RetrieveNewBranch:
                                                // This only makes sense at the last insertion level
                                                if (level == maxInsertLevel)
                                                {
                                                    //Use the returned Index to retrieve a new list. Go ahead and
                                                    //leave the current node in the kill list and get a brand new one.
                                                    tnPrev = tnChild;
                                                    try
                                                    {
                                                        tnChild = ExpandTreeNode(
                                                            tnCurParent, null, attachIndex, 0, false, out expansionCount, out subItemIncr);
                                                        tnPrev.TransferPositionTrackerTo(tnChild);
                                                        changeCount += expansionCount;
                                                        subItemChange += subItemIncr;
                                                        if (level == 0)
                                                        {
                                                            InsertIndexedNode(tnCurParent, tnChild, ref tnLastChildPrimary);
                                                        }
                                                        else
                                                        {
                                                            tnLastChildSecondary = null;
                                                            InsertIndexedNode(tnCurParent, tnChild, ref tnLastChildSecondary);
                                                        }
                                                    }
                                                    catch
                                                    {
                                                        // Ignore an error here, just let the expansion be lost.
                                                        level = maxInsertLevel + 1;
                                                    }
                                                }
                                                else
                                                {
                                                    goto case BranchLocationAction.DiscardBranch;
                                                }
                                                break;
                                            case BranchLocationAction.DiscardBranch:
                                                // The deletion of items in this branch have already been
                                                // accounted for. Just leave it in the dummy list and discard
                                                // it when we're done reattaching.
                                                tnPrev = tnChild;
                                                level = maxInsertLevel + 1;
                                                break;
                                            case BranchLocationAction.KeepBranchAtThisLevel:
                                            case BranchLocationAction.KeepBranch:
                                                // Detach from the list of nodes to insert
                                                tnPrev.NextSibling = tnNext;
                                                tnChild.NextSibling = null;
                                                if (action == BranchLocationAction.KeepBranchAtThisLevel)
                                                {
                                                    // Easy to do, just pretend we're at the last level and move on.
                                                    level = maxInsertLevel;
                                                }
                                                if (level == 0)
                                                {
                                                    // First level down, we do a little more work here than in the secondary branches

                                                    // Validate the attach index. We don't want to put a new
                                                    // branch on top of one outside our edit range.
                                                    if (start != -1
                                                        &&
                                                        (attachIndex < start || attachIndex >= maxIndexReattach))
                                                    {
                                                        // Undo detach
                                                        tnChild.NextSibling = tnPrev.NextSibling;
                                                        tnPrev.NextSibling = tnChild;
                                                        goto case BranchLocationAction.DiscardBranch;
                                                    }
                                                    else if (insertLevels == 0
                                                             || level == maxInsertLevel)
                                                    {
                                                        // Removing nodes only, just reattach
                                                        if (expandBranch)
                                                        {
                                                            changeCount += tnChild.FullCount;
                                                            subItemChange += tnChild.ExpandedSubItemGain;
                                                        }
                                                        subItemChange += tnChild.ImmedSubItemGain;
                                                        tnChild.Index = attachIndex;
                                                        InsertIndexedNode(tn, tnChild, ref tnLastChildPrimary);
                                                    }
                                                    else
                                                    {
                                                        tnNextParent = FindIndexedNode(
                                                            attachIndex,
                                                            (tnLastUntouched == null) ? tn.FirstChild : tnLastUntouched.NextSibling,
                                                            ref tnLastChildPrimary);
                                                        if (tnNextParent != null)
                                                        {
                                                            if (expandBranch && !tnNextParent.Expanded)
                                                            {
                                                                tnNextParent.Expanded = true;
                                                                changeCount += tnNextParent.FullCount;
                                                                subItemChange += tnNextParent.ExpandedSubItemGain;
                                                            }
                                                        }
                                                        else
                                                        {
                                                            tnNextParent = ExpandTreeNode(
                                                                tnCurParent, null, attachIndex, 0, false, out expansionCount,
                                                                out dummyRequireSubItemExpansion);
                                                            tnNextParent.Expanded = expandBranch;
                                                            if (expandBranch)
                                                            {
                                                                changeCount += tnNextParent.FullCount;
                                                                subItemChange += tnNextParent.ExpandedSubItemGain;
                                                            }
                                                            subItemChange += tnNextParent.ImmedSubItemGain;
                                                            InsertIndexedNode(tnCurParent, tnNextParent, ref tnLastChildPrimary);
                                                        }
                                                        tnCurParent = tnNextParent;
                                                    }
                                                }
                                                else if (level == maxInsertLevel)
                                                {
                                                    // Reattach existing node to current parent
                                                    tnLastChildSecondary = null;
                                                    tnChild.Index = attachIndex;
                                                    InsertIndexedNode(tnCurParent, tnChild, ref tnLastChildSecondary);
                                                    if (expandBranch)
                                                    {
                                                        changeCount += tnChild.FullCount;
                                                        subItemChange += tnChild.FullSubItemGain;
                                                    }
                                                    if (tnCurParent != tn)
                                                    {
                                                        ChangeFullCountRecursive(
                                                            tnCurParent, tnChild.Expanded ? tnChild.FullCount : 0, tnChild.FullSubItemGain,
                                                            tn);
                                                    }
                                                }
                                                else
                                                {
                                                    tnLastChildSecondary = null;
                                                    tnNextParent = FindIndexedNode(
                                                        attachIndex, tnCurParent.FirstChild, ref tnLastChildSecondary);
                                                    if (tnNextParent != null)
                                                    {
                                                        if (expandBranch && !tnNextParent.Expanded)
                                                        {
                                                            tnNextParent.Expanded = true;
                                                            changeCount += tnNextParent.FullCount;
                                                            subItemChange += tnNextParent.FullSubItemGain;
                                                            ChangeFullCountRecursive(
                                                                tnCurParent, tnNextParent.FullCount, tnNextParent.FullSubItemGain, tn);
                                                        }
                                                    }
                                                    else
                                                    {
                                                        tnNextParent = ExpandTreeNode(
                                                            tnCurParent, null, attachIndex, 0, false, out expansionCount,
                                                            out dummyRequireSubItemExpansion);
                                                        tnNextParent.Expanded = expandBranch;
                                                        if (expandBranch)
                                                        {
                                                            changeCount += tnNextParent.FullCount;
                                                            subItemChange += tnNextParent.FullSubItemGain;
                                                            ChangeFullCountRecursive(
                                                                tnCurParent, tnNextParent.FullCount, tnNextParent.FullSubItemGain, tn);
                                                        }
                                                        InsertIndexedNode(tnCurParent, tnNextParent, ref tnLastChildSecondary);
                                                    }
                                                    tnCurParent = tnNextParent;
                                                }
                                                break;
                                        }
                                    }
                                }
                            }
                        }
                        finally
                        {
                            // Delete any items that are still attached to the dummy head.
                            // We need to explicitly free here so that DestroyTreeNode is
                            // called correctly, GC can't do this automatically for us.
                            tnNext = tnDummyHeadInKillZone.NextSibling;
                            while ((tnChild = tnNext) != null)
                            {
                                tnNext = tnChild.NextSibling;
                                FreeRecursive(ref tnChild, ref detachedTrackers);
                            }
                            // tnDummyHeadInKillZone will be destroyed via the ref parameter
                        }

                        // Trigger a second pass
                        tnDummyHeadInKillZone = tnDummyHead;
                        tnDummyHead = null;
                    } // tnDummyHeadInKillZone != null
                } // tnChild != null

                // Attempt to reattach any trackable objects back into the new tree structure
                if (detachedTrackers != null)
                {
                    int reattachChangeCount;
                    int reattachSubItemChangeCount;
                    detachedTrackers.QueryReattachObjects(
                        this, tn, (removeLevels == 0) ? insertLevels : insertLevels - 1, out reattachChangeCount,
                        out reattachSubItemChangeCount);
                    changeCount += reattachChangeCount;
                    subItemChange += reattachSubItemChangeCount;
                }
                if ((changeCount + subItemChange) != 0)
                {
                    ChangeFullCountRecursive(tn, changeCount, subItemChange);
                }
            }
        }
コード例 #7
0
        private void ShiftBranchLevels(
            IBranch branch, int removeLevels, int insertLevels, int depth, IBranch replacementBranch, ILevelShiftAdjuster branchTester,
            int startIndex, int count, int newCount)
        {
            //UNDONE: Do some argument validation
            if (removeLevels < 0)
            {
                throw new ArgumentOutOfRangeException("removeLevels");
            }
            if (insertLevels < 0)
            {
                throw new ArgumentOutOfRangeException("insertLevels");
            }
            if (depth < 0)
            {
                throw new ArgumentOutOfRangeException("depth");
            }
            if ((insertLevels + removeLevels) == 0)
            {
                throw new ArgumentException(VirtualTreeStrings.GetString(VirtualTreeStrings.ShiftBranchLevelsExceptionDesc));
            }

            var tnFirst = LocateTrackedNode(branch);
            var tn = tnFirst;
            Debug.Assert(tn != null); //Expect LocateTrackedNode to throw otherwise
            if (startIndex != -1)
            {
                // Use the original count if newCount not specified
                if (newCount == -1)
                {
                    newCount = count;
                }

                // Do a quick sanity check to make sure that the current branch is consistent
                // with the past in properties. This is called after the branch is modified
                // to reflect th new settings.
                var testBranch = (replacementBranch == null) ? branch : replacementBranch;
                if (testBranch.VisibleItemCount != (tn.ImmedCount + newCount - count))
                {
                    throw new ArgumentException(string.Empty, "newCount"); //UNDONE: EXCEPTION
                }
            }
            int startFullCount;
            int startExpandedSubItemGain;
            // Make sure we are officially shuffling a list before the replacement
            // branch is blasted into the structure.
            ListShuffle = true;
            var reattachTracker = false;
            try
            {
                // Take care of the replacementBranch up front
                if (replacementBranch != null)
                {
                    //UNDONE: Cache a delegate for this.OnBranchModification
                    branch.OnBranchModification -= OnBranchModification;
                    replacementBranch.OnBranchModification += OnBranchModification;
                    var newFlags = replacementBranch.Features;
                    while (tn != null)
                    {
                        tn.Branch = replacementBranch;
                        tn.SetFlags(newFlags);
                        tn.Dynamic = true; // Set regardless
                        tn = tn.NextNode;
                    }
                    Debug.Assert(tnFirst.Dynamic); // LocateTrackedNode wouldn't have worked without this
                    // Get the branch out of the tracked list altogether. Readding the list of
                    // nodes with the replacement branch is deferred until all the work is done because
                    // it is possible to replace with an existing branch, or expand the existing branch
                    // in a different locaion while reattaching tree nodes. As a simple example, the top
                    // level branch can be eliminated by calling ShiftBranchLevels(branch, 2, 1, replacement)
                    // where the replacement a child list of the parent (useful if parent has 1 item in it).
                    myNodeTracker.RemoveBranch(branch);
                    reattachTracker = true;
                    tn = tnFirst;
                }

                while (tn != null)
                {
                    startFullCount = tn.FullCount;
                    startExpandedSubItemGain = tn.ExpandedSubItemGain;
                    RealignTreeNodeLevelShift(tn, removeLevels, insertLevels, depth, branchTester, startIndex, count, newCount, true);
                    DoRealignNotification(tn, startFullCount, startExpandedSubItemGain);
                    tn = tn.NextNode;
                }
            }
            finally
            {
                if (reattachTracker)
                {
                    myNodeTracker.Add(replacementBranch, tnFirst);
                }
                ClearPositionCache(); //Cached absolute information is toast.
                ListShuffle = false;
            }
        }
コード例 #8
0
 /// <summary>
 ///     For all expansions of 'branch', first remove 'removeLevels' then insert 'insertLevels' branches from
 ///     all branches 'depth' levels below the current branch. Replace the tree's reference to the current
 ///     branch object with a reference to 'replacementBranch' (if set). If depth>0, then a new VisibleItemCount
 ///     is retrieved from the branch and all sub branches are relocated. If 'branchTester' is not null, then
 ///     it is used to adjust the operation as described in the ILevelShiftAdjuster interface.
 /// </summary>
 /// <param name="branch"></param>
 /// <param name="removeLevels">The number of levels to remove</param>
 /// <param name="insertLevels">The number of levels to insert</param>
 /// <param name="depth">The number of levels below the starting branch to skip before removing and inserting levels</param>
 /// <param name="replacementBranch">Replace the starting branch with this one</param>
 /// <param name="branchTester">A callback interface for advanced level shifting</param>
 public ShiftBranchLevelsData(
     IBranch branch, int removeLevels, int insertLevels, int depth, IBranch replacementBranch, ILevelShiftAdjuster branchTester)
     :
         this(branch, removeLevels, insertLevels, depth, replacementBranch, branchTester, -1, -1, -1)
 {
 }
コード例 #9
0
 /// <summary>
 ///     For all expansions of 'branch', first remove 'removeLevels' then insert 'insertLevels' branches from
 ///     all branches 'depth' levels below the current branch. Replace the tree's reference to the current
 ///     branch object with a reference to 'replacementBranch' (if set). The operation affects 'count' items
 ///     at position 'startIndex', which are replaced with 'newCount' new items. If depth>0, then a new VisibleItemCount
 ///     is retrieved from the branch and all sub branches are relocated. StartIndex/Count/NewCount affect only the passed in branch.
 ///     If Start/Count/NewCount are used with a replacement branch, then the replacement branch must be
 ///     consistent with the original. If 'branchTester' is not null, then it is used to adjust the operation
 ///     as described in the ILevelShiftAdjuster interface.
 /// </summary>
 /// <param name="branch"></param>
 /// <param name="removeLevels">The number of levels to remove</param>
 /// <param name="insertLevels">The number of levels to insert</param>
 /// <param name="depth">The number of levels below the starting branch to skip before removing and inserting levels</param>
 /// <param name="replacementBranch">Replace the starting branch with this one</param>
 /// <param name="branchTester">A callback interface for advanced level shifting</param>
 /// <param name="startIndex">The starting index of the items being adjusted in the starting branch</param>
 /// <param name="count">The original count for the number of items being adjusted in the starting branch</param>
 /// <param name="newCount">The replacement count for the number of items being adjusted in the starting branch</param>
 public ShiftBranchLevelsData(
     IBranch branch, int removeLevels, int insertLevels, int depth, IBranch replacementBranch, ILevelShiftAdjuster branchTester,
     int startIndex, int count, int newCount)
 {
     myBranch = branch;
     myRemoveLevels = removeLevels;
     myInsertLevels = insertLevels;
     myDepth = depth;
     myReplacementBranch = replacementBranch;
     myBranchTester = branchTester;
     myStartIndex = startIndex;
     myCount = count;
     myNewCount = newCount;
 }