Пример #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();
            }
        }
Пример #2
0
        // Validation for SetValue.
        private void ValidateSetValue(TextPointer position)
        {
            TextElement element; 

            if (position.TextContainer != this) 
            { 
                throw new InvalidOperationException(SR.Get(SRID.NotInThisTree, "position"));
            } 

            position.SyncToTreeGeneration();

            element = position.Parent as TextElement; 
            if (element == null)
            { 
                throw new InvalidOperationException(SR.Get(SRID.NoElement)); 
            }
        } 
Пример #3
0
        // The InsertText worker.  Adds text to the tree at a specified position. 
        // text is either a string or char[] to insert. 
        internal void InsertTextInternal(TextPointer position, object text)
        { 
            TextTreeTextNode textNode;
            SplayTreeNode containingNode;
            TextPointer originalPosition;
            int symbolOffset; 
            int textLength;
            LogicalDirection direction; 
 
            Invariant.Assert(text is string || text is char[], "Unexpected type for 'text' parameter!");
 
            textLength = GetTextLength(text);

            if (textLength == 0)
                return; 

            DemandCreateText(); 
 
            position.SyncToTreeGeneration();
 
            if (Invariant.Strict)
            {
                if (position.Node.SymbolCount == 0)
                { 
                    // We expect only TextTreeTextNodes ever have zero symbol counts.
                    // This can happen in two cases: 
                    // 
                    // <TextNode referencedEdge=BeforeStart symbolCount=1+/> <TextNode referencedEdge=AfterEnd symbolCount=0/>
                    // or 
                    // <TextNode referencedEdge=BeforeStart symbolCount=0/> <TextNode referencedEdge=AfterEnd symbolCount=1+/>
                    //
                    Invariant.Assert(position.Node is TextTreeTextNode);
                    Invariant.Assert((position.Edge == ElementEdge.AfterEnd && position.Node.GetPreviousNode() is TextTreeTextNode && position.Node.GetPreviousNode().SymbolCount > 0) || 
                                 (position.Edge == ElementEdge.BeforeStart && position.Node.GetNextNode() is TextTreeTextNode && position.Node.GetNextNode().SymbolCount > 0));
                } 
            } 

            BeforeAddChange(); 

            // 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.
            originalPosition = this.HasListeners ? new TextPointer(position, LogicalDirection.Backward) : null; 

            // Find a bordering TextTreeTextNode, if any. 
 
            // We know position already points to the current TextNode, if there is one, so
            // we can't append text to that node (it would disrespect position's gravity to do so). 
            // So we either have to find a neighboring text node with no position references, or
            // create a new node.

            // Look for a bordering text node. 
            if (position.Edge == ElementEdge.BeforeStart || position.Edge == ElementEdge.BeforeEnd)
            { 
                direction = LogicalDirection.Backward; 
            }
            else 
            {
                direction = LogicalDirection.Forward;
            }
            textNode = position.GetAdjacentTextNodeSibling(direction); 

            if (textNode != null) 
            { 
                // We can't use a text node that is already referred to by text positions.
                // Doing so could displace the positions, since they expect to remain at 
                // the node edge no matter what happens.
                if ((direction == LogicalDirection.Backward && textNode.AfterEndReferenceCount) ||
                    (direction == LogicalDirection.Forward && textNode.BeforeStartReferenceCount))
                { 
                    textNode = null;
                } 
            } 

            if (textNode == null) 
            {
                // No text node available.  Create and insert one.
                textNode = new TextTreeTextNode();
                textNode.InsertAtPosition(position); 
                containingNode = textNode.GetContainingNode();
            } 
            else 
            {
                // We didn't insert a new node, so splay textNode to the root so 
                // we don't invalidate any LeftSymbolCounts of ancestor nodes.
                textNode.Splay();
                containingNode = textNode.ParentNode;
            } 

            // Update the symbol counts. 
            textNode.SymbolCount += textLength; // This simultaneously updates textNode.IMECharCount. 
            UpdateContainerSymbolCount(containingNode, /* symbolCount */ textLength, /* charCount */ textLength);
 
            // Insert the raw text.
            symbolOffset = textNode.GetSymbolOffset(this.Generation);
            TextTreeText.InsertText(_rootNode.RootTextBlock, symbolOffset, text);
 
            // Handle undo.
            TextTreeUndo.CreateInsertUndoUnit(this, symbolOffset, textLength); 
 
            // Announce the change.
            NextGeneration(false /* deletedContent */); 

            AddChange(originalPosition, /* symbolCount */ textLength, /* charCount */ textLength, PrecursorTextChangeType.ContentAdded);

            // Notify the TextElement of a content change. 
            TextElement textElement = position.Parent as TextElement;
            if (textElement != null) 
            { 
                textElement.OnTextUpdated();
            } 
        }
