예제 #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();
            }
        }