コード例 #1
0
 public static bool Add(
     TREENODE parentNode, PositionTracker[] positions, int positionIndex, int relativeRow, int relativeColumn,
     ref NODEPOSITIONTRACKER lastTracker)
 {
     Debug.Assert((lastTracker == null) || (lastTracker.myNext == null));
     Debug.Assert(!parentNode.NoTracking);
     var retVal = false;
     NODEPOSITIONTRACKER newTracker = null;
     if (parentNode.DefaultTracking)
     {
         newTracker = new NODEPOSITIONTRACKER_Default(parentNode, positions, positionIndex, relativeRow, relativeColumn);
     }
     else
     {
         object trackerObject;
         var dummyOptions = 0;
         trackerObject = parentNode.Branch.GetObject(relativeRow, relativeColumn, ObjectStyle.TrackingObject, ref dummyOptions);
         if (trackerObject != null)
         {
             newTracker = new NODEPOSITIONTRACKER_Dynamic(parentNode, positions, positionIndex, trackerObject);
         }
     }
     if (newTracker != null)
     {
         if (lastTracker != null)
         {
             lastTracker.myNext = newTracker;
         }
         lastTracker = newTracker;
         retVal = true;
     }
     return retVal;
 }
コード例 #2
0
 public void Remove(IBranch branch, TREENODE value)
 {
     var existing = this[branch];
     if (existing == null)
     {
         return;
     }
     if (existing == value)
     {
         if (existing.NextNode == null)
         {
             Dictionary.Remove(branch);
         }
         else
         {
             existing.NextNode.PrevNode = null;
             Dictionary[branch] = existing.NextNode;
         }
     }
     else
     {
         Debug.Assert(value.PrevNode != null);
         value.PrevNode.NextNode = existing.NextNode;
         if (value.NextNode != null)
         {
             value.NextNode.PrevNode = value.PrevNode;
         }
     }
 }
コード例 #3
0
        // Helper function to insert a node into the middle of an existing sibling list of
        // child nodes. The hint item is used to optimize adding items in order.
        private static void InsertIndexedNode(TREENODE tnParent, TREENODE tnChild, ref TREENODE tnHintChild)
        {
            TREENODE tnPrev;
            tnChild.Parent = tnParent;
            if (tnParent.FirstChild != null)
            {
                if (tnHintChild != null
                    &&
                    tnChild.Index > tnHintChild.Index)
                {
                    //Optimized for case where expanded lists are still in order
                    tnPrev = tnChild;
                    tnChild = tnHintChild;
                    tnHintChild = tnPrev;
                }
                else
                {
                    //Have to search from beginning and re-insert
                    tnHintChild = tnChild;
                    tnChild = tnParent.FirstChild;
                }

                tnPrev = null;
                while (tnHintChild.Index >= tnChild.Index)
                {
                    tnPrev = tnChild;
                    if (null == (tnChild = tnPrev.NextSibling))
                    {
                        break;
                    }
                }
                if (tnPrev != null)
                {
                    Debug.Assert(tnPrev.Index != tnHintChild.Index);
                    tnPrev.NextSibling = tnHintChild;
                }
                else
                {
                    tnParent.FirstChild = tnHintChild;
                }
                tnHintChild.NextSibling = tnChild;
            }
            else
            {
                tnParent.FirstChild = tnHintChild = tnChild;
                tnChild.NextSibling = null;
            }
        }
コード例 #4
0
        private void RealignTreeNode(TREENODE tn)
        {
            TREENODE tnChild;
            TREENODE tnChildTmp;
            TREENODE tnLastChild;
            int ChangeCount;

            //Make sure the number of items is in sync 
            ChangeCount = 0;
            ChangeCount -= tn.ImmedCount;
            tn.ImmedCount = tn.Branch.VisibleItemCount;
            ChangeCount += tn.ImmedCount;

            //Make sure the expanded items are in sync
            if (tn.NoRelocate)
            {
                //Relocation isn't supported. Just
                //close down any open children
                tnChildTmp = tn.FirstChild;
                tn.FirstChild = null;
                while ((tnChild = tnChildTmp) != null)
                {
                    if (tnChild.Expanded)
                    {
                        ChangeCount -= tnChild.FullCount;
                    }
                    tnChildTmp = tnChild.NextSibling; //Get child before calling FreeRecursive
                    FreeRecursive(ref tnChild);
                }
            }
            else
            {
                int ExpansionCount;
                int ReloadIndex;
                BranchLocationAction action;
                //This is similar to above, but more complicated
                tnChildTmp = tn.FirstChild;
                tn.FirstChild = null;
                tnLastChild = null;
                while ((tnChild = tnChildTmp) != null)
                {
                    tnChildTmp = tnChild.NextSibling; //Get next child before calling FreeRecursive
                    var locateData = tn.Branch.LocateObject(tnChild.Branch, ObjectStyle.ExpandedBranch, 0);
                    // UNDONE_MC: locateData.Column not used
                    tnChild.Index = locateData.Row;
                    action = (BranchLocationAction)locateData.Options;
                    switch (action)
                    {
                        case BranchLocationAction.DiscardBranch:
                            //Get rid of list, just like in norelocate case
                            if (tnChild.Expanded)
                            {
                                ChangeCount -= tnChild.FullCount;
                            }
                            FreeRecursive(ref tnChild);
                            break;
                        case BranchLocationAction.KeepBranchAtThisLevel:
                        case BranchLocationAction.KeepBranch:
                            InsertIndexedNode(tn, tnChild, ref tnLastChild);
                            break;
                        case BranchLocationAction.RetrieveNewBranch:
                            //Use the returned Index to retrieve a new list
                            ReloadIndex = tnChild.Index;
                            ChangeCount -= tnChild.FullCount;
                            FreeRecursive(ref tnChild);
                            try
                            {
                                int subItemIncr;
                                tnChild = ExpandTreeNode(
                                    tn, null, ReloadIndex, COLUMN_ZERO /*UNDONE_MC*/, false,
                                    out ExpansionCount, out subItemIncr);
                            }
                            catch
                            {
                                // Ignore an error here, just let the expansion be lost.
                                break;
                            }
                            goto case BranchLocationAction.KeepBranch;
                    }
                }
            }

            if (ChangeCount != 0)
            {
                ChangeFullCountRecursive(tn, ChangeCount, 0 /*UNDONE_MC*/);
            }
        }