Пример #4
0
        // DeleteContent worker.  Removes content from the tree.
        internal void DeleteContentInternal(TextPointer startPosition, TextPointer endPosition) 
        {
            TextTreeNode containingNode;
            int symbolCount;
            int charCount; 
            TextTreeUndoUnit undoUnit;
            TextPointer deletePosition; 
 
            startPosition.SyncToTreeGeneration();
            endPosition.SyncToTreeGeneration(); 

            if (startPosition.CompareTo(endPosition) == 0)
                return;
 
            BeforeAddChange();
 
            undoUnit = TextTreeUndo.CreateDeleteContentUndoUnit(this, startPosition, endPosition); 

            containingNode = startPosition.GetScopingNode(); 

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

            int nextIMEVisibleNodeCharDelta = 0; 
            TextTreeTextElementNode nextIMEVisibleNode = GetNextIMEVisibleNode(startPosition, endPosition); 
            if (nextIMEVisibleNode != null)
            { 
                // The node following the delete just became the first sibling.
                // This might affect its ime char count.
                nextIMEVisibleNodeCharDelta = -nextIMEVisibleNode.IMELeftEdgeCharCount;
                nextIMEVisibleNode.IMECharCount += nextIMEVisibleNodeCharDelta; 
            }
 
            // First cut: remove all top-level TextElements and their chilren. 
            // We need to put each TextElement in its own tree, so that any outside
            // references can still play with the TextElements safely. 
            symbolCount = CutTopLevelLogicalNodes(containingNode, startPosition, endPosition, out charCount);

            // Cut what's left.
            int remainingCharCount; 
            symbolCount += DeleteContentFromSiblingTree(containingNode, startPosition, endPosition, nextIMEVisibleNodeCharDelta != 0, out remainingCharCount);
            charCount += remainingCharCount; 
 
            Invariant.Assert(symbolCount > 0);
 
            if (undoUnit != null)
            {
                undoUnit.SetTreeHashCode();
            } 

            // Public tree event. 
            deletePosition = new TextPointer(startPosition, LogicalDirection.Forward); 
            AddChange(deletePosition, symbolCount, charCount, PrecursorTextChangeType.ContentRemoved);
 
            if (nextIMEVisibleNodeCharDelta != 0)
            {
                RaiseEventForNewFirstIMEVisibleNode(nextIMEVisibleNode);
            } 
        }
