Beispiel #1
0
        // InsertElement worker.  Adds a TextElement to the tree.
        // If element is already in a tree, we remove it, do a deep copy of its content, 
        // and insert that too.
        internal void InsertElementInternal(TextPointer startPosition, TextPointer endPosition, TextElement element) 
        { 
            TextTreeTextElementNode elementNode;
            int symbolOffset; 
            int childSymbolCount;
            TextPointer startEdgePosition;
            TextPointer endEdgePosition;
            char[] elementText; 
            ExtractChangeEventArgs extractChangeEventArgs;
            DependencyObject parentLogicalNode; 
            bool newElementNode; 
            int deltaCharCount;
 
            Invariant.Assert(!this.PlainTextOnly);
            Invariant.Assert(startPosition.TextContainer == this);
            Invariant.Assert(endPosition.TextContainer == this);
 
            DemandCreateText();
 
            startPosition.SyncToTreeGeneration(); 
            endPosition.SyncToTreeGeneration();
 
            bool scopesExistingContent = startPosition.CompareTo(endPosition) != 0;

            BeforeAddChange();
 
            // Remove element from any previous tree.
            // When called from a public method we already checked all the 
            // illegal cases in CanInsertElementInternal. 
            if (element.TextElementNode != null)
            { 
                // This element is already in a tree.  Remove it!

                bool sameTextContainer = (this == element.TextContainer);
 
                if (!sameTextContainer)
                { 
                    // This is a cross-tree extract. 
                    // We need to start a change block now, so that we can
                    // raise a Changing event inside ExtractElementInternal 
                    // before raising the LogicalTree events below.
                    // We'll make an EndChange call to wrap up below.
                    element.TextContainer.BeginChange();
                } 

                bool exceptionThrown = true; 
                try 
                {
                    // ExtractElementInternal will raise LogicalTree events which 
                    // could raise exceptions from external code.
                    elementText = element.TextContainer.ExtractElementInternal(element, true /* deep */, out extractChangeEventArgs);
                    exceptionThrown = false;
                } 
                finally
                { 
                    if (exceptionThrown && !sameTextContainer) 
                    {
                        // If an exception is thrown, make sure we close the 
                        // change block we opened above before unwinding.
                        element.TextContainer.EndChange();
                    }
                } 

                elementNode = element.TextElementNode; 
                deltaCharCount = extractChangeEventArgs.ChildIMECharCount; 

                if (sameTextContainer) 
                {
                    // Re-[....] the TextPointers in case we just extracted from this tree.
                    startPosition.SyncToTreeGeneration();
                    endPosition.SyncToTreeGeneration(); 

                    // We must add the extract change now, before we move on to the insert. 
                    // (When !sameTextContainer we want to delay the notification in the extract 
                    // tree until the insert tree is in an accessible state, ie at the end of this method.)
                    extractChangeEventArgs.AddChange(); 

                    // Don't re-raise the change below.
                    extractChangeEventArgs = null;
                } 

                newElementNode = false; 
            } 
            else
            { 
                // Allocate a node in the tree to hold the element.
                elementText = null;
                elementNode = new TextTreeTextElementNode();
                deltaCharCount = 0; 
                newElementNode = true;
                extractChangeEventArgs = null; 
            } 

            parentLogicalNode = startPosition.GetLogicalTreeNode(); 

            // Invalidate any TextElementCollection that depends on the parent.
            // Make sure we do that before raising any public events.
            TextElementCollectionHelper.MarkDirty(parentLogicalNode); 

            // Link the TextElement to the TextElementNode. 
            if (newElementNode) 
            {
                elementNode.TextElement = element; 
                element.TextElementNode = (TextTreeTextElementNode)elementNode;
            }

            // If the new element will become the parent of an old element, 
            // the old element may become a firstIMEVisibleSibling.
            TextTreeTextElementNode newFirstIMEVisibleNode = null; 
            int newFirstIMEVisibleNodeCharDelta = 0; 
            if (scopesExistingContent)
            { 
                newFirstIMEVisibleNode = startPosition.GetAdjacentTextElementNodeSibling(LogicalDirection.Forward);
                if (newFirstIMEVisibleNode != null)
                {
                    newFirstIMEVisibleNodeCharDelta = -newFirstIMEVisibleNode.IMELeftEdgeCharCount; 
                    newFirstIMEVisibleNode.IMECharCount += newFirstIMEVisibleNodeCharDelta;
                } 
            } 

            // Attach the element node. 
            childSymbolCount = InsertElementToSiblingTree(startPosition, endPosition, elementNode);

            // Add the edge char count to our delta.  We couldn't get this before
            // because it depends on the position of the element in the tree. 
            deltaCharCount += elementNode.IMELeftEdgeCharCount;
 
            TextTreeTextElementNode formerFirstIMEVisibleNode = null; 
            int formerFirstIMEVisibleNodeCharDelta = 0;
            if (element.IsFirstIMEVisibleSibling && !scopesExistingContent) 
            {
                formerFirstIMEVisibleNode = (TextTreeTextElementNode)elementNode.GetNextNode();
                if (formerFirstIMEVisibleNode != null)
                { 
                    // The following node was the former first ime visible sibling.
                    // It just moved, and gains an edge character. 
                    formerFirstIMEVisibleNodeCharDelta = formerFirstIMEVisibleNode.IMELeftEdgeCharCount; 
                    formerFirstIMEVisibleNode.IMECharCount += formerFirstIMEVisibleNodeCharDelta;
                } 
            }

            // Ancester nodes gain the two edge symbols.
            UpdateContainerSymbolCount(elementNode.GetContainingNode(), /* symbolCount */ elementText == null ? 2 : elementText.Length, deltaCharCount + formerFirstIMEVisibleNodeCharDelta + newFirstIMEVisibleNodeCharDelta); 

            symbolOffset = elementNode.GetSymbolOffset(this.Generation); 
 
            if (newElementNode)
            { 
                // Insert text to account for the element edges.
                TextTreeText.InsertElementEdges(_rootNode.RootTextBlock, symbolOffset, childSymbolCount);
            }
            else 
            {
                // element already has an existing child, just copy over the corresponding text. 
                TextTreeText.InsertText(_rootNode.RootTextBlock, symbolOffset, elementText); 
            }
 
            NextGeneration(false /* deletedContent */);

            // Handle undo.
            TextTreeUndo.CreateInsertElementUndoUnit(this, symbolOffset, elementText != null /* deep */); 

            // If we extracted the TextElement from another tree, raise that event now. 
            // We can't raise this event any earlier, because prior to now _this_ tree 
            // is in an invalid state and this tree could be referenced by a listener
            // to changes on the other tree. 
            if (extractChangeEventArgs != null)
            {
                // Announce the extract from the old tree.
                // NB: we already Removed the element from the original logical tree with LogicalTreeHelper, 
                // and did a BeginChange above.
                extractChangeEventArgs.AddChange(); 
                extractChangeEventArgs.TextContainer.EndChange(); 
            }
 
            // Raise the public event for the insert into this tree.
            // During document load we won't have listeners and we can save
            // an allocation on every insert.  This can easily save 1000's of allocations during boot.
            if (this.HasListeners) 
            {
                // 
                startEdgePosition = new TextPointer(this, elementNode, ElementEdge.BeforeStart); 

                if (childSymbolCount == 0 || elementText != null) 
                {
                    AddChange(startEdgePosition, elementText == null ? 2 : elementText.Length, deltaCharCount, PrecursorTextChangeType.ContentAdded);
                }
                else 
                {
                    endEdgePosition = new TextPointer(this, elementNode, ElementEdge.BeforeEnd); 
 
                    AddChange(startEdgePosition, endEdgePosition, elementNode.SymbolCount,
                              elementNode.IMELeftEdgeCharCount, elementNode.IMECharCount - elementNode.IMELeftEdgeCharCount, 
                              PrecursorTextChangeType.ElementAdded, null, false);
                }

                if (formerFirstIMEVisibleNodeCharDelta != 0) 
                {
                    RaiseEventForFormerFirstIMEVisibleNode(formerFirstIMEVisibleNode); 
                } 

                if (newFirstIMEVisibleNodeCharDelta != 0) 
                {
                    RaiseEventForNewFirstIMEVisibleNode(newFirstIMEVisibleNode);
                }
            } 

            // Insert the element into a Framework logical tree 
            element.BeforeLogicalTreeChange(); 
            try
            { 
                LogicalTreeHelper.AddLogicalChild(parentLogicalNode, element);
            }
            finally
            { 
                element.AfterLogicalTreeChange();
            } 
 
            // Reparent all children.
            // We only need to do this if we created a new element node. 
            if (newElementNode)
            {
                ReparentLogicalChildren(elementNode, elementNode.TextElement, parentLogicalNode /* oldParent */);
            } 

            // Notify the TextElement of a content change if it was moved to parent new content. This 
            // can happen when Runs get merged. 
            if (scopesExistingContent)
            { 
                element.OnTextUpdated();
            }
        }