コード例 #5
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);
                }
            }
        }
コード例 #6
0
 // Modify the count up to, but not including, the ceiling node
 private void ChangeFullCountRecursive(TREENODE tn, int itemIncr, int subItemIncr, TREENODE tnCeiling)
 {
     int subItemRemainder;
     ChangeFullCountRecursive(tn, itemIncr, subItemIncr, tnCeiling, out subItemRemainder);
 }
コード例 #7
0
        private TREENODE ExpandTreeNode(
            TREENODE parentNode, TREENODE startNode, int row, int column, ObjectStyle branchStyle, bool insertNewChild,
            out ExpansionOptions expansionOptions, out int itemIncr, out bool requireInitialSubItemExpansion)
        {
            TREENODE tnCur;
            TREENODE tn1;
            TREENODE tn2;
            TREENODE newChildNode = null;
            // Don't test expandable while loading a complex column, the request can be ambiguous.
            // Just go ahead and fire off the request. A null return will leave the cell as a simple item.
            Debug.Assert(
                branchStyle == ObjectStyle.SubItemRootBranch || parentNode.Branch.IsExpandable(row, column),
                "GetExpandable should be called before this is attempted");
            IBranch newBranch;
            try
            {
                SetStateFlag(TreeStateFlags.InExpansion, true);
                var options = 0;
                newBranch = parentNode.Branch.GetObject(row, column, branchStyle, ref options) as IBranch;
                expansionOptions = (ExpansionOptions)options;
            }
            finally
            {
                SetStateFlag(TreeStateFlags.InExpansion, false);
            }

            itemIncr = 0;
            requireInitialSubItemExpansion = false;
            newChildNode = null;
            if (newBranch != null)
            {
                itemIncr = newBranch.VisibleItemCount; //ExpansionCount can be 0 on success, don't check
                // UNDONE_MC: Only allow multi-column children if the parent branch does,
                // or if this is the root node of a complex item in the last column of a tree.
                var allowMultiColumn = true;
                tnCur = CreateTreeNode(startNode, newBranch, this, allowMultiColumn, parentNode.InSubItemColumn, parentNode.MultiColumn);
                tnCur.Index = row;
                tnCur.ImmedCount = tnCur.FullCount = itemIncr;
                if (allowMultiColumn
                    && tnCur.MultiColumn
                    && tnCur.ComplexColumns)
                {
                    requireInitialSubItemExpansion = true;
                }
                tnCur.Expanded = true;
                tnCur.AllowRecursion = (expansionOptions & ExpansionOptions.BlockRecursion) == 0;
                tnCur.UpdateDelayed = false;
                tnCur.Parent = parentNode;
                if (tnCur.Dynamic)
                {
                    AddTrackedNode(newBranch, tnCur);
                }
                newChildNode = tnCur;
                if (insertNewChild && startNode == null)
                {
                    //Place node in proper position in child chain based on index
                    tn1 = parentNode.FirstChild;
                    tn2 = null;
                    if (tn1 != null)
                    {
                        while (row >= tn1.Index)
                        {
                            Debug.Assert(tn1.Index != row, "TrackIndex screwed up");
                            tn2 = tn1;
                            if (null == (tn1 = tn2.NextSibling))
                            {
                                break;
                            }
                        }
                        if (tn2 != null)
                        {
                            tn2.NextSibling = tnCur;
                        }
                        else
                        {
                            parentNode.FirstChild = tnCur;
                        }
                        tnCur.NextSibling = tn1; //tn1 May be null, not worth the check
                    }
                    else
                    {
                        parentNode.FirstChild = tnCur;
                    }
                }
            }
            return newChildNode;
        }
