// Copies a run of text into a ContentContainer. // Returns the next node to examine. private TextTreeNode CopyTextNode(TextTreeTextNode textNode, TextTreeNode haltNode, out ContentContainer container) { SplayTreeNode node; char[] text; int count; int symbolOffset; Invariant.Assert(textNode != haltNode, "Expect at least one node to copy!"); symbolOffset = textNode.GetSymbolOffset(this.TextContainer.Generation); // Get a count of all the characters we're about to copy. count = 0; node = textNode; do { count += textNode.SymbolCount; node = textNode.GetNextNode(); textNode = node as TextTreeTextNode; }while (textNode != null && textNode != haltNode); // Allocate storage. text = new char[count]; // Copy the text. TextTreeText.ReadText(this.TextContainer.RootTextBlock, symbolOffset, count, text, 0 /*startIndex*/); container = new TextContentContainer(text); return((TextTreeNode)node); }
// Token: 0x06003E0B RID: 15883 RVA: 0x0011D230 File Offset: 0x0011B430 internal TextTreeTextNode Split(int localOffset, ElementEdge edge) { Invariant.Assert(this._symbolCount > 0, "Splitting a zero-width TextNode!"); Invariant.Assert(localOffset >= 0 && localOffset <= this._symbolCount, "Bad localOffset!"); Invariant.Assert(edge == ElementEdge.BeforeStart || edge == ElementEdge.AfterEnd, "Bad edge parameter!"); TextTreeTextNode textTreeTextNode = new TextTreeTextNode(); textTreeTextNode._generation = this._generation; base.Splay(); ElementEdge edge2; TextTreeTextNode result; if (this._positionRefCount > 0 && this._referencedEdge == ElementEdge.BeforeStart) { textTreeTextNode._symbolOffsetCache = ((this._symbolOffsetCache == -1) ? -1 : (this._symbolOffsetCache + localOffset)); textTreeTextNode._symbolCount = this._symbolCount - localOffset; this._symbolCount = localOffset; edge2 = ElementEdge.AfterEnd; result = ((edge == ElementEdge.BeforeStart) ? this : textTreeTextNode); } else { textTreeTextNode._symbolOffsetCache = this._symbolOffsetCache; textTreeTextNode._symbolCount = localOffset; this._symbolOffsetCache = ((this._symbolOffsetCache == -1) ? -1 : (this._symbolOffsetCache + localOffset)); this._symbolCount -= localOffset; edge2 = ElementEdge.BeforeStart; result = ((edge == ElementEdge.BeforeStart) ? textTreeTextNode : this); } Invariant.Assert(this._symbolCount >= 0); Invariant.Assert(textTreeTextNode._symbolCount >= 0); textTreeTextNode.InsertAtNode(this, edge2); return(result); }
// Token: 0x06003E07 RID: 15879 RVA: 0x0011D0F8 File Offset: 0x0011B2F8 internal override TextTreeNode Clone() { TextTreeTextNode textTreeTextNode = null; if (this._symbolCount > 0) { textTreeTextNode = new TextTreeTextNode(); textTreeTextNode._symbolCount = this._symbolCount; } return(textTreeTextNode); }
//------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods // Returns a shallow copy of this node. // Returns null if this node has a zero symbol count -- // a clone would have no references, so there's no point // in allocating one. internal override TextTreeNode Clone() { TextTreeTextNode clone; clone = null; if (_symbolCount > 0) { clone = new TextTreeTextNode(); clone._symbolCount = _symbolCount; } return(clone); }
// Token: 0x06003D13 RID: 15635 RVA: 0x0011BAC8 File Offset: 0x00119CC8 private TextTreeNode CopyTextNode(TextTreeTextNode textNode, TextTreeNode haltNode, out TextTreeDeleteContentUndoUnit.ContentContainer container) { Invariant.Assert(textNode != haltNode, "Expect at least one node to copy!"); int symbolOffset = textNode.GetSymbolOffset(base.TextContainer.Generation); int num = 0; SplayTreeNode nextNode; do { num += textNode.SymbolCount; nextNode = textNode.GetNextNode(); textNode = (nextNode as TextTreeTextNode); }while (textNode != null && textNode != haltNode); char[] array = new char[num]; TextTreeText.ReadText(base.TextContainer.RootTextBlock, symbolOffset, num, array, 0); container = new TextTreeDeleteContentUndoUnit.TextContentContainer(array); return((TextTreeNode)nextNode); }
// Token: 0x06003E28 RID: 15912 RVA: 0x0011D458 File Offset: 0x0011B658 private void Merge() { Invariant.Assert(this._positionRefCount == 0, "Inappropriate Merge call!"); TextTreeTextNode textTreeTextNode = base.GetPreviousNode() as TextTreeTextNode; if (textTreeTextNode != null && (textTreeTextNode._positionRefCount == 0 || textTreeTextNode._referencedEdge == ElementEdge.BeforeStart)) { base.Remove(); this._parentNode = null; textTreeTextNode.Splay(); textTreeTextNode._symbolCount += this._symbolCount; } else { textTreeTextNode = this; } TextTreeTextNode textTreeTextNode2 = textTreeTextNode.GetNextNode() as TextTreeTextNode; if (textTreeTextNode2 != null) { if (textTreeTextNode._positionRefCount == 0 && (textTreeTextNode2._positionRefCount == 0 || textTreeTextNode2._referencedEdge == ElementEdge.AfterEnd)) { textTreeTextNode.Remove(); textTreeTextNode._parentNode = null; textTreeTextNode2.Splay(); if (textTreeTextNode2._symbolOffsetCache != -1) { textTreeTextNode2._symbolOffsetCache -= textTreeTextNode._symbolCount; } textTreeTextNode2._symbolCount += textTreeTextNode._symbolCount; return; } if ((textTreeTextNode._positionRefCount == 0 || textTreeTextNode._referencedEdge == ElementEdge.BeforeStart) && textTreeTextNode2._positionRefCount == 0) { textTreeTextNode2.Remove(); textTreeTextNode2._parentNode = null; textTreeTextNode.Splay(); textTreeTextNode._symbolCount += textTreeTextNode2._symbolCount; } } }
// Token: 0x06003D12 RID: 15634 RVA: 0x0011BA44 File Offset: 0x00119C44 private TextTreeDeleteContentUndoUnit.ContentContainer CopyContent(TextTreeNode node, TextTreeNode haltNode) { TextTreeDeleteContentUndoUnit.ContentContainer result = null; TextTreeDeleteContentUndoUnit.ContentContainer contentContainer = null; while (node != haltNode && node != null) { TextTreeTextNode textTreeTextNode = node as TextTreeTextNode; TextTreeDeleteContentUndoUnit.ContentContainer contentContainer2; if (textTreeTextNode != null) { node = this.CopyTextNode(textTreeTextNode, haltNode, out contentContainer2); } else { TextTreeObjectNode textTreeObjectNode = node as TextTreeObjectNode; if (textTreeObjectNode != null) { node = this.CopyObjectNode(textTreeObjectNode, out contentContainer2); } else { Invariant.Assert(node is TextTreeTextElementNode, "Unexpected TextTreeNode type!"); TextTreeTextElementNode elementNode = (TextTreeTextElementNode)node; node = this.CopyElementNode(elementNode, out contentContainer2); } } if (contentContainer == null) { result = contentContainer2; } else { contentContainer.NextContainer = contentContainer2; } contentContainer = contentContainer2; } return(result); }
// Copies a run of text into a ContentContainer. // Returns the next node to examine. private TextTreeNode CopyTextNode(TextTreeTextNode textNode, TextTreeNode haltNode, out ContentContainer container) { SplayTreeNode node; char[] text; int count; int symbolOffset; Invariant.Assert(textNode != haltNode, "Expect at least one node to copy!"); symbolOffset = textNode.GetSymbolOffset(this.TextContainer.Generation); // Get a count of all the characters we're about to copy. count = 0; node = textNode; do { count += textNode.SymbolCount; node = textNode.GetNextNode(); textNode = node as TextTreeTextNode; } while (textNode != null && textNode != haltNode); // Allocate storage. text = new char[count]; // Copy the text. TextTreeText.ReadText(this.TextContainer.RootTextBlock, symbolOffset, count, text, 0 /*startIndex*/); container = new TextContentContainer(text); return (TextTreeNode)node; }
// Splits this node into two adjacent nodes. // Returns the node which contains the original requested edge // (e.g., if edge == ElementEdge.BeforeStart, this method returns the // preceding node, if edge == ElementEdge.AfterEnd, it returns the // following node). internal TextTreeTextNode Split(int localOffset, ElementEdge edge) { TextTreeTextNode newNode; TextTreeTextNode edgeNode; ElementEdge newNodeEdge; Invariant.Assert(_symbolCount > 0, "Splitting a zero-width TextNode!"); Invariant.Assert(localOffset >= 0 && localOffset <= _symbolCount, "Bad localOffset!"); Invariant.Assert(edge == ElementEdge.BeforeStart || edge == ElementEdge.AfterEnd, "Bad edge parameter!"); #if DEBUG if (localOffset == 0) { TextTreeNode previousNode; Invariant.Assert(edge == ElementEdge.BeforeStart, "Unexpected edge!"); Invariant.Assert(edge != _referencedEdge, "Splitting at referenced edge!"); previousNode = (TextTreeNode)GetPreviousNode(); Invariant.Assert(previousNode == null || previousNode.SymbolCount > 0 || previousNode.AfterEndReferenceCount, "Found preceding zero-width text node inside Split!"); } else if (localOffset == _symbolCount) { TextTreeNode nextNode; Invariant.Assert(edge == ElementEdge.AfterEnd, "Unexpected edge!"); Invariant.Assert(edge != _referencedEdge, "Splitting at referenced edge!"); nextNode = (TextTreeNode)GetNextNode(); Invariant.Assert(nextNode == null || nextNode.SymbolCount > 0 || nextNode.BeforeStartReferenceCount, "Found following zero-width text node inside Split! (2)"); } #endif // DEBUG newNode = new TextTreeTextNode(); newNode._generation = _generation; // Splay this node to the root so we don't corrupt any LeftSymbolCounts // of ancestor nodes when we fixup _symbolCount below. Splay(); if (_positionRefCount > 0 && _referencedEdge == ElementEdge.BeforeStart) { // New node is the following node. newNode._symbolOffsetCache = (_symbolOffsetCache == -1) ? -1 : _symbolOffsetCache + localOffset; newNode._symbolCount = _symbolCount - localOffset; _symbolCount = localOffset; newNodeEdge = ElementEdge.AfterEnd; edgeNode = (edge == ElementEdge.BeforeStart) ? this : newNode; } else { // New node is the preceding node. newNode._symbolOffsetCache = _symbolOffsetCache; newNode._symbolCount = localOffset; _symbolOffsetCache = (_symbolOffsetCache == -1) ? -1 : _symbolOffsetCache + localOffset; _symbolCount -= localOffset; newNodeEdge = ElementEdge.BeforeStart; edgeNode = (edge == ElementEdge.BeforeStart) ? newNode : this; } Invariant.Assert(_symbolCount >= 0); Invariant.Assert(newNode._symbolCount >= 0); newNode.InsertAtNode(this, newNodeEdge); return(edgeNode); }
//------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods // Returns a shallow copy of this node. // Returns null if this node has a zero symbol count -- // a clone would have no references, so there's no point // in allocating one. internal override TextTreeNode Clone() { TextTreeTextNode clone; clone = null; if (_symbolCount > 0) { clone = new TextTreeTextNode(); clone._symbolCount = _symbolCount; } return clone; }
// Splits this node into two adjacent nodes. // Returns the node which contains the original requested edge // (e.g., if edge == ElementEdge.BeforeStart, this method returns the // preceding node, if edge == ElementEdge.AfterEnd, it returns the // following node). internal TextTreeTextNode Split(int localOffset, ElementEdge edge) { TextTreeTextNode newNode; TextTreeTextNode edgeNode; ElementEdge newNodeEdge; Invariant.Assert(_symbolCount > 0, "Splitting a zero-width TextNode!"); Invariant.Assert(localOffset >= 0 && localOffset <= _symbolCount, "Bad localOffset!"); Invariant.Assert(edge == ElementEdge.BeforeStart || edge == ElementEdge.AfterEnd, "Bad edge parameter!"); #if DEBUG if (localOffset == 0) { TextTreeNode previousNode; Invariant.Assert(edge == ElementEdge.BeforeStart, "Unexpected edge!"); Invariant.Assert(edge != _referencedEdge, "Splitting at referenced edge!"); previousNode = (TextTreeNode)GetPreviousNode(); Invariant.Assert(previousNode == null || previousNode.SymbolCount > 0 || previousNode.AfterEndReferenceCount, "Found preceding zero-width text node inside Split!"); } else if (localOffset == _symbolCount) { TextTreeNode nextNode; Invariant.Assert(edge == ElementEdge.AfterEnd, "Unexpected edge!"); Invariant.Assert(edge != _referencedEdge, "Splitting at referenced edge!"); nextNode = (TextTreeNode)GetNextNode(); Invariant.Assert(nextNode == null || nextNode.SymbolCount > 0 || nextNode.BeforeStartReferenceCount, "Found following zero-width text node inside Split! (2)"); } #endif // DEBUG newNode = new TextTreeTextNode(); newNode._generation = _generation; // Splay this node to the root so we don't corrupt any LeftSymbolCounts // of ancestor nodes when we fixup _symbolCount below. Splay(); if (_positionRefCount > 0 && _referencedEdge == ElementEdge.BeforeStart) { // New node is the following node. newNode._symbolOffsetCache = (_symbolOffsetCache == -1) ? -1 : _symbolOffsetCache + localOffset; newNode._symbolCount = _symbolCount - localOffset; _symbolCount = localOffset; newNodeEdge = ElementEdge.AfterEnd; edgeNode = (edge == ElementEdge.BeforeStart) ? this : newNode; } else { // New node is the preceding node. newNode._symbolOffsetCache = _symbolOffsetCache; newNode._symbolCount = localOffset; _symbolOffsetCache = (_symbolOffsetCache == -1) ? -1 : _symbolOffsetCache + localOffset; _symbolCount -= localOffset; newNodeEdge = ElementEdge.BeforeStart; edgeNode = (edge == ElementEdge.BeforeStart) ? newNode : this; } Invariant.Assert(_symbolCount >= 0); Invariant.Assert(newNode._symbolCount >= 0); newNode.InsertAtNode(this, newNodeEdge); return edgeNode; }
// 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(); } }
internal static int GetTextInRun(TextContainer textContainer, int symbolOffset, TextTreeTextNode textNode, int nodeOffset, LogicalDirection direction, char[] textBuffer, int startIndex, int count) { int skipCount; int finalCount; if (textBuffer == null) { throw new ArgumentNullException("textBuffer"); } if (startIndex < 0) { throw new ArgumentException(SR.Get(SRID.NegativeValue, "startIndex")); } if (startIndex > textBuffer.Length) { throw new ArgumentException(SR.Get(SRID.StartIndexExceedsBufferSize, startIndex, textBuffer.Length)); } if (count < 0) { throw new ArgumentException(SR.Get(SRID.NegativeValue, "count")); } if (count > textBuffer.Length - startIndex) { throw new ArgumentException(SR.Get(SRID.MaxLengthExceedsBufferSize, count, textBuffer.Length, startIndex)); } Invariant.Assert(textNode != null, "textNode is expected to be non-null"); textContainer.EmptyDeadPositionList(); if (nodeOffset < 0) { skipCount = 0; } else { skipCount = (direction == LogicalDirection.Forward) ? nodeOffset : textNode.SymbolCount - nodeOffset; symbolOffset += nodeOffset; } finalCount = 0; // Loop and combine adjacent text nodes into a single run. // This isn't just a perf optimization. Because text positions // split text nodes, if we just returned a single node's text // callers would see strange side effects where position.GetTextLength() != // position.GetText() if another position is moved between the calls. while (textNode != null) { // Never return more textBuffer than the text following this position in the current text node. finalCount += Math.Min(count - finalCount, textNode.SymbolCount - skipCount); skipCount = 0; if (finalCount == count) break; textNode = ((direction == LogicalDirection.Forward) ? textNode.GetNextNode() : textNode.GetPreviousNode()) as TextTreeTextNode; } // If we're reading backwards, need to fixup symbolOffset to point into the node. if (direction == LogicalDirection.Backward) { symbolOffset -= finalCount; } if (finalCount > 0) // We may not have allocated textContainer.RootTextBlock if no text was ever inserted. { TextTreeText.ReadText(textContainer.RootTextBlock, symbolOffset, finalCount, textBuffer, startIndex); } return finalCount; }