Beispiel #2
0
        // ExtractElement worker.  Removes a TextElement from the tree.
        //
        // If deep is true, also removes any content covered by the element. 
        // In this case element.TextTreeElementNode will be replaced with a
        // deep copy of all contained nodes.  Since this is a copy, it can 
        // be safely inserted into a new tree -- no positions reference it. 
        //
        // deep is true when this method is called during a cross-tree insert 
        // (that is, when a TextElement is extracted from one tree and inserted
        // into another via a call to InsertElement).
        //
        // If deep is true, returns the raw text corresponding to element and 
        // its contained content.  Otherwise returns null.
        // 
        // If deep is true, extractChangeEventArgs will be non-null on exit, 
        // containing all the information needed to raise a matching TextChanged
        // event.  Otherwise, extractChangeEventArgs will be null on exit. 
        private char[] ExtractElementInternal(TextElement element, bool deep, out ExtractChangeEventArgs extractChangeEventArgs)
        {
            TextTreeTextElementNode elementNode;
            SplayTreeNode containingNode; 
            TextPointer startPosition;
            TextPointer endPosition; 
            bool empty; 
            int symbolOffset;
            char[] elementText; 
            TextTreeUndoUnit undoUnit;
            SplayTreeNode firstContainedChildNode;
            SplayTreeNode lastContainedChildNode;
            DependencyObject oldLogicalParent; 

            BeforeAddChange(); 
 
            firstContainedChildNode = null;
            lastContainedChildNode = null; 
            extractChangeEventArgs = null;

            elementText = null;
            elementNode = element.TextElementNode; 
            containingNode = elementNode.GetContainingNode();
            empty = (elementNode.ContainedNode == null); 
 
            startPosition = new TextPointer(this, elementNode, ElementEdge.BeforeStart, LogicalDirection.Backward);
            // We only need the end position if this element originally spanned any content. 
            endPosition = null;
            if (!empty)
            {
                endPosition = new TextPointer(this, elementNode, ElementEdge.AfterEnd, LogicalDirection.Backward); 
            }
 
            symbolOffset = elementNode.GetSymbolOffset(this.Generation); 

            // Remember the old parent 
            oldLogicalParent = ((TextTreeNode)containingNode).GetLogicalTreeNode();

            // Invalidate any TextElementCollection that depends on the parent.
            // Make sure we do that before raising any public events. 
            TextElementCollectionHelper.MarkDirty(oldLogicalParent);
 
 
            // Remove the element from the logical tree.
            // NB: we do this even for a deep extract, because we can't wait -- 
            // during a deep extract/move to new tree, the property system must be
            // notified before the element moves into its new tree.
            element.BeforeLogicalTreeChange();
            try 
            {
                LogicalTreeHelper.RemoveLogicalChild(oldLogicalParent, element); 
            } 
            finally
            { 
                element.AfterLogicalTreeChange();
            }

            // Handle undo. 
            if (deep && !empty)
            { 
                undoUnit = TextTreeUndo.CreateDeleteContentUndoUnit(this, startPosition, endPosition); 
            }
            else 
            {
                undoUnit = TextTreeUndo.CreateExtractElementUndoUnit(this, elementNode);
            }
 
            // Save the first/last contained node now -- after the ExtractElementFromSiblingTree
            // call it will be too late to find them. 
            if (!deep && !empty) 
            {
                firstContainedChildNode = elementNode.GetFirstContainedNode(); 
                lastContainedChildNode = elementNode.GetLastContainedNode();
            }

            // Record all the IME related char state before the extract. 
            int imeCharCount = elementNode.IMECharCount;
            int imeLeftEdgeCharCount = elementNode.IMELeftEdgeCharCount; 
            TextTreeTextElementNode nextNode = (empty && element.IsFirstIMEVisibleSibling) ? (TextTreeTextElementNode)elementNode.GetNextNode() : null; 

            int nextNodeCharDelta = 0; 
            if (nextNode != null)
            {
                // The following node is the new first ime visible sibling.
                // It just moved, and loses an edge character. 
                nextNodeCharDelta = -nextNode.IMELeftEdgeCharCount;
                nextNode.IMECharCount += nextNodeCharDelta; 
            } 

            // Rip the element out of its sibling tree. 
            // If this is a deep extract element's TextElementNode will be updated
            // with a deep copy of all contained nodes.
            ExtractElementFromSiblingTree(containingNode, elementNode, deep);
 
            // The first contained node of the extracted node may no longer
            // be a first sibling after the parent extract.  If that's the case, 
            // update its char count. 
            int containedNodeCharDelta = 0;
            TextTreeTextElementNode firstContainedElementNode = firstContainedChildNode as TextTreeTextElementNode; 
            if (firstContainedElementNode != null)
            {
                containedNodeCharDelta = firstContainedElementNode.IMELeftEdgeCharCount;
                firstContainedElementNode.IMECharCount += containedNodeCharDelta; 
            }
 
            if (!deep) 
            {
                // Unlink the TextElement from the TextElementNode. 
                element.TextElementNode = null;

                // Pull out the edge symbols from the text store.
                TextTreeText.RemoveElementEdges(_rootNode.RootTextBlock, symbolOffset, elementNode.SymbolCount); 
            }
            else 
            { 
                // We leave element.TextElement alone, since for a deep extract we've already
                // stored a copy of the original nodes there that we'll use in a following insert. 

                // Cut and return the matching symbols.
                elementText = TextTreeText.CutText(_rootNode.RootTextBlock, symbolOffset, elementNode.SymbolCount);
            } 

            // Ancestor nodes lose either the whole node or two element edge symbols, depending 
            // on whether or not this is a deep extract. 
            if (deep)
            { 
                UpdateContainerSymbolCount(containingNode, -elementNode.SymbolCount, -imeCharCount + nextNodeCharDelta + containedNodeCharDelta);
            }
            else
            { 
                UpdateContainerSymbolCount(containingNode, /* symbolCount */ -2, /* charCount */ -imeLeftEdgeCharCount + nextNodeCharDelta + containedNodeCharDelta);
            } 
 
            NextGeneration(true /* deletedContent */);
 
            if (undoUnit != null)
            {
                undoUnit.SetTreeHashCode();
            } 

            // Raise the public event. 
            if (deep) 
            {
                extractChangeEventArgs = new ExtractChangeEventArgs(this, startPosition, elementNode, nextNodeCharDelta == 0 ? null : nextNode, containedNodeCharDelta == 0 ? null : firstContainedElementNode, imeCharCount, imeCharCount - imeLeftEdgeCharCount); 
            }
            else if (empty)
            {
                AddChange(startPosition, /* symbolCount */ 2, /* charCount */ imeCharCount, PrecursorTextChangeType.ContentRemoved); 
            }
            else 
            { 
                AddChange(startPosition, endPosition, elementNode.SymbolCount,
                          imeLeftEdgeCharCount, 
                          imeCharCount - imeLeftEdgeCharCount,
                          PrecursorTextChangeType.ElementExtracted, null, false);
            }
 
            // Raise events for nodes that just gained or lost an IME char due
            // to changes in their surroundings. 
            if (extractChangeEventArgs == null) 
            {
                if (nextNodeCharDelta != 0) 
                {
                    RaiseEventForNewFirstIMEVisibleNode(nextNode);
                }
 
                if (containedNodeCharDelta != 0)
                { 
                    RaiseEventForFormerFirstIMEVisibleNode(firstContainedElementNode); 
                }
            } 

            if (!deep && !empty)
            {
                ReparentLogicalChildren(firstContainedChildNode, lastContainedChildNode, oldLogicalParent /* new parent */, element /* old parent */); 
            }
 
            // 
            // Remove char count for logical break, since the element is leaving the tree.
            // For more data refer to comments of dev10 bug 703174. 
            //
            if (null != element.TextElementNode)
            {
                element.TextElementNode.IMECharCount -= imeLeftEdgeCharCount; 
            }
 
            return elementText; 
        }