コード例 #8
0
 private TREENODE ExpandTreeNode(
     TREENODE parentNode, TREENODE startNode, int row, int column, bool insertNewChild, out int itemIncr,
     out bool requireInitialSubItemExpansion)
 {
     ExpansionOptions options;
     return ExpandTreeNode(
         parentNode,
         startNode,
         row,
         column,
         column == 0 ? ObjectStyle.ExpandedBranch : ObjectStyle.SubItemExpansion,
         insertNewChild,
         out options,
         out itemIncr,
         out requireInitialSubItemExpansion);
 }
コード例 #9
0
 private static VirtualTreeCoordinate EnumAbsoluteIndices(int index, ref TREENODE nextNode, out int singleColumnAbsIndex)
 {
     singleColumnAbsIndex = VirtualTreeConstant.NullIndex;
     if (nextNode != null)
     {
         int singleColumnSubItemAdjust;
         var coord = FindAbsoluteIndex(nextNode, index, out singleColumnSubItemAdjust);
         if (coord.Column == 0)
         {
             singleColumnAbsIndex = coord.Row - singleColumnSubItemAdjust;
         }
         nextNode = nextNode.NextNode;
         return coord;
     }
     return VirtualTreeCoordinate.Invalid;
 }
コード例 #10
0
        /// <summary>
        ///     Get the absolute index of an item in a TREENODE
        /// </summary>
        /// <param name="tn">A TREENODE in this tree</param>
        /// <param name="index">The relative index of the item within the TREENODE</param>
        /// <param name="singleColumnSubItemAdjust">The number of blanks before the item. Undefined if Column > 0.</param>
        /// <returns>A valid coordinate, or VirtualTreeCoordinate.Invalid if the item does not currently appear.</returns>
        private static VirtualTreeCoordinate FindAbsoluteIndex(TREENODE tn, int index, out int singleColumnSubItemAdjust)
        {
            Debug.Assert(tn != null);
            TREENODE tnTmp;
            var isExpanded = false;
            var column = 0;
            int tnImmedSubItemGain; // ImmedSubItemGain is virtual, limit number of calls
            int tmpSubItemGain;
            singleColumnSubItemAdjust = 0;
            //Walk up parent chain to find offset
            tnImmedSubItemGain = tn.ImmedSubItemGain;
            singleColumnSubItemAdjust = tnImmedSubItemGain;
            var absIndex = tn.Index + index + tnImmedSubItemGain + 1;
            while ((tn != null)
                   && (isExpanded = tn.MultiLine))
            {
                if (tn.Expanded)
                {
                    //First, take into account all expanded nodes at this level
                    //which occur before our start position
                    tnTmp = tn.FirstChild;
                    while ((tnTmp != null)
                           && tnTmp.Index < index)
                    {
                        if (tnTmp.Expanded)
                        {
                            tmpSubItemGain = tnTmp.FullSubItemGain;
                            absIndex += tnTmp.FullCount;
                        }
                        else
                        {
                            tmpSubItemGain = tnTmp.ImmedSubItemGain;
                        }
                        absIndex += tmpSubItemGain;
                        singleColumnSubItemAdjust += tmpSubItemGain;
                        tnTmp = tnTmp.NextSibling;
                    }
                }
                index = tn.Index;
                tnTmp = tn;
                if ((tn = tn.Parent) != null)
                {
                    tnImmedSubItemGain = tn.ImmedSubItemGain;
                    if (tnTmp.SubItemRoot)
                    {
                        // Track how many columns we need to shift for this column
                        column += tn.FirstSubItem.ColumnOfRootNode(tnTmp);

                        // The final index does not include the new parent's subitem gain,
                        // and there is no automatic plus one like with a normal expanded branch 
                        absIndex += tn.Index;
                        if (!tnTmp.ComplexSubItem)
                        {
                            // Expanded cell
                            ++absIndex;
                        }
                    }
                    else
                    {
                        singleColumnSubItemAdjust += tnImmedSubItemGain;
                        absIndex += tn.Index + tnImmedSubItemGain + 1;
                    }
                }
            }
            return isExpanded ? new VirtualTreeCoordinate(absIndex, column) : VirtualTreeCoordinate.Invalid;
        }