Пример #5
0
        // Does a deep extract of all top-level TextElements between two positions. 
        // Returns the combined symbol count of all extracted elements.
        // Each extracted element (and its children) are moved into a private tree. 
        // This insures that outside references to the TextElement can still use
        // the TextElements freely, inserting or removing content, etc.
        //
        // Also calls AddLogicalChild on any top-level UIElements encountered. 
        private int CutTopLevelLogicalNodes(TextTreeNode containingNode, TextPointer startPosition, TextPointer endPosition, out int charCount)
        { 
            SplayTreeNode node; 
            SplayTreeNode nextNode;
            SplayTreeNode stopNode; 
            TextTreeTextElementNode elementNode;
            TextTreeObjectNode uiElementNode;
            char[] elementText;
            int symbolCount; 
            TextContainer tree;
            TextPointer newTreeStart; 
            DependencyObject logicalParent; 
            object currentLogicalChild;
 
            Invariant.Assert(startPosition.GetScopingNode() == endPosition.GetScopingNode(), "startPosition/endPosition not in same sibling tree!");

            node = startPosition.GetAdjacentSiblingNode(LogicalDirection.Forward);
            stopNode = endPosition.GetAdjacentSiblingNode(LogicalDirection.Forward); 

            symbolCount = 0; 
            charCount = 0; 

            logicalParent = containingNode.GetLogicalTreeNode(); 

            while (node != stopNode)
            {
                currentLogicalChild = null; 

                // Get the next node now, before we extract any TextElementNodes. 
                nextNode = node.GetNextNode(); 

                elementNode = node as TextTreeTextElementNode; 
                if (elementNode != null)
                {
                    // Grab the IMECharCount before we modify the node.
                    // This value depends on the node's current context. 
                    int imeCharCountInOriginalContainer = elementNode.IMECharCount;
 
                    // Cut and record the matching symbols. 
                    elementText = TextTreeText.CutText(_rootNode.RootTextBlock, elementNode.GetSymbolOffset(this.Generation), elementNode.SymbolCount);
 
                    // Rip the element out of its sibling tree.
                    // textElementNode.TextElement's TextElementNode will be updated
                    // with a deep copy of all contained nodes. We need a deep copy
                    // to ensure the new element/tree has no TextPointer references. 
                    ExtractElementFromSiblingTree(containingNode, elementNode, true /* deep */);
                    // Assert that the TextElement now points to a new TextElementNode, not the original one. 
                    Invariant.Assert(elementNode.TextElement.TextElementNode != elementNode); 
                    // We want to start referring to the copied node, update elementNode.
                    elementNode = elementNode.TextElement.TextElementNode; 

                    UpdateContainerSymbolCount(containingNode, -elementNode.SymbolCount, -imeCharCountInOriginalContainer);
                    NextGeneration(true /* deletedContent */);
 
                    // Stick it in a private tree so it's safe for the outside world to play with.
                    tree = new TextContainer(null, false /* plainTextOnly */); 
                    newTreeStart = tree.Start; 

                    tree.InsertElementToSiblingTree(newTreeStart, newTreeStart, elementNode); 
                    Invariant.Assert(elementText.Length == elementNode.SymbolCount);
                    tree.UpdateContainerSymbolCount(elementNode.GetContainingNode(), elementNode.SymbolCount, elementNode.IMECharCount);
                    tree.DemandCreateText();
                    TextTreeText.InsertText(tree.RootTextBlock, 1 /* symbolOffset */, elementText); 
                    tree.NextGeneration(false /* deletedContent */);
 
                    currentLogicalChild = elementNode.TextElement; 

                    // Keep a running total of how many symbols we've removed. 
                    symbolCount += elementNode.SymbolCount;
                    charCount += imeCharCountInOriginalContainer;
                }
                else 
                {
                    uiElementNode = node as TextTreeObjectNode; 
                    if (uiElementNode != null) 
                    {
                        currentLogicalChild = uiElementNode.EmbeddedElement; 
                    }
                }

                // Remove the child from the logical tree 
                LogicalTreeHelper.RemoveLogicalChild(logicalParent, currentLogicalChild);
 
                node = nextNode; 
            }
 
            if (symbolCount > 0)
            {
                startPosition.SyncToTreeGeneration();
                endPosition.SyncToTreeGeneration(); 
            }
 
            return symbolCount; 
        }
