/// <summary> /// Merges two naighboring lists ending and starting at position mergePosition /// </summary> /// <param name="mergePosition"> /// Position at with two List elements are expected to appear next to each other /// </param> /// <returns> /// true if there were two mergeable List elements and merge happened. /// false if there is no pair of List elements at the mergePosition. /// </returns> internal static bool MergeLists(TextPointer mergePosition) { if (mergePosition.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.ElementEnd || mergePosition.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.ElementStart) { return(false); } List precedingList = mergePosition.GetAdjacentElement(LogicalDirection.Backward) as List; List followingList = mergePosition.GetAdjacentElement(LogicalDirection.Forward) as List; if (precedingList == null || followingList == null) { return(false); } precedingList.Reposition(precedingList.ContentStart, followingList.ElementEnd); followingList.Reposition(null, null); // We need to set appropriate FlowDirection property on the new List and its paragraph children. // We take the FlowDirection value from the preceding list. TextRangeEdit.SetParagraphProperty(precedingList.ElementStart, precedingList.ElementEnd, Paragraph.FlowDirectionProperty, precedingList.GetValue(Paragraph.FlowDirectionProperty)); return(true); }
// Returns true if passed textelement has any Hyperlink or AnchoredBlock descendant. // It this context, the element or one of its ancestors is a Hyperlink. private static bool HasIllegalHyperlinkDescendant(TextElement element, bool throwIfIllegalDescendent) { TextPointer start = element.ElementStart; TextPointer end = element.ElementEnd; while (start.CompareTo(end) < 0) { TextPointerContext forwardContext = start.GetPointerContext(LogicalDirection.Forward); if (forwardContext == TextPointerContext.ElementStart) { TextElement nextElement = (TextElement)start.GetAdjacentElement(LogicalDirection.Forward); if (nextElement is Hyperlink || nextElement is AnchoredBlock) { if (throwIfIllegalDescendent) { throw new InvalidOperationException(SR.Get(SRID.TextSchema_IllegalHyperlinkChild, nextElement.GetType())); } return(true); } } start = start.GetNextContextPosition(LogicalDirection.Forward); } return(false); }
// Token: 0x06003C04 RID: 15364 RVA: 0x00114F28 File Offset: 0x00113128 private static bool HasIllegalHyperlinkDescendant(TextElement element, bool throwIfIllegalDescendent) { TextPointer textPointer = element.ElementStart; TextPointer elementEnd = element.ElementEnd; while (textPointer.CompareTo(elementEnd) < 0) { TextPointerContext pointerContext = textPointer.GetPointerContext(LogicalDirection.Forward); if (pointerContext == TextPointerContext.ElementStart) { TextElement textElement = (TextElement)textPointer.GetAdjacentElement(LogicalDirection.Forward); if (textElement is Hyperlink || textElement is AnchoredBlock) { if (throwIfIllegalDescendent) { throw new InvalidOperationException(SR.Get("TextSchema_IllegalHyperlinkChild", new object[] { textElement.GetType() })); } return(true); } } textPointer = textPointer.GetNextContextPosition(LogicalDirection.Forward); } return(false); }
// Token: 0x06003B80 RID: 15232 RVA: 0x0010EE34 File Offset: 0x0010D034 internal static bool MergeLists(TextPointer mergePosition) { if (mergePosition.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.ElementEnd || mergePosition.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.ElementStart) { return(false); } List list = mergePosition.GetAdjacentElement(LogicalDirection.Backward) as List; List list2 = mergePosition.GetAdjacentElement(LogicalDirection.Forward) as List; if (list == null || list2 == null) { return(false); } list.Reposition(list.ContentStart, list2.ElementEnd); list2.Reposition(null, null); TextRangeEdit.SetParagraphProperty(list.ElementStart, list.ElementEnd, Block.FlowDirectionProperty, list.GetValue(Block.FlowDirectionProperty)); return(true); }
/// <summary>Initializes a new instance of the <see cref="T:System.Windows.Documents.Hyperlink" /> class, taking two <see cref="T:System.Windows.Documents.TextPointer" /> objects that indicate the beginning and end of a selection of content to be contained by the new <see cref="T:System.Windows.Documents.Hyperlink" />.</summary> /// <param name="start">A <see cref="T:System.Windows.Documents.TextPointer" /> indicating the beginning of a selection of content to be contained by the new <see cref="T:System.Windows.Documents.Hyperlink" />.</param> /// <param name="end">A <see cref="T:System.Windows.Documents.TextPointer" /> indicating the end of a selection of content to be contained by the new <see cref="T:System.Windows.Documents.Hyperlink" />.</param> /// <exception cref="T:System.ArgumentNullException"> /// <paramref name="start" /> or <paramref name="end" /> is <see langword="null" />.</exception> /// <exception cref="T:System.ArgumentException"> /// <paramref name="start" /> and <paramref name="end" /> do not resolve to a range of content suitable for enclosure by a <see cref="T:System.Windows.Documents.Span" /> element; for example, if <paramref name="start" /> and <paramref name="end" /> indicate positions in different paragraphs.</exception> // Token: 0x0600301F RID: 12319 RVA: 0x000D8A18 File Offset: 0x000D6C18 public Hyperlink(TextPointer start, TextPointer end) : base(start, end) { TextPointer textPointer = base.ContentStart.CreatePointer(); TextPointer contentEnd = base.ContentEnd; while (textPointer.CompareTo(contentEnd) < 0) { Hyperlink hyperlink = textPointer.GetAdjacentElement(LogicalDirection.Forward) as Hyperlink; if (hyperlink != null) { hyperlink.Reposition(null, null); } else { textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } } }
/// <summary> /// Creates a new Hyperlink instance covering existing content. /// </summary> /// <param name="start"> /// Start position of the new Hyperlink. /// </param> /// <param name="end"> /// End position of the new Hyperlink. /// </param> /// <remarks> /// start and end must both be parented by the same Paragraph, otherwise /// the method will raise an ArgumentException. /// </remarks> public Hyperlink(TextPointer start, TextPointer end) : base(start, end) { // After inserting this Hyperlink, we need to extract any child Hyperlinks. TextPointer navigator = this.ContentStart.CreatePointer(); TextPointer stop = this.ContentEnd; while (navigator.CompareTo(stop) < 0) { Hyperlink hyperlink = navigator.GetAdjacentElement(LogicalDirection.Forward) as Hyperlink; if (hyperlink != null) { hyperlink.Reposition(null, null); } else { navigator.MoveToNextContextPosition(LogicalDirection.Forward); } } }
public void SetCell(BufferCell cell, TextPointer position, LogicalDirection direction) { TextRange range = null; TextPointer positionPlus = position.GetNextInsertionPosition(LogicalDirection.Forward); if (positionPlus != null) { range = new TextRange(position, positionPlus); } if (null == range || range.IsEmpty) { position = position.GetInsertionPosition(LogicalDirection.Forward); if (position != null) { Run r = position.GetAdjacentElement(LogicalDirection.Forward) as Run; if (null != r) { if (r.Text.Length > 0) { char[] chr = r.Text.ToCharArray(); chr[0] = cell.Character; r.Text = chr.ToString(); } else { r.Text = cell.Character.ToString(); } } else { r = position.GetAdjacentElement(LogicalDirection.Backward) as Run; if (null != r && r.Background == BrushFromConsoleColor(cell.BackgroundColor) && r.Foreground == BrushFromConsoleColor(cell.ForegroundColor) ) { if (r.Text.Length > 0) { r.Text = r.Text + cell.Character; } else { r.Text = cell.Character.ToString(); } } else { r = new Run(cell.Character.ToString(), position); } } r.Background = BrushFromConsoleColor(cell.BackgroundColor); r.Foreground = BrushFromConsoleColor(cell.ForegroundColor); //position = r.ElementStart; } } else { range.Text = cell.Character.ToString(); range.ApplyPropertyValue(TextElement.BackgroundProperty, BrushFromConsoleColor(cell.BackgroundColor)); range.ApplyPropertyValue(TextElement.ForegroundProperty, BrushFromConsoleColor(cell.ForegroundColor)); } }
/// <summary> /// Merges two naighboring lists ending and starting at position mergePosition /// </summary> /// <param name="mergePosition"> /// Position at with two List elements are expected to appear next to each other /// </param> /// <returns> /// true if there were two mergeable List elements and merge happened. /// false if there is no pair of List elements at the mergePosition. /// </returns> internal static bool MergeLists(TextPointer mergePosition) { if (mergePosition.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.ElementEnd || mergePosition.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.ElementStart) { return false; } List precedingList = mergePosition.GetAdjacentElement(LogicalDirection.Backward) as List; List followingList = mergePosition.GetAdjacentElement(LogicalDirection.Forward) as List; if (precedingList == null || followingList == null) { return false; } precedingList.Reposition(precedingList.ContentStart, followingList.ElementEnd); followingList.Reposition(null, null); // We need to set appropriate FlowDirection property on the new List and its paragraph children. // We take the FlowDirection value from the preceding list. TextRangeEdit.SetParagraphProperty(precedingList.ElementStart, precedingList.ElementEnd, Paragraph.FlowDirectionProperty, precedingList.GetValue(Paragraph.FlowDirectionProperty)); return true; }
// Merges inline elements with equivalent formatting properties at a given position // Returns true if some changes happened at this position, false otherwise internal static bool MergeFormattingInlines(TextPointer position) { // Remove unnecessary Spans around this position RemoveUnnecessarySpans(position); // Delete empty formatting elements at this position (if any) ExtractEmptyFormattingElements(position); // Skip formatting tags towards potential merging position while (position.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart && TextSchema.IsMergeableInline(position.Parent.GetType())) { position = ((Inline)position.Parent).ElementStart; } while (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd && TextSchema.IsMergeableInline(position.Parent.GetType())) { position = ((Inline)position.Parent).ElementEnd; } // Merge formatting Inlines at this position Inline firstInline, secondInline; bool merged = false; while ( position.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementEnd && position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart && (firstInline = position.GetAdjacentElement(LogicalDirection.Backward) as Inline) != null && (secondInline = position.GetAdjacentElement(LogicalDirection.Forward) as Inline) != null) { if (TextSchema.IsFormattingType(firstInline.GetType()) && firstInline.TextRange.IsEmpty) { firstInline.RepositionWithContent(null); merged = true; } else if (TextSchema.IsFormattingType(secondInline.GetType()) && secondInline.TextRange.IsEmpty) { secondInline.RepositionWithContent(null); merged = true; } else if (TextSchema.IsKnownType(firstInline.GetType()) && TextSchema.IsKnownType(secondInline.GetType()) && (firstInline is Run && secondInline is Run || firstInline is Span && secondInline is Span) && TextSchema.IsMergeableInline(firstInline.GetType()) && TextSchema.IsMergeableInline(secondInline.GetType()) && CharacterPropertiesAreEqual(firstInline, secondInline)) { firstInline.Reposition(firstInline.ElementStart, secondInline.ElementEnd); secondInline.Reposition(null, null); merged = true; } else { break; } } // Now that Inlines have been merged we can try to optimize tree structure // by eliminating some unecessary wrapping Inlines if (merged) { RemoveUnnecessarySpans(position); } return merged; }
// -------------------------------------------------------------------- // // Internal Methods // // -------------------------------------------------------------------- #region Internal Methods /// <summary> /// Merges two paragraphs followinng one another. /// The content of a second paragraph is moved into the end /// of the first one. /// </summary> /// <param name="firstParagraphOrBlockUIContainer"> /// First of two merged paragraphs or BlockUIContainer. /// </param> /// <param name="secondParagraphOrBlockUIContainer"> /// Second of two mered paragraphs or BlockUIContainer. /// </param> /// <returns> /// true if paragraphs have been merged; false if no actions where made. /// </returns> internal static bool MergeParagraphs(Block firstParagraphOrBlockUIContainer, Block secondParagraphOrBlockUIContainer) { if (!ParagraphsAreMergeable(firstParagraphOrBlockUIContainer, secondParagraphOrBlockUIContainer)) { return(false); // Cannot mearge these paragraphs. } // Store parent list item of a second paragraph - // to correct its structure after the merge ListItem secondListItem = secondParagraphOrBlockUIContainer.PreviousBlock == null ? secondParagraphOrBlockUIContainer.Parent as ListItem : null; if (secondListItem != null && secondListItem.PreviousListItem == null && secondParagraphOrBlockUIContainer.NextBlock is List) { // The second paragraph is a first list item in some list. // It has a sublists in it, so this sublist must be unindented // to avoid double bulleted line. List sublistOfSecondParagraph = (List)secondParagraphOrBlockUIContainer.NextBlock; if (sublistOfSecondParagraph.ElementEnd.CompareTo(secondListItem.ContentEnd) == 0) { secondListItem.Reposition(null, null); } else { secondListItem.Reposition(sublistOfSecondParagraph.ElementEnd, secondListItem.ContentEnd); } // At this point the schema is temporaty broken: the secondParagraph and the sublistOfSecondParagraph have List as a parent sublistOfSecondParagraph.Reposition(null, null); // The schema is repared as to sublistOfSecondParagraph concern, but still broken for secondParagraph - must be corrected in the following code } // Move the second paragraph out of its wrappers separating from the first paragraph (if any). // We can not use RepositionWithContent because it would destroy // all pointers and ranges within a moved paragraph. // Instead we reposition elements around the two paragraphs. while (secondParagraphOrBlockUIContainer.ElementStart.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart) { TextElement parentBlock = (TextElement)secondParagraphOrBlockUIContainer.Parent; Invariant.Assert(parentBlock != null); Invariant.Assert(TextSchema.AllowsParagraphMerging(parentBlock.GetType())); if (secondParagraphOrBlockUIContainer.ElementEnd.CompareTo(parentBlock.ContentEnd) == 0) { // Remove ancestor block if it becomes empty parentBlock.Reposition(null, null); } else { // Move ancestor's Start after the end of our paragraph parentBlock.Reposition(secondParagraphOrBlockUIContainer.ElementEnd, parentBlock.ContentEnd); } } // Store a position after the second paragraph where list merging may be needed TextPointer positionAfterSecondParagraph = secondParagraphOrBlockUIContainer.ElementEnd.GetFrozenPointer(LogicalDirection.Forward); // Move the second paragraph to become an immediate following sibling of the first paragraph while (true) { TextElement previousBlock = secondParagraphOrBlockUIContainer.ElementStart.GetAdjacentElement(LogicalDirection.Backward) as TextElement; // Note: We cannot use Block.NextSibling property, because the structure is invalid during this process Invariant.Assert(previousBlock != null); if (previousBlock is Paragraph || previousBlock is BlockUIContainer) { break; } Invariant.Assert(TextSchema.AllowsParagraphMerging(previousBlock.GetType())); previousBlock.Reposition(previousBlock.ContentStart, secondParagraphOrBlockUIContainer.ElementEnd); } // Now that paragraphs are next to each other merge them. // If one of paragraphs is empty we will apply special logic - to preserve a formatting from a non-empty one if (secondParagraphOrBlockUIContainer.TextRange.IsEmpty) { secondParagraphOrBlockUIContainer.RepositionWithContent(null); } else if (firstParagraphOrBlockUIContainer.TextRange.IsEmpty) { firstParagraphOrBlockUIContainer.RepositionWithContent(null); } else if (firstParagraphOrBlockUIContainer is Paragraph && secondParagraphOrBlockUIContainer is Paragraph) { // Do reposition magic for merging paragraph content // without destroying any pointers positioned in them. // Pull the second paragraph into the first one Invariant.Assert(firstParagraphOrBlockUIContainer.ElementEnd.CompareTo(secondParagraphOrBlockUIContainer.ElementStart) == 0); firstParagraphOrBlockUIContainer.Reposition(firstParagraphOrBlockUIContainer.ContentStart, secondParagraphOrBlockUIContainer.ElementEnd); // Store inline merging position TextPointer inlineMergingPosition = secondParagraphOrBlockUIContainer.ElementStart; // Now we can delete the second paragraph secondParagraphOrBlockUIContainer.Reposition(null, null); // Merge formatting elements at the point of paragraphs merging TextRangeEdit.MergeFormattingInlines(inlineMergingPosition); } // Merge ListItems wrapping first and second paragraphs. ListItem followingListItem = positionAfterSecondParagraph.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart ? positionAfterSecondParagraph.GetAdjacentElement(LogicalDirection.Forward) as ListItem : null; if (followingListItem != null && followingListItem == secondListItem) { ListItem precedingListItem = positionAfterSecondParagraph.GetAdjacentElement(LogicalDirection.Backward) as ListItem; if (precedingListItem != null) { // Merge the second list item with the preceding one Invariant.Assert(positionAfterSecondParagraph.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart); Invariant.Assert(positionAfterSecondParagraph.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementEnd); precedingListItem.Reposition(precedingListItem.ContentStart, followingListItem.ElementEnd); followingListItem.Reposition(null, null); } } // Merge lists at merge position MergeLists(positionAfterSecondParagraph); return(true); }
// Helper for DeleteContentInternal. // If startPosition is placed at the very front of its parent's sibling list, // returns the next sibling following endPositoin (the new head of the sibling // list). The new head node is interesting because its IMELeftEdgeCharCount may // change because of its new position. private TextTreeTextElementNode GetNextIMEVisibleNode(TextPointer startPosition, TextPointer endPosition) { TextTreeTextElementNode nextIMEVisibleNode = null; TextElement adjacentElement = startPosition.GetAdjacentElement(LogicalDirection.Forward) as TextElement; if (adjacentElement != null && adjacentElement.IsFirstIMEVisibleSibling) { nextIMEVisibleNode = (TextTreeTextElementNode)endPosition.GetAdjacentSiblingNode(LogicalDirection.Forward); } return nextIMEVisibleNode; }
/// <summary> /// Test if the text pointer is in a hyperlink /// </summary> /// <param name="pos"></param> /// <returns></returns> private bool searchHyperlink(TextPointer pos) { if (null == pos) return false; TextPointerContext context = pos.GetPointerContext(LogicalDirection.Backward); if (null == context) return false; if (TextPointerContext.ElementStart == context) { object elem = pos.GetAdjacentElement(LogicalDirection.Backward); if (elem is Hyperlink) { return true; } } else if (TextPointerContext.ElementEnd == context) { object elem = pos.GetAdjacentElement(LogicalDirection.Backward); if (elem is Hyperlink) { return false; } } // go backwards... TextPointer back = pos.GetNextContextPosition(LogicalDirection.Backward); return searchHyperlink(back); }
/// <summary> /// Writes the container into the specified XmlWriter. /// </summary> private void WriteContainer(TextPointer start, TextPointer end, XmlWriter writer) { TextElement textElement; System.Diagnostics.Debug.Assert(start != null); System.Diagnostics.Debug.Assert(end != null); System.Diagnostics.Debug.Assert(writer != null); _writer = writer; WriteWordXmlHead(); _cursor = start; while (_cursor.CompareTo(end) < 0) { switch (_cursor.GetPointerContext(_dir)) { case TextPointerContext.None: System.Diagnostics.Debug.Assert(false, "Next symbol should never be None if cursor < End."); break; case TextPointerContext.Text: RequireOpenRange(); _writer.WriteStartElement(WordXmlSerializer.WordTextTag); _writer.WriteString(_cursor.GetTextInRun(_dir)); _writer.WriteEndElement(); break; case TextPointerContext.EmbeddedElement: DependencyObject obj = _cursor.GetAdjacentElement(LogicalDirection.Forward); if (obj is LineBreak) { RequireOpenRange(); _writer.WriteStartElement(WordXmlSerializer.WordBreakTag); _writer.WriteEndElement(); } // TODO: try to convert some known embedded objects. break; case TextPointerContext.ElementStart: TextPointer position; position = _cursor; position = position.GetNextContextPosition(LogicalDirection.Forward); textElement = position.Parent as TextElement; if (textElement is Paragraph) { RequireClosedRange(); RequireOpenParagraph(); } else if (textElement is Inline) { RequireClosedRange(); RequireOpenParagraph(); RequireOpenRange(); } break; case TextPointerContext.ElementEnd: textElement = _cursor.Parent as TextElement; if (textElement is Inline) { RequireClosedRange(); } else if (textElement is Paragraph) { RequireClosedParagraph(); } break; } _cursor = _cursor.GetNextContextPosition(_dir); } RequireClosedRange(); WriteWordXmlTail(); }
// Token: 0x06003B7E RID: 15230 RVA: 0x0010EB84 File Offset: 0x0010CD84 internal static bool MergeParagraphs(Block firstParagraphOrBlockUIContainer, Block secondParagraphOrBlockUIContainer) { if (!TextRangeEditLists.ParagraphsAreMergeable(firstParagraphOrBlockUIContainer, secondParagraphOrBlockUIContainer)) { return(false); } ListItem listItem = (secondParagraphOrBlockUIContainer.PreviousBlock == null) ? (secondParagraphOrBlockUIContainer.Parent as ListItem) : null; if (listItem != null && listItem.PreviousListItem == null && secondParagraphOrBlockUIContainer.NextBlock is List) { List list = (List)secondParagraphOrBlockUIContainer.NextBlock; if (list.ElementEnd.CompareTo(listItem.ContentEnd) == 0) { listItem.Reposition(null, null); } else { listItem.Reposition(list.ElementEnd, listItem.ContentEnd); } list.Reposition(null, null); } while (secondParagraphOrBlockUIContainer.ElementStart.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart) { TextElement textElement = (TextElement)secondParagraphOrBlockUIContainer.Parent; Invariant.Assert(textElement != null); Invariant.Assert(TextSchema.AllowsParagraphMerging(textElement.GetType())); if (secondParagraphOrBlockUIContainer.ElementEnd.CompareTo(textElement.ContentEnd) == 0) { textElement.Reposition(null, null); } else { textElement.Reposition(secondParagraphOrBlockUIContainer.ElementEnd, textElement.ContentEnd); } } TextPointer frozenPointer = secondParagraphOrBlockUIContainer.ElementEnd.GetFrozenPointer(LogicalDirection.Forward); for (;;) { TextElement textElement2 = secondParagraphOrBlockUIContainer.ElementStart.GetAdjacentElement(LogicalDirection.Backward) as TextElement; Invariant.Assert(textElement2 != null); if (textElement2 is Paragraph || textElement2 is BlockUIContainer) { break; } Invariant.Assert(TextSchema.AllowsParagraphMerging(textElement2.GetType())); textElement2.Reposition(textElement2.ContentStart, secondParagraphOrBlockUIContainer.ElementEnd); } if (secondParagraphOrBlockUIContainer.TextRange.IsEmpty) { secondParagraphOrBlockUIContainer.RepositionWithContent(null); } else if (firstParagraphOrBlockUIContainer.TextRange.IsEmpty) { firstParagraphOrBlockUIContainer.RepositionWithContent(null); } else if (firstParagraphOrBlockUIContainer is Paragraph && secondParagraphOrBlockUIContainer is Paragraph) { Invariant.Assert(firstParagraphOrBlockUIContainer.ElementEnd.CompareTo(secondParagraphOrBlockUIContainer.ElementStart) == 0); firstParagraphOrBlockUIContainer.Reposition(firstParagraphOrBlockUIContainer.ContentStart, secondParagraphOrBlockUIContainer.ElementEnd); TextPointer elementStart = secondParagraphOrBlockUIContainer.ElementStart; secondParagraphOrBlockUIContainer.Reposition(null, null); TextRangeEdit.MergeFormattingInlines(elementStart); } ListItem listItem2 = (frozenPointer.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart) ? (frozenPointer.GetAdjacentElement(LogicalDirection.Forward) as ListItem) : null; if (listItem2 != null && listItem2 == listItem) { ListItem listItem3 = frozenPointer.GetAdjacentElement(LogicalDirection.Backward) as ListItem; if (listItem3 != null) { Invariant.Assert(frozenPointer.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart); Invariant.Assert(frozenPointer.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementEnd); listItem3.Reposition(listItem3.ContentStart, listItem2.ElementEnd); listItem2.Reposition(null, null); } } TextRangeEditLists.MergeLists(frozenPointer); return(true); }
//------------------------------------------------------------------- // IContentHost Helpers //------------------------------------------------------------------- /// <summary> /// Searches for an element in the _structuralCache.TextContainer. If the element is found, returns the /// position at which it is found. Otherwise returns null. /// </summary> /// <param name="e"> /// Element to be found. /// </param> /// <param name="isLimitedToTextView"> /// bool value indicating whether the search should only be limited to the text view of the page, /// in which case we search only text segments in the text view /// </param> private TextPointer FindElementPosition(IInputElement e, bool isLimitedToTextView) { // Parameter validation Debug.Assert(e != null); // Validate that this function is only called when a TextContainer exists as complex content Debug.Assert(_structuralCache.TextContainer is TextContainer); TextPointer elementPosition = null; // If e is a TextElement we can optimize by checking its TextContainer if (e is TextElement) { if ((e as TextElement).TextContainer == _structuralCache.TextContainer) { // Element found elementPosition = new TextPointer((e as TextElement).ElementStart); } // else: elementPosition stays null } else { // Else: search for e in the complex content if (!(_structuralCache.TextContainer.Start is TextPointer) || !(_structuralCache.TextContainer.End is TextPointer)) { // Invalid TextContainer, don't search return null; } TextPointer searchPosition = new TextPointer(_structuralCache.TextContainer.Start as TextPointer); while (elementPosition == null && ((ITextPointer)searchPosition).CompareTo(_structuralCache.TextContainer.End) < 0) { // Search each position in _structuralCache.TextContainer for the element switch (searchPosition.GetPointerContext(LogicalDirection.Forward)) { case TextPointerContext.EmbeddedElement: DependencyObject embeddedObject = searchPosition.GetAdjacentElement(LogicalDirection.Forward); if (embeddedObject is ContentElement || embeddedObject is UIElement) { if (embeddedObject == e as ContentElement || embeddedObject == e as UIElement) { // Element found. Stop searching elementPosition = new TextPointer(searchPosition); break; } } break; default: break; } searchPosition.MoveToNextContextPosition(LogicalDirection.Forward); } } // If the element was found, check if we are limited to text view if (elementPosition != null) { if (isLimitedToTextView) { // At this point, we should create TextView if it doesn't exist _textView = GetTextView(); Invariant.Assert(_textView != null); // Check all segements in text view for position for (int segmentIndex = 0; segmentIndex < ((ITextView)_textView).TextSegments.Count; segmentIndex++) { if (((ITextPointer)elementPosition).CompareTo(((ITextView)_textView).TextSegments[segmentIndex].Start) >= 0 && ((ITextPointer)elementPosition).CompareTo(((ITextView)_textView).TextSegments[segmentIndex].End) < 0) { // Element lies within a segment. Return position return elementPosition; } } // Element not found in all segments of TextView. Set position to null elementPosition = null; } } return elementPosition; }
// Finds a Run element with ElementStart at or after the given pointer // Creates Runs at potential run positions if encounters some. private static Run GetNextRun(TextPointer pointer, TextPointer limit) { Run run = null; while (pointer != null && pointer.CompareTo(limit) < 0) { if (pointer.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart && (run = pointer.GetAdjacentElement(LogicalDirection.Forward) as Run) != null) { break; } if (TextPointerBase.IsAtPotentialRunPosition(pointer)) { pointer = TextRangeEditTables.EnsureInsertionPosition(pointer); Invariant.Assert(pointer.Parent is Run); run = pointer.Parent as Run; break; } // Advance the scanning pointer pointer = pointer.GetNextContextPosition(LogicalDirection.Forward); } return run; }
private static void ApplyStructuralInlinePropertyAcrossRun(TextPointer start, TextPointer end, Run run, DependencyProperty formattingProperty, object value) { if (start.CompareTo(end) == 0) { // When the range is empty we should ignore the command, except // for the case of empty Run which can be encountered in empty paragraphs if (run.IsEmpty) { run.SetValue(formattingProperty, value); } } else { // Split elements at start and end boundaries. start = SplitFormattingElements(start, /*keepEmptyFormatting:*/false, /*limitingAncestor*/run.Parent as TextElement); end = SplitFormattingElements(end, /*keepEmptyFormatting:*/false, /*limitingAncestor*/run.Parent as TextElement); run = (Run)start.GetAdjacentElement(LogicalDirection.Forward); run.SetValue(formattingProperty, value); } // Clear property value from all ancestors of this Run. FixupStructuralPropertyEnvironment(run, formattingProperty); }
// ------------------------------------------------------------------ // IContentHost Helpers // ------------------------------------------------------------------ /// <summary> /// Searches for an element in the _complexContent.TextContainer. If the element is found, returns the /// position at which it is found. Otherwise returns null. /// </summary> /// <param name="e"> /// Element to be found. /// </param> /// <remarks> /// We assume that this function is called from within text if the caller knows that _complexContent exists /// and contains a TextContainer. Hence we assert for this condition within the function /// </remarks> private TextPointer FindElementPosition(IInputElement e) { // Parameter validation Debug.Assert(e != null); // Validate that this function is only called when a TextContainer exists as complex content Debug.Assert(_complexContent.TextContainer is TextContainer); TextPointer position; // If e is a TextElement we can optimize by checking its TextContainer if (e is TextElement) { if ((e as TextElement).TextContainer == _complexContent.TextContainer) { // Element found position = new TextPointer((e as TextElement).ElementStart); return position; } } // Else: search for e in the complex content position = new TextPointer((TextPointer)_complexContent.TextContainer.Start); while (position.CompareTo((TextPointer)_complexContent.TextContainer.End) < 0) { // Search each position in _complexContent.TextContainer for the element switch (position.GetPointerContext(LogicalDirection.Forward)) { case TextPointerContext.EmbeddedElement: DependencyObject embeddedObject = position.GetAdjacentElement(LogicalDirection.Forward); if (embeddedObject is ContentElement || embeddedObject is UIElement) { if (embeddedObject == e as ContentElement || embeddedObject == e as UIElement) { return position; } } break; default: break; } position.MoveByOffset(+1); } // Reached end of complex content without finding the element return null; }
private static void ApplyStructuralInlinePropertyAcrossInline(TextPointer start, TextPointer end, TextElement commonAncestor, DependencyProperty formattingProperty, object value) { start = SplitFormattingElements(start, /*keepEmptyFormatting:*/false, commonAncestor); end = SplitFormattingElements(end, /*keepEmptyFormatting:*/false, commonAncestor); DependencyObject forwardElement = start.GetAdjacentElement(LogicalDirection.Forward); DependencyObject backwardElement = end.GetAdjacentElement(LogicalDirection.Backward); if (forwardElement == backwardElement && (forwardElement is Run || forwardElement is Span)) { // After splitting we have exactly one Run or Span between start and end. Use it for setting the property. Inline inline = (Inline)start.GetAdjacentElement(LogicalDirection.Forward); // Set the property to existing element. inline.SetValue(formattingProperty, value); // Clear property value from all ancestors of this inline. FixupStructuralPropertyEnvironment(inline, formattingProperty); if (forwardElement is Span) { // Clear property value from all Span and Run children of this span. ClearPropertyValueFromSpansAndRuns(inline.ContentStart, inline.ContentEnd, formattingProperty); } } else { Span span; if (commonAncestor is Span && start.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart && end.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd && start.GetAdjacentElement(LogicalDirection.Backward) == commonAncestor) { // Special case when start and end are at parent Span boundaries. // Don't need to create a new Span in this case. span = (Span)commonAncestor; } else { // Create a new span from start to end. span = new Span(); span.Reposition(start, end); } // Set property on the span. span.SetValue(formattingProperty, value); // Clear property value from all ancestors of this span. FixupStructuralPropertyEnvironment(span, formattingProperty); // Clear property value from all Span and Run children of this span. ClearPropertyValueFromSpansAndRuns(span.ContentStart, span.ContentEnd, formattingProperty); } }
// Validates that the sibling element at this position belong to expected itemType (Inline, Block, ListItem) private static void ValidateMergingPositions(Type itemType, TextPointer start, TextPointer end) { if (start.CompareTo(end) < 0) { // Verify inner part TextPointerContext forwardFromStart = start.GetPointerContext(LogicalDirection.Forward); TextPointerContext backwardFromEnd = end.GetPointerContext(LogicalDirection.Backward); Invariant.Assert(forwardFromStart == TextPointerContext.ElementStart, "Expecting first opening tag of pasted fragment"); Invariant.Assert(backwardFromEnd == TextPointerContext.ElementEnd, "Expecting last closing tag of pasted fragment"); Invariant.Assert(itemType.IsAssignableFrom(start.GetAdjacentElement(LogicalDirection.Forward).GetType()), "The first pasted fragment item is expected to be a " + itemType.Name); Invariant.Assert(itemType.IsAssignableFrom(end.GetAdjacentElement(LogicalDirection.Backward).GetType()), "The last pasted fragment item is expected to be a " + itemType.Name); // Veryfy outer part TextPointerContext backwardFromStart = start.GetPointerContext(LogicalDirection.Backward); TextPointerContext forwardFromEnd = end.GetPointerContext(LogicalDirection.Forward); Invariant.Assert(backwardFromStart == TextPointerContext.ElementStart || backwardFromStart == TextPointerContext.ElementEnd || backwardFromStart == TextPointerContext.None, "Bad context preceding a pasted fragment"); Invariant.Assert(!(backwardFromStart == TextPointerContext.ElementEnd) || itemType.IsAssignableFrom(start.GetAdjacentElement(LogicalDirection.Backward).GetType()), "An element preceding a pasted fragment is expected to be a " + itemType.Name); Invariant.Assert(forwardFromEnd == TextPointerContext.ElementStart || forwardFromEnd == TextPointerContext.ElementEnd || forwardFromEnd == TextPointerContext.None, "Bad context following a pasted fragment"); Invariant.Assert(!(forwardFromEnd == TextPointerContext.ElementStart) || itemType.IsAssignableFrom(end.GetAdjacentElement(LogicalDirection.Forward).GetType()), "An element following a pasted fragment is expected to be a " + itemType.Name); } }
private static IEnumerable<Paragraph> GetParagraphsBetweenPositions(TextPointer start, TextPointer end) { var changedParas = new HashSet<Paragraph>(); var para = start.Paragraph ?? start.GetAdjacentElement(LogicalDirection.Forward) as Paragraph; while (para != null && para.ContentStart.CompareTo(end) < 0) { changedParas.Add(para); para = para.NextBlock as Paragraph; } return changedParas; }