コード例 #11
0
            public void QueryReattachObjects(
                VirtualTree tree, TREENODE tnStartParent, int maxLevels, out int changeCount, out int subItemChangeCount)
            {
                // Return the ChangeCount wrt/tnParent
                NODEPOSITIONTRACKER_Dynamic nptPrev = null;
                var nptNext = this;
                NODEPOSITIONTRACKER_Dynamic nptCur;
                //TREENODE tnLastChildPrimary = null;
                TREENODE tnLastChildSecondary;
                int level;
                int attachIndex;
                int expansionCount;
                TrackingObjectAction action;
                TREENODE tnCurParent;
                TREENODE tnNextParent;
                changeCount = 0;
                subItemChangeCount = 0;
                while ((nptCur = nptNext) != null)
                {
                    // Grab the next up front
                    nptNext = (NODEPOSITIONTRACKER_Dynamic)nptCur.myNextSibling;

                    // Walk the parents to see where this goes
                    for (level = 0, tnCurParent = tnStartParent; level <= maxLevels; ++level)
                    {
                        if (tnCurParent.NoTracking)
                        {
                            // Move along, reattach failed
                            nptPrev = nptCur;
                            break;
                        }
                        else
                        {
                            var locateData = tnCurParent.Branch.LocateObject(nptCur.myTrackerObject, ObjectStyle.TrackingObject, 0);
                            attachIndex = locateData.Row;
                            action = (TrackingObjectAction)locateData.Options;
                            // Protect against inconsistent data from clients
                            if (attachIndex == -1)
                            {
                                action = TrackingObjectAction.NotTracked;
                            }
                            switch (action)
                            {
                                case TrackingObjectAction.ThisLevel:
                                    // Detach from the list of nodes to insert
                                    if (nptPrev != null)
                                    {
                                        nptPrev.myNextSibling = nptNext;
                                    }
                                    nptCur.myNextSibling = tnCurParent.FirstPositionTracker;
                                    tnCurParent.FirstPositionTracker = nptCur;
                                    nptCur.myParentNode = tnCurParent;

                                    // Easy to break out, just pretend we're at the last level and move on.
                                    level = maxLevels;
                                    break;
                                case TrackingObjectAction.NextLevel:
                                    tnLastChildSecondary = null;
                                    tnNextParent = FindIndexedNode(attachIndex, tnCurParent.FirstChild, ref tnLastChildSecondary);
                                    if (tnNextParent != null)
                                    {
                                        if (!tnNextParent.Expanded)
                                        {
                                            tnNextParent.Expanded = true;
                                            changeCount += tnNextParent.FullCount;
                                            subItemChangeCount += tnNextParent.ExpandedSubItemGain;
                                                // UNDONE_MC: Is ExpandedSubItemGain correct?
                                            tree.ChangeFullCountRecursive(
                                                tnCurParent, tnNextParent.FullCount, tnNextParent.ExpandedSubItemGain, tnStartParent);
                                        }
                                    }
                                    else
                                    {
                                        int subItemIncr;
                                        // UNDONE_MC: Need a column
                                        tnNextParent = tree.ExpandTreeNode(
                                            tnCurParent, null, attachIndex, COLUMN_ZERO, false, out expansionCount, out subItemIncr);
                                        changeCount += tnNextParent.FullCount;
                                        subItemChangeCount += subItemIncr;
                                        tree.ChangeFullCountRecursive(tnCurParent, tnNextParent.FullCount, subItemIncr, tnStartParent);
                                        InsertIndexedNode(tnCurParent, tnNextParent, ref tnLastChildSecondary);
                                    }
                                    tnCurParent = tnNextParent;
                                    break;
                                case TrackingObjectAction.NotTracked:
                                case TrackingObjectAction.NotTrackedReturnParent:
                                    // Break out
                                    nptPrev = nptCur;
                                    level = maxLevels;
                                    break;
                            }
                        }
                    }
                }
            }