Пример #6
0
        // InsertEmbeddedObject worker.  Adds a UIElement to the tree. 
        internal void InsertEmbeddedObjectInternal(TextPointer position, DependencyObject embeddedObject) 
        {
            TextTreeNode objectNode; 
            int symbolOffset;
            DependencyObject parentLogicalNode;
            TextPointer insertPosition;
 
            Invariant.Assert(!this.PlainTextOnly);
 
            DemandCreateText(); 

            position.SyncToTreeGeneration(); 

            BeforeAddChange();

            parentLogicalNode = position.GetLogicalTreeNode(); 

            // Insert a node. 
            objectNode = new TextTreeObjectNode(embeddedObject); 
            objectNode.InsertAtPosition(position);
 
            // Update the symbol count.
            UpdateContainerSymbolCount(objectNode.GetContainingNode(), objectNode.SymbolCount, objectNode.IMECharCount);

            // Insert the corresponding text. 
            symbolOffset = objectNode.GetSymbolOffset(this.Generation);
            TextTreeText.InsertObject(_rootNode.RootTextBlock, symbolOffset); 
 
            NextGeneration(false /* deletedContent */);
 
            // Handle undo.
            TextTreeUndo.CreateInsertUndoUnit(this, symbolOffset, 1);

            // Tell parent to update Logical Tree 
            LogicalTreeHelper.AddLogicalChild(parentLogicalNode, embeddedObject);
 
            // Raise the public event. 
            // 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)
            {
                insertPosition = new TextPointer(this, objectNode, ElementEdge.BeforeStart);
                AddChange(insertPosition, 1, 1, PrecursorTextChangeType.ContentAdded); 
            }
        } 
Пример #7
0
        /// <summary>
        /// Returns the distance between this TextPointer and another.
        /// </summary>
        /// <param name="position">
        /// TextPointer to compare.
        /// </param>
        /// <exception cref="System.ArgumentException">
        /// Throws an ArgumentException if the TextPointer position is not
        /// positioned within the same document as this TextPointer.
        /// </exception>
        /// <returns>
        /// <para>The return value will be negative if the TextPointer position
        /// preceeds this TextPointer, zero if the two TextPointers
        /// are equally positioned, or positive if position follows this
        /// TextPointer.</para>
        /// </returns>
        /// <remarks>
        /// <para>The distance is represented as a number of "symbols"
        /// between these two pointers.</para>
        /// <para>Each opening and each closing tag of any TextElement
        /// is considered as one symbol. So an empty TextElement contributes
        /// two symbols - one for each of tags.</para>
        /// <para>UIElement placed within InlineUIContainer or BlockUIContainer
        /// represented as one symbol - independently of how complex
        /// is its content. Even if the UIElement contains or is a
        /// text container it is treated as atomic entity - single symbol.
        /// This may be confusing especially if you do not pay
        /// muchy attention to a difference between the <see cref="TextElement"/>
        /// the <see cref="UIElement"/> class.</para>
        /// <para>Each 16-bit unicode character inside a <see cref="Run"/> element
        /// is considered as one symbol.</para>
        /// <para>For instance, for the following xaml:
        /// &lt;Run&gt;abc&lt;/Run&gt;&lt;InlineUIContainer&gt;&lt;Button&gt;OK&lt;/Button&gt;&lt;/InlineUIContainer&gt;
        /// the offset from itw content start to content end will be 8 -
        /// one for each of: (1) Run start, (2) "a", (3) "b", (4) "c", (5) Run end, (6) InlineUIContainer start,
        /// (7) whole Button element, (8) InlineUIContainer end. Note that <c>Button</c>
        /// element considered as one symbol even though it is represented
        /// by two tags and two characters.</para>
        /// </remarks>
        /// <example>
        /// <para>In this example we show how to use TextPointer offsets for
        /// persisting positional information. Assuming that the content of
        /// a RichTextBox is not changed between calls of
        /// GetPersistedSelection and RestoreSelectionFromPersistedRange
        /// methods, the selection will be restored to its original state.</para>
        /// <code>
        ///     struct PersistedTextRange { int Start; int End; }
        ///
        ///     PersistedTextRange GetPersistedSelection(RichTextBox richTextBox)
        ///     {
        ///         PersistedTextRange persistedSelection;
        ///
        ///         TextPointer contentStart = richTextBox.Document.ContentStart;
        ///         persistedSelection.Start = contentStart.GetOffsetToPosition(richTextBox.Selection.Start);
        ///         persistedSelection.End = contentStart.GetOffsetToPosition(richTextBox.Selection.End);
        ///
        ///         return persistedSelection;
        ///     }
        ///
        ///     RestoreSelectionFromPersistedRange(RichTextBox richTextBox, PersistedTextRange persistedRange)
        ///     {
        ///         TextPointer contentStart = richTextBox.Document.ContentStart;
        ///
        ///         richTextBox.Selection.Select(
        ///             contentStart.GetPositionAtOffset(persistedRange.Start),
        ///             contentStart.GetPositionAtOffset(persistedRange.End));
        ///     }
        ///
        /// </code>
        /// </example>
        public int GetOffsetToPosition(TextPointer position)
        {
            _tree.EmptyDeadPositionList();

            ValidationHelper.VerifyPosition(_tree, position);

            SyncToTreeGeneration();
            position.SyncToTreeGeneration();

            return (position.GetSymbolOffset() - GetSymbolOffset());
        }