コード例 #12
0
        private void DoRealignNotification(TREENODE tn, int startFullCount, int startExpandedSubItemGain)
        {
            var fireNormalEvent = ItemCountChanged != null;
            var fireSingleColumnEvent = GetStateFlag(TreeStateFlags.FireSingleColumnItemCountChanged);
            if (fireNormalEvent || fireSingleColumnEvent)
            {
                int singleColumnAbsIndex;
                ClearPositionCache(); //Cached absolute information is toast.
                var tnNext = tn; //Ignore next
                var coord = EnumAbsoluteIndices(VirtualTreeConstant.NullIndex, ref tnNext, out singleColumnAbsIndex);
                if (coord.IsValid
                    || tn == myRootNode)
                {
                    var startFullExpansionCount = startFullCount + startExpandedSubItemGain;
                    var fullCount = tn.FullCount;
                    var expandedSubItemGain = tn.ExpandedSubItemGain;
                    var fullExpansionCount = fullCount + expandedSubItemGain;
                    var resetExpansion = false;
                    ITree singleTree = null;
                    SingleColumnView singleView = null;
                    if ((fullExpansionCount != 0)
                        && (myRedrawCount == 0))
                    {
                        //Vegas#47930  If Redraw is on, then firing notifications may
                        //call back into this object, so we have to keep the object completely
                        //in sync with the events we're firing.  The OnDeleteItems call indicates
                        //that the entire branch is collapsed, so fake that state, then unhide it
                        //before the OnInsertItems call.

                        //Temporarily empty out node so DeleteItems notifications are accurate
                        Debug.Assert(tn.Expanded, "EnumAbsoluteIndices gave bad data");
                        ChangeFullCountRecursive(tn, -fullCount, -expandedSubItemGain);
                        Debug.Assert(tn.TotalCount == tn.ImmedSubItemGain && tn.Expanded, "Node should be expanded, but empty");
                        tn.Expanded = false;
                        resetExpansion = true;
                    }
                    if (fireNormalEvent && startFullExpansionCount != 0)
                    {
                        DelayTurnOffRedraw();
                        // UNDONE_MC: UNDONE_NOW:
                        //ItemsDeleted(this, absIndex + 1, startFullExpansionCount);
                        ItemCountChanged(
                            this,
                            new ItemCountChangedEventArgs(
                                this, coord.Row, coord.Column, -startFullExpansionCount, coord.Row, 0, null, false));
                    }
                    if (fireSingleColumnEvent
                        && singleColumnAbsIndex != VirtualTreeConstant.NullIndex
                        && startFullCount != 0)
                    {
                        DelayTurnOffSingleColumnRedraw();
                        singleTree = SingleColumnTree;
                        singleView = singleTree as SingleColumnView;
                        singleView.myItemCountChanged(
                            singleTree,
                            new ItemCountChangedEventArgs(
                                singleTree, singleColumnAbsIndex, 0, -startFullCount, singleColumnAbsIndex, 0, null, false));
                    }
                    if (resetExpansion)
                    {
                        tn.Expanded = true;
                        ChangeFullCountRecursive(tn, fullCount, expandedSubItemGain);
                        ClearPositionCache(); //Cached absolute information is toast.
                    }
                    if (fireNormalEvent && fullExpansionCount != 0)
                    {
                        DelayTurnOffRedraw();
                        // UNDONE_MC: UNDONE_NOW:
                        //ItemsInserted(this, absIndex, fullExpansionCount);
                        ItemCountChanged(
                            this,
                            new ItemCountChangedEventArgs(this, coord.Row, coord.Column, fullExpansionCount, coord.Row, 0, null, false));
                    }
                    if (fireSingleColumnEvent
                        && singleColumnAbsIndex != VirtualTreeConstant.NullIndex
                        && fullCount != 0)
                    {
                        DelayTurnOffSingleColumnRedraw();
                        if (singleTree == null)
                        {
                            singleTree = SingleColumnTree;
                            singleView = singleTree as SingleColumnView;
                        }
                        singleView.myItemCountChanged(
                            singleTree,
                            new ItemCountChangedEventArgs(
                                singleTree, singleColumnAbsIndex, 0, fullCount, singleColumnAbsIndex, 0, null, false));
                    }
                }
            }
        }
コード例 #13
0
 public NODEPOSITIONTRACKER_Dynamic(TREENODE parentNode, PositionTracker[] positions, int positionIndex, object trackerObject)
     :
         base(parentNode, positions, positionIndex)
 {
     myTrackerObject = trackerObject;
 }
コード例 #14
0
 public NODEPOSITIONTRACKER_Default(
     TREENODE parentNode, PositionTracker[] positions, int positionIndex, int relativeRow, int relativeColumn)
     :
         base(parentNode, positions, positionIndex)
 {
     myRelativeRow = relativeRow;
     myRelativeColumn = relativeColumn;
 }
コード例 #15
0
 public void ReBranchTreeNode(IBranch newBranch, TREENODE value)
 {
     Remove(value.Branch, value);
     Add(newBranch, value);
     value.Branch = newBranch;
 }
コード例 #16
0
 private void AddTrackedNode(IBranch branch, TREENODE tn)
 {
     NodeTracker.Add(branch, tn);
 }
コード例 #17
0
        private static TREENODE CreateTreeNode(
            TREENODE startNode, IBranch branch, VirtualTree tree, bool allowMultiColumn, bool inSubItemColumn, bool multiColumnParent)
        {
            Debug.Assert(startNode == null || (startNode.Branch == null && startNode.GetType() == typeof(TREENODE_Complex)));
            BranchFeatures tf;
            TREENODE retVal;
            tf = branch.Features;
            var isDynamic = TREENODE.RequireDynamic(tf);
            var supportMultiColumn = tree.MultiColumnSupport;
            allowMultiColumn &= supportMultiColumn;
            var isMultiColumn = false;
            if (allowMultiColumn)
            {
                var mcTest = branch as IMultiColumnBranch;
                // Checking the column count allows a single wrapper class to
                // support either single or multicolumn branches
                isMultiColumn = mcTest != null && mcTest.ColumnCount > 1;
            }

            if (isDynamic)
            {
                if (TREENODE.RequireUpdatable(tf))
                {
                    retVal = isMultiColumn
                                 ? new TREENODE_Multi_Tracked_Updatable()
                                 : (supportMultiColumn
                                        ? (multiColumnParent
                                               ? (new TREENODE_Complex_Tracked_Updatable()) as TREENODE
                                               : new TREENODE_Single_Tracked_Updatable())
                                        : new TREENODE_Tracked_Updatable());
                }
                else
                {
                    retVal = isMultiColumn
                                 ? new TREENODE_Multi_Tracked()
                                 : (supportMultiColumn
                                        ? (multiColumnParent
                                               ? (new TREENODE_Complex_Tracked()) as TREENODE
                                               : new TREENODE_Single_Tracked())
                                        : new TREENODE_Tracked());
                }
            }
            else if (TREENODE.RequireUpdatable(tf))
            {
                retVal = isMultiColumn
                             ? new TREENODE_Multi_Updatable()
                             : (supportMultiColumn
                                    ? (multiColumnParent
                                           ? (new TREENODE_Complex_Updatable()) as TREENODE
                                           : new TREENODE_Single_Updatable())
                                    : new TREENODE_Updatable());
            }
            else
            {
                retVal = isMultiColumn
                             ? new TREENODE_Multi()
                             : (supportMultiColumn
                                    ? (multiColumnParent
                                           ? ((startNode == null) ? new TREENODE_Complex() : startNode)
                                           : new TREENODE_Single())
                                    : new TREENODE());
            }
            retVal.Branch = branch;
            retVal.SetFlags(tf);
            retVal.MultiColumn = isMultiColumn;
            retVal.AllowMultiColumnChildren = allowMultiColumn;
            retVal.InSubItemColumn = inSubItemColumn;
            if (retVal.CallUpdate)
            {
                retVal.UpdateCounter = branch.UpdateCounter;
            }
            branch.OnBranchModification += tree.OnBranchModification;

            if (startNode != null
                && startNode != retVal)
            {
                var subItem = startNode.FirstSubItem;
                retVal.FirstSubItem = subItem;
                retVal.ImmedSubItemGain = startNode.ImmedSubItemGain;
                retVal.FullSubItemGain = startNode.FullSubItemGain;
                while (subItem != null)
                {
                    subItem.RootNode.Parent = retVal;
                    subItem = subItem.NextSibling;
                }
                TREENODE tnPrev = null;
                var tnTest = startNode.Parent.FirstChild;
                while (tnTest != startNode)
                {
                    tnPrev = tnTest;
                    tnTest = tnTest.NextSibling;
                }
                if (tnPrev == null)
                {
                    startNode.Parent.FirstChild = retVal;
                    retVal.NextSibling = startNode.NextSibling;
                }
                else
                {
                    tnPrev.NextSibling = retVal;
                    retVal.NextSibling = startNode.NextSibling;
                }
            }

            return retVal;
        }
コード例 #18
0
 /// <summary>
 ///     Find the column number of the given node in the
 ///     subitem chain starting with this item
 /// </summary>
 /// <param name="tn">The root node of this item or one of its trailing siblings</param>
 /// <returns>Column number</returns>
 public int ColumnOfRootNode(TREENODE tn)
 {
     var sn = this;
     while (sn.RootNode != tn)
     {
         sn = sn.NextSibling;
     }
     Debug.Assert(sn != null);
     return sn.Column;
 }
コード例 #19
0
 private TREENODE ExpandTreeNode(
     TREENODE parentNode, TREENODE startNode, int row, int column, ObjectStyle branchStyle, bool insertNewChild,
     out ExpansionOptions expansionOptions, out int itemIncr, out int subItemIncr)
 {
     bool requireInitialSubItemExpansion;
     subItemIncr = 0;
     var retVal = ExpandTreeNode(
         parentNode,
         startNode,
         row,
         column,
         branchStyle,
         insertNewChild,
         out expansionOptions,
         out itemIncr,
         out requireInitialSubItemExpansion);
     if (retVal != null && requireInitialSubItemExpansion)
     {
         try
         {
             // UNDONE_MC: The full subitem gain will be higher than the immediate
             // gain if the expanded items are also complex
             ExpandInitialComplexSubItems(retVal as TREENODE_Multi, out subItemIncr);
         }
         catch (Exception ex)
         {
             Debug.Assert(false, ex.Message, ex.StackTrace);
             // Swallow this exception. The tree is left in a very bad state
             // if we get this far into an expansion and then throw.
         }
         finally
         {
             // These values may be positive already set if startnode is set,
             // so increment instead of blindly setting to subItemIncr.
             retVal.FullSubItemGain += subItemIncr;
         }
     }
     return retVal;
 }
コード例 #20
0
 private bool TrackPosition(ref PositionTracker tracker, out TREENODE tnParent, out int relativeRow, out int relativeColumn)
 {
     relativeRow = VirtualTreeConstant.NullIndex;
     relativeColumn = 0;
     tnParent = null;
     var startRow = tracker.StartRow;
     if (startRow != VirtualTreeConstant.NullIndex
         && startRow < (this as ITree).VisibleItemCount)
     {
         var column = tracker.Column;
         var pos = TrackCell(startRow, ref column);
         if (!pos.ParentNode.NoTracking)
         {
             tnParent = pos.ParentNode;
             relativeRow = pos.Index;
             relativeColumn = column;
         }
     }
     return tnParent != null;
 }