Пример #8
0
        /// <summary>
        /// Compares positions of this TextPointer with another TextPointer.
        /// </summary>
        /// <param name="position">
        /// The TextPointer to compare with.
        /// </param>
        /// <returns>
        /// Less than zero: this TextPointer preceeds position.
        /// Zero: this TextPointer is at the same location as position.
        /// Greater than zero: this TextPointer follows position.
        /// </returns>
        /// <exception cref="System.ArgumentException">
        /// Throws ArgumentException if position does not belong to the same
        /// text container as this TextPointer (you can use <see cref="TextPointer.IsInSameDocument"/>
        /// method to detect whether comparison is possible).
        /// </exception>
        public int CompareTo(TextPointer position)
        {
            int offsetThis;
            int offsetPosition;
            int result;

            _tree.EmptyDeadPositionList();

            ValidationHelper.VerifyPosition(_tree, position);

            SyncToTreeGeneration();
            position.SyncToTreeGeneration();

            offsetThis = GetSymbolOffset();
            offsetPosition = position.GetSymbolOffset();

            if (offsetThis < offsetPosition)
            {
                result = -1;
            }
            else if (offsetThis > offsetPosition)
            {
                result = +1;
            }
            else
            {
                result = 0;
            }

            return result;
        }
Пример #9
0
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------

        #region Private Methods

        // Called by the TextPointer ctor.  Initializes this instance.
        private void InitializeOffset(TextPointer position, int distance, LogicalDirection direction)
        {
            SplayTreeNode node;
            ElementEdge edge;
            int offset;
            bool isCaretUnitBoundaryCacheValid;

            // We MUST [....] to the current tree, otherwise we could addref
            // an orphaned node, resulting in a future unmatched release...
            // Ref counts on orphaned nodes are only considered at the time
            // of removal, not afterwards.
            position.SyncToTreeGeneration();

            if (distance != 0)
            {
                offset = position.GetSymbolOffset() + distance;
                if (offset < 1 || offset > position.TextContainer.InternalSymbolCount - 1)
                {
                    throw new ArgumentException(SR.Get(SRID.BadDistance));
                }

                position.TextContainer.GetNodeAndEdgeAtOffset(offset, out node, out edge);

                isCaretUnitBoundaryCacheValid = false;
            }
            else
            {
                node = position.Node;
                edge = position.Edge;
                isCaretUnitBoundaryCacheValid = position.IsCaretUnitBoundaryCacheValid;
            }

            Initialize(position.TextContainer, (TextTreeNode)node, edge, direction, position.TextContainer.PositionGeneration,
                position.CaretUnitBoundaryCache, isCaretUnitBoundaryCacheValid, position._layoutGeneration);
        }
Пример #10
0
        /// <summary>
        /// Moves this TextPointer to another TextPointer's position.
        /// </summary>
        /// <param name="textPosition">
        /// Position to move to.
        /// </param>
        /// <exception cref="System.ArgumentException">
        /// Throws an ArgumentException if textPosition is not
        /// positioned within the same document.
        /// </exception>
        /// <exception cref="System.InvalidOperationException">
        /// Throws an InvalidOperationException if this TextPointer's IsFrozen
        /// property is set true.  Frozen TextPointers may not be repositioned.
        /// </exception>
        internal void MoveToPosition(TextPointer textPosition)
        {
            ValidationHelper.VerifyPosition(_tree, textPosition);

            VerifyNotFrozen();

            _tree.EmptyDeadPositionList();

            SyncToTreeGeneration();
            textPosition.SyncToTreeGeneration();

            MoveToNode(_tree, textPosition.Node, textPosition.Edge);
        }