コード例 #21
0
        // An advanced version of this routine to deal with multi column scenarios
        private void ChangeFullCountRecursive(
            TREENODE tn, int itemIncr, int subItemIncr, TREENODE tnCeiling, ref AffectedSubItems affectedColumns, out int rowIncr,
            out SubItemColumnAdjustment[] subItemChanges)
        {
            Debug.Assert(tn != null, "");
            if (MultiColumnSupport)
            {
                var affectedColumnsCount = affectedColumns.Count;
                subItemChanges = (affectedColumnsCount == 0) ? null : new SubItemColumnAdjustment[affectedColumnsCount];
                rowIncr = 0;
                while (tn != tnCeiling)
                {
                    tn.FullCount += itemIncr;
                    tn.FullSubItemGain += subItemIncr;
                    if (tn.Expanded
                        || tn.SubItemRoot)
                    {
                        if (tn.SubItemRoot)
                        {
                            // A subitem root node always has a parent node in the owning branch
                            var subItemRoot = tn.Parent;
                            var totalIncr = itemIncr + subItemIncr;
                            Debug.Assert(subItemRoot != null);
                            itemIncr = 0;
                            // Recalculate, see if this has affected the parent total
                            subItemIncr = subItemRoot.AdjustSubItemGain();
                            if (affectedColumnsCount != 0)
                            {
                                --affectedColumnsCount;
                                // Figure out the trailing items, which is the number of rows wholly
                                // contained in the column that did not change.

                                // First, get the number of rows past the current change
                                var offset = affectedColumns.GetOffset(affectedColumnsCount);
                                var trailingItems = tn.TotalCount - offset - 1;
                                var itemsBelowAnchor = subItemRoot.ImmedSubItemGain - offset - subItemIncr;
                                if (!tn.ComplexSubItem)
                                {
                                    ++trailingItems;
                                }
                                if (totalIncr > 0)
                                {
                                    trailingItems -= totalIncr;
                                }

                                // Now, adjust by the number of rows that have moved into/out of being
                                // wholly contained by this column
                                if (subItemIncr > 0)
                                {
                                    trailingItems -= subItemIncr;
                                }
                                else
                                {
                                    trailingItems += subItemIncr;
                                }
                                if (trailingItems < 0)
                                {
                                    trailingItems = 0;
                                }

                                // UNDONE_MC: Need a global column for the first two parameters here
                                subItemChanges[affectedColumnsCount] = new SubItemColumnAdjustment(
                                    subItemRoot.FirstSubItem.ColumnOfRootNode(tn),
                                    subItemRoot.Parent.GetColumnCount(subItemRoot.Index) - 1,
                                    totalIncr,
                                    trailingItems,
                                    itemsBelowAnchor);
                            }
                            if (subItemIncr == 0)
                            {
                                break;
                            }
                            subItemRoot.FullSubItemGain += subItemIncr;
                            tn = subItemRoot;
                        }
                        tn = tn.Parent;
                        if (tn == null)
                        {
                            rowIncr = itemIncr + subItemIncr;
                            break;
                        }
                    }
                    else if (subItemIncr != 0)
                    {
                        tn = tn.Parent;
                        if (tn != null
                            &&
                            (tn.Expanded || tn.SubItemRoot))
                        {
                            itemIncr = 0;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
            }
            else
            {
                var tnNext = tn;
                do
                {
                    tn = tnNext;
                    if (tn == tnCeiling)
                    {
                        break;
                    }
                    tn.FullCount += itemIncr;
                }
                while (tn.Expanded
                       && ((tnNext = tn.Parent) != null));
                subItemChanges = null;
                rowIncr = (tn != null && tn.Expanded) ? itemIncr : 0;
            }
        }
コード例 #22
0
        private void FreeRecursive(ref TREENODE pStart)
        {
            if (pStart != null)
            {
                var tn1 = pStart;
                TREENODE tn2;
                SUBITEMNODE sn;

                //Stop the recursion from walking above the node
                tn1.Parent = null;
                while (tn1 != null)
                {
                    tn2 = tn1.FirstChild;
                    if (tn2 != null)
                    {
                        tn1.FirstChild = tn2.NextSibling;
                        tn1 = tn2;
                    }
                    else if (tn1.FirstSubItem != null)
                    {
                        sn = tn1.FirstSubItem;
                        tn1.FirstSubItem = sn.NextSibling;
                        tn1 = sn.RootNode;
                    }
                    else
                    {
                        tn2 = tn1;
                        tn1 = tn2.Parent;
                        DestroyTreeNode(ref tn2);
                    }
                }
                pStart = null;
            }
        }
コード例 #23
0
 private void ChangeFullCountRecursive(TREENODE tn, int itemIncr, int subItemIncr, TREENODE tnCeiling, out int subItemRemainder)
 {
     Debug.Assert(tn != null, "");
     if (MultiColumnSupport)
     {
         while (tn != tnCeiling)
         {
             tn.FullCount += itemIncr;
             tn.FullSubItemGain += subItemIncr;
             if (tn.Expanded
                 || tn.SubItemRoot)
             {
                 if (tn.SubItemRoot)
                 {
                     // A subitem root node always has a parent node in the owning branch
                     Debug.Assert(tn.Parent != null);
                     tn = tn.Parent;
                     itemIncr = 0;
                     // Recalculate, see if this has affected the parent total
                     subItemIncr = tn.AdjustSubItemGain();
                     if (subItemIncr == 0)
                     {
                         break;
                     }
                     tn.FullSubItemGain += subItemIncr;
                 }
                 tn = tn.Parent;
                 if (tn == null)
                 {
                     break;
                 }
             }
             else if (subItemIncr != 0)
             {
                 tn = tn.Parent;
                 if (tn != null
                     &&
                     (tn.Expanded || tn.SubItemRoot))
                 {
                     itemIncr = 0;
                 }
                 else
                 {
                     break;
                 }
             }
             else
             {
                 break;
             }
         }
     }
     else
     {
         do
         {
             if (tn == tnCeiling)
             {
                 break;
             }
             tn.FullCount += itemIncr;
         }
         while (tn.Expanded
                && ((tn = tn.Parent) != null));
     }
     subItemRemainder = subItemIncr;
 }
コード例 #24
0
        private void FreeRecursive(ref TREENODE pStart, ref NODEPOSITIONTRACKER_Dynamic ntDetached)
        {
            if (pStart != null)
            {
                if (myPositionManager == null
                    || pStart.NoTracking
                    || pStart.DefaultTracking)
                {
                    FreeRecursive(ref pStart);
                    return;
                }
                var tn1 = pStart;
                TREENODE tn2;
                SUBITEMNODE sn;

                //Stop the recursion from walking above the node
                tn1.Parent = null;
                while (tn1 != null)
                {
                    tn2 = tn1.FirstChild;
                    if (tn2 != null)
                    {
                        tn1.FirstChild = tn2.NextSibling;
                        tn1 = tn2;
                    }
                    else if (tn1.FirstSubItem != null)
                    {
                        sn = tn1.FirstSubItem;
                        tn1.FirstSubItem = sn.NextSibling;
                        tn1 = sn.RootNode;
                    }
                    else
                    {
                        tn2 = tn1;
                        tn1 = tn2.Parent;
                        if (tn2.FirstPositionTracker != null)
                        {
                            NODEPOSITIONTRACKER_Dynamic.DetachTrackers(ref tn2.FirstPositionTracker, ref ntDetached);
                            Debug.Assert(tn2.FirstPositionTracker == null);
                        }
                        DestroyTreeNode(ref tn2);
                    }
                }
                pStart = null;
            }
        }
コード例 #25
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);
                }
            }
        }
コード例 #26
0
 private void DestroyTreeNode(ref TREENODE tn)
 {
     if (tn.Branch != null)
     {
         tn.Branch.OnBranchModification -= OnBranchModification;
         if (tn.Dynamic)
         {
             RemoveTrackedNode(tn.Branch, tn);
         }
     }
     if (tn.FirstPositionTracker != null)
     {
         tn.FirstPositionTracker.OnParentNodeDeleted();
     }
     tn = null;
 }
コード例 #27
0
 // Helper function to see if a node already exists in a sibling list
 private static TREENODE FindIndexedNode(int findIndex, TREENODE tnFirstChild, ref TREENODE tnHintChild)
 {
     TREENODE retVal = null;
     if (tnFirstChild != null)
     {
         if (tnHintChild == null
             || tnHintChild.Index > findIndex)
         {
             tnHintChild = tnFirstChild;
         }
         var tnChild = tnHintChild;
         while (tnChild != null)
         {
             if (tnChild.Index < findIndex)
             {
                 tnChild = tnHintChild = tnChild.NextSibling;
             }
             else
             {
                 if (tnChild.Index == findIndex)
                 {
                     retVal = tnHintChild = tnChild;
                 }
                 break;
             }
         }
     }
     return retVal;
 }
コード例 #28
0
 private void RemoveTrackedNode(IBranch branch, TREENODE tn)
 {
     Debug.Assert(myNodeTracker != null);
     myNodeTracker.Remove(branch, tn);
 }
コード例 #29
0
 private void UpdateTreeNodeRecursive(TREENODE tn)
 {
     int UpdateCounter;
     while (tn != null)
     {
         if (tn.CallUpdate)
         {
             UpdateCounter = tn.Branch.UpdateCounter;
             if (tn.UpdateCounter != UpdateCounter)
             {
                 tn.UpdateCounter = UpdateCounter;
                 RealignTreeNode(tn);
             }
         }
         if (tn.FirstChild != null)
         {
             UpdateTreeNodeRecursive(tn.FirstChild);
         }
         tn = tn.NextSibling;
     }
 }
コード例 #30
0
 public void Add(IBranch branch, TREENODE value)
 {
     // Add this item the an existing branch if any
     var existing = this[branch];
     if (existing == null)
     {
         Dictionary.Add(branch, value);
     }
     else
     {
         // Add to end of the list. The end is important because
         // value might already have nodes attached to it. We
         // need to walk one list or the other.
         while (existing.NextNode != null)
         {
             existing = existing.NextNode;
         }
         existing.NextNode = value;
         value.PrevNode = existing;
     }
 }