// Token: 0x060039C1 RID: 14785 RVA: 0x001064C0 File Offset: 0x001046C0 private static bool IsAdjacentToFormatElement(ITextPointer pointer, LogicalDirection direction) { bool result = false; if (direction == LogicalDirection.Forward) { TextPointerContext pointerContext = pointer.GetPointerContext(LogicalDirection.Forward); if (pointerContext == TextPointerContext.ElementStart && TextSchema.IsFormattingType(pointer.GetElementType(LogicalDirection.Forward))) { result = true; } else if (pointerContext == TextPointerContext.ElementEnd && TextSchema.IsFormattingType(pointer.ParentType)) { result = true; } } else { TextPointerContext pointerContext = pointer.GetPointerContext(LogicalDirection.Backward); if (pointerContext == TextPointerContext.ElementEnd && TextSchema.IsFormattingType(pointer.GetElementType(LogicalDirection.Backward))) { result = true; } else if (pointerContext == TextPointerContext.ElementStart && TextSchema.IsFormattingType(pointer.ParentType)) { result = true; } } return(result); }
// Token: 0x06007245 RID: 29253 RVA: 0x0020AA54 File Offset: 0x00208C54 internal static List <AutomationPeer> GetAutomationPeersFromRange(ITextPointer start, ITextPointer end, ITextPointer ownerContentStart) { List <AutomationPeer> list = new List <AutomationPeer>(); start = start.CreatePointer(); while (start.CompareTo(end) < 0) { bool flag = false; if (start.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart) { object adjacentElement = start.GetAdjacentElement(LogicalDirection.Forward); if (adjacentElement is ContentElement) { AutomationPeer automationPeer = ContentElementAutomationPeer.CreatePeerForElement((ContentElement)adjacentElement); if (automationPeer != null) { if (ownerContentStart == null || TextContainerHelper.IsImmediateAutomationChild(start, ownerContentStart)) { list.Add(automationPeer); } start.MoveToNextContextPosition(LogicalDirection.Forward); start.MoveToElementEdge(ElementEdge.AfterEnd); flag = true; } } } else if (start.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.EmbeddedElement) { object adjacentElement = start.GetAdjacentElement(LogicalDirection.Forward); if (adjacentElement is UIElement) { if (ownerContentStart == null || TextContainerHelper.IsImmediateAutomationChild(start, ownerContentStart)) { AutomationPeer automationPeer = UIElementAutomationPeer.CreatePeerForElement((UIElement)adjacentElement); if (automationPeer != null) { list.Add(automationPeer); } else { TextContainerHelper.iterate((Visual)adjacentElement, list); } } } else if (adjacentElement is ContentElement) { AutomationPeer automationPeer = ContentElementAutomationPeer.CreatePeerForElement((ContentElement)adjacentElement); if (automationPeer != null && (ownerContentStart == null || TextContainerHelper.IsImmediateAutomationChild(start, ownerContentStart))) { list.Add(automationPeer); } } } if (!flag && !start.MoveToNextContextPosition(LogicalDirection.Forward)) { break; } } return(list); }
// Token: 0x060038D8 RID: 14552 RVA: 0x00100E4C File Offset: 0x000FF04C private static bool IsErrorAtNonMergeableInlineEdge(SpellingError spellingError, out ITextPointer textStart, out ITextPointer textEnd) { bool result = false; textStart = spellingError.Start.CreatePointer(LogicalDirection.Backward); while (textStart.CompareTo(spellingError.End) < 0 && textStart.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text) { textStart.MoveToNextContextPosition(LogicalDirection.Forward); } textEnd = spellingError.End.CreatePointer(); while (textEnd.CompareTo(spellingError.Start) > 0 && textEnd.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.Text) { textEnd.MoveToNextContextPosition(LogicalDirection.Backward); } if (textStart.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text || textStart.CompareTo(spellingError.End) == 0) { return(false); } Invariant.Assert(textEnd.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.Text && textEnd.CompareTo(spellingError.Start) != 0); if ((TextPointerBase.IsAtNonMergeableInlineStart(textStart) || TextPointerBase.IsAtNonMergeableInlineEnd(textEnd)) && typeof(Run).IsAssignableFrom(textStart.ParentType) && textStart.HasEqualScope(textEnd)) { result = true; } return(result); }
// Token: 0x06002C19 RID: 11289 RVA: 0x000C8360 File Offset: 0x000C6560 private static DocumentSequenceTextPointer xGetClingDSTP(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { TextPointerContext pointerContext = thisTp.ChildPointer.GetPointerContext(direction); if (pointerContext != TextPointerContext.None) { return(thisTp); } ChildDocumentBlock childDocumentBlock = thisTp.ChildBlock; ITextPointer textPointer = thisTp.ChildPointer; if (direction == LogicalDirection.Forward) { while (pointerContext == TextPointerContext.None) { if (childDocumentBlock.IsTail) { break; } childDocumentBlock = childDocumentBlock.NextBlock; textPointer = childDocumentBlock.ChildContainer.Start; pointerContext = textPointer.GetPointerContext(direction); } } else { while (pointerContext == TextPointerContext.None && !childDocumentBlock.IsHead) { childDocumentBlock = childDocumentBlock.PreviousBlock; textPointer = childDocumentBlock.ChildContainer.End; pointerContext = textPointer.GetPointerContext(direction); } } return(new DocumentSequenceTextPointer(childDocumentBlock, textPointer)); }
// Token: 0x06007CED RID: 31981 RVA: 0x0023255C File Offset: 0x0023075C private static FlowDirection GetTextFlowDirection(ITextPointer pointer) { Invariant.Assert(pointer != null, "Null pointer passed."); Invariant.Assert(pointer.IsAtInsertionPosition, "Pointer is not an insertion position"); int num = 0; LogicalDirection logicalDirection = pointer.LogicalDirection; TextPointerContext pointerContext = pointer.GetPointerContext(logicalDirection); FlowDirection result; if ((pointerContext == TextPointerContext.ElementEnd || pointerContext == TextPointerContext.ElementStart) && !TextSchema.IsFormattingType(pointer.ParentType)) { result = (FlowDirection)pointer.GetValue(FrameworkElement.FlowDirectionProperty); } else { Rect anchorRectangle = TextSelectionHelper.GetAnchorRectangle(pointer); ITextPointer textPointer = pointer.GetNextInsertionPosition(logicalDirection); if (textPointer != null) { textPointer = textPointer.CreatePointer((logicalDirection == LogicalDirection.Backward) ? LogicalDirection.Forward : LogicalDirection.Backward); if (logicalDirection == LogicalDirection.Forward) { if (pointerContext == TextPointerContext.ElementEnd && textPointer.GetPointerContext(textPointer.LogicalDirection) == TextPointerContext.ElementStart) { return((FlowDirection)pointer.GetValue(FrameworkElement.FlowDirectionProperty)); } } else if (pointerContext == TextPointerContext.ElementStart && textPointer.GetPointerContext(textPointer.LogicalDirection) == TextPointerContext.ElementEnd) { return((FlowDirection)pointer.GetValue(FrameworkElement.FlowDirectionProperty)); } Rect anchorRectangle2 = TextSelectionHelper.GetAnchorRectangle(textPointer); if (anchorRectangle2 != Rect.Empty && anchorRectangle != Rect.Empty) { num = Math.Sign(anchorRectangle2.Left - anchorRectangle.Left); if (logicalDirection == LogicalDirection.Backward) { num = -num; } } } if (num == 0) { result = (FlowDirection)pointer.GetValue(FrameworkElement.FlowDirectionProperty); } else { result = ((num > 0) ? FlowDirection.LeftToRight : FlowDirection.RightToLeft); } } return(result); }
// Token: 0x06006B2D RID: 27437 RVA: 0x001EF394 File Offset: 0x001ED594 private List <TextElement> GetAttachedObjectElements(int dcpFirst, int dcpLast) { List <TextElement> list = new List <TextElement>(); ITextPointer contentStart = TextContainerHelper.GetContentStart(base.StructuralCache.TextContainer, base.Element); ITextPointer textPointerFromCP = TextContainerHelper.GetTextPointerFromCP(base.StructuralCache.TextContainer, base.ParagraphStartCharacterPosition + dcpFirst, LogicalDirection.Forward); if (dcpLast > base.Cch) { dcpLast = base.Cch; } while (contentStart.GetOffsetToPosition(textPointerFromCP) < dcpLast) { if (textPointerFromCP.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart) { TextElement adjacentElementFromOuterPosition = ((TextPointer)textPointerFromCP).GetAdjacentElementFromOuterPosition(LogicalDirection.Forward); if (adjacentElementFromOuterPosition is Figure || adjacentElementFromOuterPosition is Floater) { list.Add(adjacentElementFromOuterPosition); textPointerFromCP.MoveByOffset(adjacentElementFromOuterPosition.SymbolCount); } else { textPointerFromCP.MoveToNextContextPosition(LogicalDirection.Forward); } } else { textPointerFromCP.MoveToNextContextPosition(LogicalDirection.Forward); } } return(list); }
// Token: 0x06003889 RID: 14473 RVA: 0x000FDB54 File Offset: 0x000FBD54 private static UIElement GetUIElementWhenMouseOver(TextEditor This, Point mouseMovePoint) { ITextPointer textPositionFromPoint = This.TextView.GetTextPositionFromPoint(mouseMovePoint, false); if (textPositionFromPoint == null) { return(null); } if (textPositionFromPoint.GetPointerContext(textPositionFromPoint.LogicalDirection) != TextPointerContext.EmbeddedElement) { return(null); } ITextPointer textPointer = textPositionFromPoint.GetNextContextPosition(textPositionFromPoint.LogicalDirection); LogicalDirection gravity = (textPositionFromPoint.LogicalDirection == LogicalDirection.Forward) ? LogicalDirection.Backward : LogicalDirection.Forward; textPointer = textPointer.CreatePointer(0, gravity); Rect rectangleFromTextPosition = This.TextView.GetRectangleFromTextPosition(textPositionFromPoint); Rect rectangleFromTextPosition2 = This.TextView.GetRectangleFromTextPosition(textPointer); Rect rect = rectangleFromTextPosition; rect.Union(rectangleFromTextPosition2); if (!rect.Contains(mouseMovePoint)) { return(null); } return(textPositionFromPoint.GetAdjacentElement(textPositionFromPoint.LogicalDirection) as UIElement); }
// Token: 0x060038DB RID: 14555 RVA: 0x00100F94 File Offset: 0x000FF194 private static ITextPointer GetNextTextPosition(ITextPointer position, ITextPointer limit, LogicalDirection direction, out char character) { bool flag = false; character = '\0'; while (position != null && !flag && (limit == null || position.CompareTo(limit) < 0)) { switch (position.GetPointerContext(direction)) { case TextPointerContext.Text: { char[] array = new char[1]; position.GetTextInRun(direction, array, 0, 1); character = array[0]; flag = true; continue; } case TextPointerContext.ElementStart: case TextPointerContext.ElementEnd: if (TextSchema.IsFormattingType(position.GetElementType(direction))) { position = position.CreatePointer(1); continue; } position = null; continue; } position = null; } return(position); }
// Token: 0x060068B0 RID: 26800 RVA: 0x001D8BE4 File Offset: 0x001D6DE4 protected override BaseParagraph GetParagraph(ITextPointer textPointer, bool fEmptyOk) { Invariant.Assert(textPointer is TextPointer); BaseParagraph baseParagraph = null; while (baseParagraph == null) { TextPointerContext pointerContext = textPointer.GetPointerContext(LogicalDirection.Forward); if (pointerContext == TextPointerContext.ElementStart) { TextElement adjacentElementFromOuterPosition = ((TextPointer)textPointer).GetAdjacentElementFromOuterPosition(LogicalDirection.Forward); if (adjacentElementFromOuterPosition is ListItem) { baseParagraph = new ListItemParagraph(adjacentElementFromOuterPosition, base.StructuralCache); break; } if (adjacentElementFromOuterPosition is List) { baseParagraph = new ListParagraph(adjacentElementFromOuterPosition, base.StructuralCache); break; } if (((TextPointer)textPointer).IsFrozen) { textPointer = textPointer.CreatePointer(); } textPointer.MoveToPosition(adjacentElementFromOuterPosition.ElementEnd); } else if (pointerContext == TextPointerContext.ElementEnd) { if (base.Element == ((TextPointer)textPointer).Parent) { break; } if (((TextPointer)textPointer).IsFrozen) { textPointer = textPointer.CreatePointer(); } textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } else { if (((TextPointer)textPointer).IsFrozen) { textPointer = textPointer.CreatePointer(); } textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } } if (baseParagraph != null) { base.StructuralCache.CurrentFormatContext.DependentMax = (TextPointer)textPointer; } return(baseParagraph); }
//------------------------------------------------------ // // Internal Property // //------------------------------------------------------ //------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods private static DocumentSequenceTextPointer xGetClingDSTP(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { TextPointerContext context = thisTp.ChildPointer.GetPointerContext(direction); if (context != TextPointerContext.None) { return(thisTp); } ChildDocumentBlock block = thisTp.ChildBlock; ITextPointer pointer = thisTp.ChildPointer; if (direction == LogicalDirection.Forward) { while (context == TextPointerContext.None && !block.IsTail) { // get next block block = block.NextBlock; // get start pointer = block.ChildContainer.Start; context = pointer.GetPointerContext(direction); } } else { Debug.Assert(direction == LogicalDirection.Backward); while (context == TextPointerContext.None && !block.IsHead) { // get next block block = block.PreviousBlock; // get start pointer = block.ChildContainer.End; context = pointer.GetPointerContext(direction); } } return(new DocumentSequenceTextPointer(block, pointer)); }
// Token: 0x06008461 RID: 33889 RVA: 0x00247F80 File Offset: 0x00246180 internal void AddCompositionLines() { this._compositionLines.Clear(); ITextPointer textPointer = this._textView.TextContainer.Start.CreatePointer(this._startOffset, LogicalDirection.Forward); ITextPointer textPointer2 = this._textView.TextContainer.Start.CreatePointer(this._endOffset, LogicalDirection.Backward); while (textPointer.CompareTo(textPointer2) < 0 && textPointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text) { textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } Invariant.Assert(textPointer.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text); if (textPointer2.HasValidLayout) { this._startRect = this._textView.GetRectangleFromTextPosition(textPointer); this._endRect = this._textView.GetRectangleFromTextPosition(textPointer2); if (this._startRect.Top != this._endRect.Top) { this.AddMultipleCompositionLines(textPointer, textPointer2); return; } Color lineColor = this._textServicesDisplayAttribute.GetLineColor(textPointer); this._compositionLines.Add(new CompositionAdorner.CompositionLine(this._startRect, this._endRect, lineColor)); } }
//------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods // Add the composition lines for rendering the composition lines. internal void AddCompositionLines() { // Erase any current lines. _compositionLines.Clear(); ITextPointer start = _textView.TextContainer.Start.CreatePointer(_startOffset, LogicalDirection.Forward); ITextPointer end = _textView.TextContainer.Start.CreatePointer(_endOffset, LogicalDirection.Backward); while (start.CompareTo(end) < 0 && start.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text) { start.MoveToNextContextPosition(LogicalDirection.Forward); } Invariant.Assert(start.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text); if (end.HasValidLayout) { // Get the rectangle for start/end position _startRect = _textView.GetRectangleFromTextPosition(start); _endRect = _textView.GetRectangleFromTextPosition(end); // Check whether the composition line is single or multiple lines if (_startRect.Top != _endRect.Top) { // Add the composition lines to be rendered for the composition string AddMultipleCompositionLines(start, end); } else { // Set the start/end pointer to draw the line Color lineColor = _textServicesDisplayAttribute.GetLineColor(start); // Add the composition line to be rendered _compositionLines.Add(new CompositionLine(_startRect, _endRect, lineColor)); } } }
// Token: 0x06003282 RID: 12930 RVA: 0x000DD010 File Offset: 0x000DB210 internal static bool HasNoTextContent(Paragraph paragraph) { ITextPointer textPointer = paragraph.ContentStart.CreatePointer(); ITextPointer contentEnd = paragraph.ContentEnd; while (textPointer.CompareTo(contentEnd) < 0) { TextPointerContext pointerContext = textPointer.GetPointerContext(LogicalDirection.Forward); if (pointerContext == TextPointerContext.Text || pointerContext == TextPointerContext.EmbeddedElement || typeof(LineBreak).IsAssignableFrom(textPointer.ParentType) || typeof(AnchoredBlock).IsAssignableFrom(textPointer.ParentType)) { return(false); } textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } return(true); }
// Token: 0x06006B2C RID: 27436 RVA: 0x001EF318 File Offset: 0x001ED518 internal int GetLastDcpAttachedObjectBeforeLine(int dcpFirst) { ITextPointer textPointerFromCP = TextContainerHelper.GetTextPointerFromCP(base.StructuralCache.TextContainer, base.ParagraphStartCharacterPosition + dcpFirst, LogicalDirection.Forward); ITextPointer contentStart = TextContainerHelper.GetContentStart(base.StructuralCache.TextContainer, base.Element); while (textPointerFromCP.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart) { TextElement adjacentElementFromOuterPosition = ((TextPointer)textPointerFromCP).GetAdjacentElementFromOuterPosition(LogicalDirection.Forward); if (!(adjacentElementFromOuterPosition is Figure) && !(adjacentElementFromOuterPosition is Floater)) { break; } textPointerFromCP.MoveByOffset(adjacentElementFromOuterPosition.SymbolCount); } return(contentStart.GetOffsetToPosition(textPointerFromCP)); }
// Token: 0x06008E5B RID: 36443 RVA: 0x0025BE9C File Offset: 0x0025A09C private void GetContent() { this._contentSegments.Clear(); ITextPointer textPointer = this._segment.Start.CreatePointer(); ITextPointer textPointer2 = null; while (textPointer.CompareTo(this._segment.End) < 0) { TextPointerContext pointerContext = textPointer.GetPointerContext(LogicalDirection.Forward); if (pointerContext == TextPointerContext.ElementStart) { Type elementType = textPointer.GetElementType(LogicalDirection.Forward); if (typeof(Run).IsAssignableFrom(elementType) || typeof(BlockUIContainer).IsAssignableFrom(elementType)) { this.OpenSegment(ref textPointer2, textPointer); } else if (typeof(Table).IsAssignableFrom(elementType) || typeof(Floater).IsAssignableFrom(elementType) || typeof(Figure).IsAssignableFrom(elementType)) { this.CloseSegment(ref textPointer2, textPointer, this._segment.End); } textPointer.MoveToNextContextPosition(LogicalDirection.Forward); if (typeof(Run).IsAssignableFrom(elementType) || typeof(BlockUIContainer).IsAssignableFrom(elementType)) { textPointer.MoveToElementEdge(ElementEdge.AfterEnd); } } else if (pointerContext == TextPointerContext.ElementEnd) { Type parentType = textPointer.ParentType; if (typeof(TableCell).IsAssignableFrom(parentType) || typeof(Floater).IsAssignableFrom(parentType) || typeof(Figure).IsAssignableFrom(parentType)) { this.CloseSegment(ref textPointer2, textPointer, this._segment.End); } textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } else if (pointerContext == TextPointerContext.Text || pointerContext == TextPointerContext.EmbeddedElement) { this.OpenSegment(ref textPointer2, textPointer); textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } else { Invariant.Assert(false, "unexpected TextPointerContext"); } } this.CloseSegment(ref textPointer2, textPointer, this._segment.End); }
// Returns the position preceeding the next text character in a specified // direction, or null if no such position exists. // The scan will halt if limit is encounted; limit may be null. private static ITextPointer GetNextTextPosition(ITextPointer position, ITextPointer limit, LogicalDirection direction, out char character) { bool foundText = false; character = (char)0; while (position != null && !foundText && (limit == null || position.CompareTo(limit) < 0)) { switch (position.GetPointerContext(direction)) { case TextPointerContext.Text: char[] buffer = new char[1]; position.GetTextInRun(direction, buffer, 0, 1); character = buffer[0]; foundText = true; break; case TextPointerContext.ElementStart: case TextPointerContext.ElementEnd: if (TextSchema.IsFormattingType(position.GetElementType(direction))) { position = position.CreatePointer(+1); } else { position = null; } break; case TextPointerContext.EmbeddedElement: case TextPointerContext.None: default: position = null; break; } } return(position); }
// Return a UIElement when mouseMovePoint is within the ui element's bounding Rect. Null otherwise. private static UIElement GetUIElementWhenMouseOver(TextEditor This, Point mouseMovePoint) { ITextPointer mouseMovePosition = This.TextView.GetTextPositionFromPoint(mouseMovePoint, /*snapToText:*/ false); if (mouseMovePosition == null) { return(null); } if (!(mouseMovePosition.GetPointerContext(mouseMovePosition.LogicalDirection) == TextPointerContext.EmbeddedElement)) { return(null); } // Find out if mouseMovePoint is within the bounding Rect of UIElement, we need to do this check explicitly // because even when snapToText is false, textview returns a first/last position on a line when point is in // an area before/after line start/end. This is by-design behavior for textview. // Need to get Rect from TextView, since Rect returned by TextPointer.GetCharacterRect() // is transformed to UiScope coordinates and we want RenderScope coordinates here. ITextPointer otherEdgePosition = mouseMovePosition.GetNextContextPosition(mouseMovePosition.LogicalDirection); LogicalDirection otherEdgeDirection = (mouseMovePosition.LogicalDirection == LogicalDirection.Forward) ? LogicalDirection.Backward : LogicalDirection.Forward; // Normalize with correct gravity otherEdgePosition = otherEdgePosition.CreatePointer(0, otherEdgeDirection); Rect uiElementFirstEdgeRect = This.TextView.GetRectangleFromTextPosition(mouseMovePosition); Rect uiElementSecondEdgeRect = This.TextView.GetRectangleFromTextPosition(otherEdgePosition); Rect boundingRect = uiElementFirstEdgeRect; boundingRect.Union(uiElementSecondEdgeRect); if (!boundingRect.Contains(mouseMovePoint)) { return(null); } return(mouseMovePosition.GetAdjacentElement(mouseMovePosition.LogicalDirection) as UIElement); }
// Token: 0x06008465 RID: 33893 RVA: 0x002480A0 File Offset: 0x002462A0 private void AddMultipleCompositionLines(ITextPointer start, ITextPointer end) { ITextPointer textPointer = start; ITextPointer textPointer2 = textPointer; while (textPointer2.CompareTo(end) < 0) { TextSegment lineRange = this._textView.GetLineRange(textPointer2); if (lineRange.IsNull) { textPointer = textPointer2; } else { if (textPointer.CompareTo(lineRange.Start) < 0) { textPointer = lineRange.Start; } if (textPointer2.CompareTo(lineRange.End) < 0) { if (end.CompareTo(lineRange.End) < 0) { textPointer2 = end.CreatePointer(); } else { textPointer2 = lineRange.End.CreatePointer(LogicalDirection.Backward); } } Rect rectangleFromTextPosition = this._textView.GetRectangleFromTextPosition(textPointer); Rect rectangleFromTextPosition2 = this._textView.GetRectangleFromTextPosition(textPointer2); this._compositionLines.Add(new CompositionAdorner.CompositionLine(rectangleFromTextPosition, rectangleFromTextPosition2, this._textServicesDisplayAttribute.GetLineColor(textPointer))); textPointer = lineRange.End.CreatePointer(LogicalDirection.Forward); } while (textPointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None && textPointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text) { textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } textPointer2 = textPointer; } }
// Token: 0x06006600 RID: 26112 RVA: 0x001CAB08 File Offset: 0x001C8D08 public override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp) { int num = 0; CharacterBufferRange empty = CharacterBufferRange.Empty; CultureInfo culture = null; if (dcp > 0) { ITextPointer textPointer = this._owner.TextContainer.CreatePointerAtOffset(dcp, LogicalDirection.Backward); while (textPointer.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.Text && textPointer.CompareTo(this._owner.TextContainer.Start) != 0) { textPointer.MoveByOffset(-1); num++; } string textInRun = textPointer.GetTextInRun(LogicalDirection.Backward); empty = new CharacterBufferRange(textInRun, 0, textInRun.Length); StaticTextPointer staticTextPointer = textPointer.CreateStaticPointer(); DependencyObject element = (staticTextPointer.Parent != null) ? staticTextPointer.Parent : this._owner; culture = DynamicPropertyReader.GetCultureInfo(element); } return(new TextSpan <CultureSpecificCharacterBufferRange>(num + empty.Length, new CultureSpecificCharacterBufferRange(culture, empty))); }
/// <summary> /// Get text immediately before specified text source position. Return CharacterBufferRange /// containing this text. /// </summary> /// <param name="dcp"> /// dcp of position relative to start of line /// </param> internal override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp) { // Parameter validation Invariant.Assert(dcp >= 0); int nonTextLength = 0; CharacterBufferRange precedingText = CharacterBufferRange.Empty; CultureInfo culture = null; if (dcp > 0) { // Create TextPointer at dcp, and pointer at paragraph start to compare ITextPointer startPosition = TextContainerHelper.GetTextPointerFromCP(_paraClient.Paragraph.StructuralCache.TextContainer, _cpPara, LogicalDirection.Forward); ITextPointer position = TextContainerHelper.GetTextPointerFromCP(_paraClient.Paragraph.StructuralCache.TextContainer, _cpPara + dcp, LogicalDirection.Forward); // Move backward until we find a position at the end of a text run, or reach start of TextContainer while (position.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.Text && position.CompareTo(startPosition) != 0) { position.MoveByOffset(-1); nonTextLength++; } // Return text in run. If it is at start of TextContainer this will return an empty string string precedingTextString = position.GetTextInRun(LogicalDirection.Backward); precedingText = new CharacterBufferRange(precedingTextString, 0, precedingTextString.Length); StaticTextPointer pointer = position.CreateStaticPointer(); DependencyObject element = (pointer.Parent != null) ? pointer.Parent : _paraClient.Paragraph.Element; culture = DynamicPropertyReader.GetCultureInfo(element); } return(new TextSpan <CultureSpecificCharacterBufferRange>( nonTextLength + precedingText.Length, new CultureSpecificCharacterBufferRange(culture, precedingText) )); }
// Token: 0x06006864 RID: 26724 RVA: 0x001D6C0C File Offset: 0x001D4E0C internal override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp) { Invariant.Assert(dcp >= 0); int num = 0; CharacterBufferRange empty = CharacterBufferRange.Empty; CultureInfo culture = null; if (dcp > 0) { ITextPointer textPointerFromCP = TextContainerHelper.GetTextPointerFromCP(this._paraClient.Paragraph.StructuralCache.TextContainer, this._cpPara, LogicalDirection.Forward); ITextPointer textPointerFromCP2 = TextContainerHelper.GetTextPointerFromCP(this._paraClient.Paragraph.StructuralCache.TextContainer, this._cpPara + dcp, LogicalDirection.Forward); while (textPointerFromCP2.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.Text && textPointerFromCP2.CompareTo(textPointerFromCP) != 0) { textPointerFromCP2.MoveByOffset(-1); num++; } string textInRun = textPointerFromCP2.GetTextInRun(LogicalDirection.Backward); empty = new CharacterBufferRange(textInRun, 0, textInRun.Length); StaticTextPointer staticTextPointer = textPointerFromCP2.CreateStaticPointer(); DependencyObject element = (staticTextPointer.Parent != null) ? staticTextPointer.Parent : this._paraClient.Paragraph.Element; culture = DynamicPropertyReader.GetCultureInfo(element); } return(new TextSpan <CultureSpecificCharacterBufferRange>(num + empty.Length, new CultureSpecificCharacterBufferRange(culture, empty))); }
// ------------------------------------------------------------------ // Get text immediately before specified text source position. // ------------------------------------------------------------------ public override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp) { // Parameter validation Debug.Assert(dcp >= 0); int nonTextLength = 0; CharacterBufferRange precedingText = CharacterBufferRange.Empty; CultureInfo culture = null; if (dcp > 0) { // Create TextPointer at dcp ITextPointer position = _owner.TextContainer.CreatePointerAtOffset(dcp, LogicalDirection.Backward); // Move backward until we find a position at the end of a text run, or reach start of TextContainer while (position.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.Text && position.CompareTo(_owner.TextContainer.Start) != 0) { position.MoveByOffset(-1); nonTextLength++; } // Return text in run. If it is at start of TextContainer this will return an empty string string precedingTextString = position.GetTextInRun(LogicalDirection.Backward); precedingText = new CharacterBufferRange(precedingTextString, 0, precedingTextString.Length); StaticTextPointer pointer = position.CreateStaticPointer(); DependencyObject element = (pointer.Parent != null) ? pointer.Parent : _owner; culture = DynamicPropertyReader.GetCultureInfo(element); } return(new TextSpan <CultureSpecificCharacterBufferRange>( nonTextLength + precedingText.Length, new CultureSpecificCharacterBufferRange(culture, precedingText) )); }
internal static bool IsAtNormalizedPosition(ITextPointer position, LogicalDirection direction, bool respectCaretUnitBoundaries) { if (!IsAtNormalizedPosition(position, respectCaretUnitBoundaries)) { return false; } // if (position.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart && position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd) { return true; } // Check if there is no any formatting tags in the given direction if (TextSchema.IsFormattingType(position.GetElementType(direction))) { position = position.CreatePointer(); while (TextSchema.IsFormattingType(position.GetElementType(direction))) { position.MoveToNextContextPosition(direction); } if (IsAtNormalizedPosition(position, respectCaretUnitBoundaries)) { // So there is a possibility to move over formatting tags only // and reach some insertion position. This means // that our position was not normalized in the given direction. return false; } } return true; }
/// <summary> /// </summary> void ITextSelection.SetCaretToPosition(ITextPointer caretPosition, LogicalDirection direction, bool allowStopAtLineEnd, bool allowStopNearSpace) { // We need a pointer with appropriate direction, // becasue it will be used in textRangeBase.Select method for // pointer normalization. caretPosition = caretPosition.CreatePointer(direction); // Normalize the position in its logical direction - to get to text content over there. caretPosition.MoveToInsertionPosition(direction); // We need a pointer with the reverse direction to confirm // the line wrapping position. So we can ensure Bidi caret navigation. // Bidi can have the different caret position by setting the // logical direction, so we have to only set the logical direction // as the forward for the real line wrapping position. ITextPointer reversePosition = caretPosition.CreatePointer(direction == LogicalDirection.Forward ? LogicalDirection.Backward : LogicalDirection.Forward); // Check line wrapping condition if (!allowStopAtLineEnd && ((TextPointerBase.IsAtLineWrappingPosition(caretPosition, this.TextView) && TextPointerBase.IsAtLineWrappingPosition(reversePosition, this.TextView)) || TextPointerBase.IsNextToPlainLineBreak(caretPosition, LogicalDirection.Backward) || TextSchema.IsBreak(caretPosition.GetElementType(LogicalDirection.Backward)))) { // Caret is at wrapping position, and we are not allowed to stay at end of line, // so we choose forward direction to appear in the begiinning of a second line caretPosition.SetLogicalDirection(LogicalDirection.Forward); } else { if (caretPosition.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.Text && caretPosition.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text) { // This is statistically most typical case. No "smartness" needed // to choose standard Forward orientation for the caret. // NOTE: By using caretPosition's direction we solve BiDi caret orientation case: // The orietnation reflects a direction from where caret has been moved // or orientation where mouse clicked. So we will stick with appropriate // character. // Nothing to do. The caretPosition is good to go. } else if (!allowStopNearSpace) { // There are some tags around, and we are not allowed to choose a side near to space. // So we need to perform some content analysis. char[] charBuffer = new char[1]; if (caretPosition.GetPointerContext(direction) == TextPointerContext.Text && caretPosition.GetTextInRun(direction, charBuffer, 0, 1) == 1 && Char.IsWhiteSpace(charBuffer[0])) { LogicalDirection oppositeDirection = direction == LogicalDirection.Forward ? LogicalDirection.Backward : LogicalDirection.Forward; // Check formatting switch condition at this position FlowDirection initialFlowDirection = (FlowDirection)caretPosition.GetValue(FrameworkElement.FlowDirectionProperty); bool moved = caretPosition.MoveToInsertionPosition(oppositeDirection); if (moved && initialFlowDirection == (FlowDirection)caretPosition.GetValue(FrameworkElement.FlowDirectionProperty) && (caretPosition.GetPointerContext(oppositeDirection) != TextPointerContext.Text || caretPosition.GetTextInRun(oppositeDirection, charBuffer, 0, 1) != 1 || !Char.IsWhiteSpace(charBuffer[0]))) { // In the opposite direction we have a non-space // character. So we choose that direction direction = oppositeDirection; caretPosition.SetLogicalDirection(direction); } } } } // Now that orientation of a caretPosition is identified, // build an empty selection at this position TextRangeBase.BeginChange(this); try { TextRangeBase.Select(this, caretPosition, caretPosition); // Note how Select method works for the case of empty range: // It creates a single instance TextPointer normalized and oriented // in a direction taken from caretPosition: ITextSelection thisSelection = this; Invariant.Assert(thisSelection.Start.LogicalDirection == caretPosition.LogicalDirection); // orientation must be as passed Invariant.Assert(this.IsEmpty); //Invariant.Assert((object)thisSelection.Start == (object)thisSelection.End); // it must be the same instance of TextPointer //Invariant.Assert(TextPointerBase.IsAtInsertionPosition(thisSelection.Start, caretPosition.LogicalDirection)); // normalization must be done in the same diredction as orientation // Clear active positions when selection is empty SetActivePositions(null, null); } finally { TextRangeBase.EndChange(this); } }
// Token: 0x06002EDF RID: 11999 RVA: 0x000D3640 File Offset: 0x000D1840 internal override Rect GetRawRectangleFromTextPosition(ITextPointer position, out Transform transform) { FixedTextPointer fixedTextPointer = this.Container.VerifyPosition(position); Rect result = new Rect(0.0, 0.0, 0.0, 10.0); transform = Transform.Identity; FixedPosition fixedPosition; if (fixedTextPointer.FlowPosition.IsBoundary) { if (!this._GetFirstFixedPosition(fixedTextPointer, out fixedPosition)) { return(result); } } else if (!this._GetFixedPosition(fixedTextPointer, out fixedPosition)) { if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.None) { return(result); } ITextPointer position2 = position.CreatePointer(1); FixedTextPointer ftp = this.Container.VerifyPosition(position2); if (!this._GetFixedPosition(ftp, out fixedPosition)) { return(result); } } if (fixedPosition.Page != this.PageIndex) { return(result); } DependencyObject element = this.FixedPage.GetElement(fixedPosition.Node); if (element is Glyphs) { Glyphs glyphs = (Glyphs)element; result = FixedTextView._GetGlyphRunDesignRect(glyphs, fixedPosition.Offset, fixedPosition.Offset); GeneralTransform transform2 = glyphs.TransformToAncestor(this.FixedPage); result = this._GetTransformedCaretRect(transform2, result.TopLeft, result.Height); } else if (element is Image) { Image image = (Image)element; GeneralTransform transform3 = image.TransformToAncestor(this.FixedPage); Point origin = new Point(0.0, 0.0); if (fixedPosition.Offset > 0) { origin.X += image.ActualWidth; } result = this._GetTransformedCaretRect(transform3, origin, image.ActualHeight); } else if (element is Path) { Path path = (Path)element; GeneralTransform transform4 = path.TransformToAncestor(this.FixedPage); Rect bounds = path.Data.Bounds; Point topLeft = bounds.TopLeft; if (fixedPosition.Offset > 0) { topLeft.X += bounds.Width; } result = this._GetTransformedCaretRect(transform4, topLeft, bounds.Height); } return(result); }
private void TextPositionsFromITfRange(UnsafeNativeMethods.ITfRange range, out ITextPointer start, out ITextPointer end) { UnsafeNativeMethods.ITfRangeACP rangeACP; int startIndex; int length; rangeACP = range as UnsafeNativeMethods.ITfRangeACP; rangeACP.GetExtent(out startIndex, out length); start = CreatePointerAtCharOffset(startIndex, LogicalDirection.Backward); end = CreatePointerAtCharOffset(startIndex + length, LogicalDirection.Forward); while (start.CompareTo(end) < 0 && start.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text) { start.MoveToNextContextPosition(LogicalDirection.Forward); } }
internal Rect GetRectangleFromTextPosition(ITextPointer position) { Rect rect = System.Windows.Rect.Empty; int cp = Paragraph.StructuralCache.TextContainer.Start.GetOffsetToPosition((TextPointer)position); int dcp = cp - Paragraph.ParagraphStartCharacterPosition; int originalDcp = dcp; if (position.LogicalDirection == LogicalDirection.Backward && dcp > 0) { --dcp; } // Query paragraph details PTS.FSTEXTDETAILS textDetails; PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); // There are 3 different types of text paragraphs: // (a) full with simple lines // (b) full with composite lines - when figures/floaters are present // (c) cached - when using ParaChache if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) { if (textDetails.u.full.cLines > 0) { int vrBaseline = 0; if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) { // (a) full with simple lines RectFromDcpSimpleLines(dcp, originalDcp, position.LogicalDirection, position.GetPointerContext(position.LogicalDirection), ref textDetails.u.full, ref rect, ref vrBaseline); } else { // (b) full with composite lines - when figures/floaters are present RectFromDcpCompositeLines(dcp, originalDcp, position.LogicalDirection, position.GetPointerContext(position.LogicalDirection), ref textDetails.u.full, ref rect, ref vrBaseline); } } } else { // (c) cached - when using ParaChache Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); Debug.Assert(false, "Should not get here. ParaCache is not currently used."); } // Mirror back to page flow direction if(ThisFlowDirection != PageFlowDirection) { PTS.FSRECT pageRect = _pageContext.PageRect; PTS.FSRECT rectTransform = new PTS.FSRECT(rect); PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectTransform, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectTransform)); rect = rectTransform.FromTextDpi(); } return rect; }
/// <summary> /// Advances this TextNavigator by a count number of characters. /// </summary> /// <param name="thisNavigator">ITextPointer to advance.</param> /// <param name="direction"> /// A direction in which to search a next characters. /// </param> /// <returns> /// True if the navigator is advanced, false if the end of document is /// encountered and the navigator is not repositioned. /// </returns> /// <remarks> /// A "character" in this context is a sequence of one or several text /// symbols: one or more Unicode code points may be a character, every /// embedded object is a character, a sequence of closing block tags /// followed by opening block tags may also be a unit. Formatting tags /// do not contribute in any unit. /// </remarks> internal static bool MoveToNextInsertionPosition(ITextPointer thisNavigator, LogicalDirection direction) { Invariant.Assert(!thisNavigator.IsFrozen, "Can't reposition a frozen pointer!"); bool moved = true; int increment = direction == LogicalDirection.Forward ? +1 : -1; ITextPointer initialPosition = thisNavigator.CreatePointer(); if (!IsAtInsertionPosition(thisNavigator)) { // If the TextPointer is not currently at an insertion position, // move the TextPointer to the next insertion position in // the indicated direction, just like the MoveToInsertionPosition method. if (!MoveToInsertionPosition(thisNavigator, direction)) { // No insertion position in all content. MoveToInsertionPosition() guarantees that navigator is moved back to initial position. moved = false; goto Exit; } if ((direction == LogicalDirection.Forward && initialPosition.CompareTo(thisNavigator) < 0) || (direction == LogicalDirection.Backward && thisNavigator.CompareTo(initialPosition) < 0)) { // We have found an insertion position in requested direction. goto Exit; } } // Start with skipping character formatting tags in this direction while (TextSchema.IsFormattingType(thisNavigator.GetElementType(direction))) { thisNavigator.MoveByOffset(increment); } do { if (thisNavigator.GetPointerContext(direction) != TextPointerContext.None) { thisNavigator.MoveByOffset(increment); } else { // No insertion position in this direction; Move back thisNavigator.MoveToPosition(initialPosition); moved = false; goto Exit; } } while (!IsAtInsertionPosition(thisNavigator)); // We must leave position normalized in backward direction if (direction == LogicalDirection.Backward) { // For this we must skip character formatting tags if we have any while (TextSchema.IsFormattingType(thisNavigator.GetElementType(direction))) { thisNavigator.MoveByOffset(increment); } // However if it is block start we should back off TextPointerContext context = thisNavigator.GetPointerContext(direction); if (context == TextPointerContext.ElementStart || context == TextPointerContext.None) { increment = -increment; while (TextSchema.IsFormattingType(thisNavigator.GetElementType(LogicalDirection.Forward)) && !IsAtInsertionPosition(thisNavigator)) { thisNavigator.MoveByOffset(increment); } } } Exit: if (moved) { if (direction == LogicalDirection.Forward) { Invariant.Assert(thisNavigator.CompareTo(initialPosition) > 0, "thisNavigator is expected to be moved from initialPosition - 1"); } else { Invariant.Assert(thisNavigator.CompareTo(initialPosition) < 0, "thisNavigator is expected to be moved from initialPosition - 2"); } } else { Invariant.Assert(thisNavigator.CompareTo(initialPosition) == 0, "thisNavigator must stay at initial position"); } return moved; }
// GetText handler for object runs. private static bool WalkObjectRun(ITextPointer navigator, ITextPointer limit, char[] text, int cchReq, ref int charsCopied, UnsafeNativeMethods.TS_RUNINFO[] runInfo, int cRunInfoReq, ref int cRunInfoRcv) { bool hitLimit; Invariant.Assert(navigator.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.EmbeddedElement); Invariant.Assert(limit == null || navigator.CompareTo(limit) <= 0); if (limit != null && navigator.CompareTo(limit) == 0) { return true; } hitLimit = false; navigator.MoveToNextContextPosition(LogicalDirection.Forward); if (cchReq >= 1) { text[charsCopied] = UnsafeNativeMethods.TS_CHAR_EMBEDDED; charsCopied++; } if (cRunInfoReq > 0) { // Be sure to merge this text run with the previous run, if they are both text runs. // (A good robustness fix would be to make cicero handle this, if we ever get the chance.) if (cRunInfoRcv > 0 && runInfo[cRunInfoRcv - 1].type == UnsafeNativeMethods.TsRunType.TS_RT_PLAIN) { runInfo[cRunInfoRcv - 1].count++; } else { runInfo[cRunInfoRcv].count = 1; runInfo[cRunInfoRcv].type = UnsafeNativeMethods.TsRunType.TS_RT_PLAIN; cRunInfoRcv++; } } return hitLimit; }
private static ITextPointer RestrictWithinBlock(ITextPointer position, ITextPointer limit, LogicalDirection direction) { Invariant.Assert(!(direction == LogicalDirection.Backward) || position.CompareTo(limit) >= 0, "for backward direction position must be >= than limit"); Invariant.Assert(!(direction == LogicalDirection.Forward) || position.CompareTo(limit) <= 0, "for forward direcion position must be <= than linit"); while (direction == LogicalDirection.Backward ? position.CompareTo(limit) > 0 : position.CompareTo(limit) < 0) { TextPointerContext context = position.GetPointerContext(direction); if (context == TextPointerContext.ElementStart || context == TextPointerContext.ElementEnd) { Type elementType = position.GetElementType(direction); if (!typeof(Inline).IsAssignableFrom(elementType)) { limit = position; break; } } else if (context == TextPointerContext.EmbeddedElement) { limit = position; break; } position = position.GetNextContextPosition(direction); } // Return normalized position - in the direction towards a center position. return limit.GetInsertionPosition(direction == LogicalDirection.Backward ? LogicalDirection.Forward : LogicalDirection.Backward); }
// Position at document end - after the last paragraph/list/table is // considered as valid insertion point position. // It has though a special behavior for caret positioning and text insertion internal static bool IsAfterLastParagraph(ITextPointer thisPosition) { return thisPosition.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.None && thisPosition.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementEnd && !typeof(Inline).IsAssignableFrom(thisPosition.GetElementType(LogicalDirection.Backward)); }
/// <summary> /// Return true if rangeEnd is not at the end of an element. /// /// textReader must already be at the start of the element. /// </summary> private static bool IsPartialNonTypographic(ITextPointer textReader, ITextPointer rangeEnd) { bool isPartial = false; Invariant.Assert(textReader.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart); ITextPointer elementNavigation = textReader.CreatePointer(); ITextPointer elementEnd = textReader.CreatePointer(); elementEnd.MoveToNextContextPosition(LogicalDirection.Forward); // Find the end position elementEnd.MoveToElementEdge(ElementEdge.AfterEnd); if (elementEnd.CompareTo(rangeEnd) > 0) { isPartial = true; } return isPartial; }
// ------------------------------------------------------------- // // Private Methods // // ------------------------------------------------------------- #region Private Methods // ............................................................. // // Serialization // // ............................................................. /// <summary> /// This function serializes text segment formed by rangeStart and rangeEnd to valid xml using xmlWriter. /// </summary> /// <SecurityNote> /// To mask the security exception from XamlWriter.Save in partial trust case, /// this function checks if the current call stack has the all clipboard permission. /// </SecurityNote> private static void WriteXamlTextSegment(XmlWriter xmlWriter, ITextPointer rangeStart, ITextPointer rangeEnd, XamlTypeMapper xamlTypeMapper, ref int elementLevel, WpfPayload wpfPayload, bool ignoreWriteHyperlinkEnd, List<int> ignoreList, bool preserveTextElements) { // Special case for pure text selection - we need a Run wrapper for it. if (elementLevel == EmptyDocumentDepth && typeof(Run).IsAssignableFrom(rangeStart.ParentType)) { elementLevel++; xmlWriter.WriteStartElement(typeof(Run).Name); } // Create text navigator for reading the range's content ITextPointer textReader = rangeStart.CreatePointer(); // Exclude last opening tag from serialization - we don't need to create extra element // is cases when we have whole paragraphs/cells selected. // NOTE: We do this slightly differently than in TextRangeEdit.AdjustRangeEnd, where we use normalization for adjusted position. // In this case normalized position does not work, because we need to keep information about crossed paragraph boundary. while (rangeEnd.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart) { rangeEnd = rangeEnd.GetNextContextPosition(LogicalDirection.Backward); } // Write the range internal contents while (textReader.CompareTo(rangeEnd) < 0) { TextPointerContext runType = textReader.GetPointerContext(LogicalDirection.Forward); switch (runType) { case TextPointerContext.ElementStart: TextElement nextElement = (TextElement)textReader.GetAdjacentElement(LogicalDirection.Forward); if (nextElement is Hyperlink) { // Don't write Hyperlink start element if Hyperlink is invalid // in case of having a UiElement except Image or stated the range end // position before the end position of the Hyperlink. if (IsHyperlinkInvalid(textReader, rangeEnd)) { ignoreWriteHyperlinkEnd = true; textReader.MoveToNextContextPosition(LogicalDirection.Forward); continue; } } else if (nextElement != null) { // TextElementEditingBehaviorAttribute att = (TextElementEditingBehaviorAttribute)Attribute.GetCustomAttribute(nextElement.GetType(), typeof(TextElementEditingBehaviorAttribute)); if (att != null && !att.IsTypographicOnly) { if (IsPartialNonTypographic(textReader, rangeEnd)) { // Add pointer to ignore list ITextPointer ptr = textReader.CreatePointer(); ptr.MoveToElementEdge(ElementEdge.BeforeEnd); ignoreList.Add(ptr.Offset); textReader.MoveToNextContextPosition(LogicalDirection.Forward); continue; } } } elementLevel++; textReader.MoveToNextContextPosition(LogicalDirection.Forward); WriteStartXamlElement(/*range:*/null, textReader, xmlWriter, xamlTypeMapper, /*reduceElement:*/wpfPayload == null, preserveTextElements); break; case TextPointerContext.ElementEnd: // Don't write Hyperlink end element if Hyperlink include the invalid // in case of having a UiElement except Image or stated the range end // before the end position of the Hyperlink or Hyperlink opening tag is // skipped from WriteOpeningTags by selecting of the partial of Hyperlink. if (ignoreWriteHyperlinkEnd && (textReader.GetAdjacentElement(LogicalDirection.Forward) is Hyperlink)) { // Reset the flag to keep walk up the next Hyperlink tag ignoreWriteHyperlinkEnd = false; textReader.MoveToNextContextPosition(LogicalDirection.Forward); continue; } // Check the ignore list ITextPointer endPointer = textReader.CreatePointer(); endPointer.MoveToElementEdge(ElementEdge.BeforeEnd); // if (ignoreList.Contains(endPointer.Offset)) { ignoreList.Remove(endPointer.Offset); textReader.MoveToNextContextPosition(LogicalDirection.Forward); continue; } elementLevel--; if (TextSchema.IsBreak(textReader.ParentType)) { // For LineBreak, etc. use empty element syntax xmlWriter.WriteEndElement(); } else { // // For all other textelements use explicit closing tag. xmlWriter.WriteFullEndElement(); } textReader.MoveToNextContextPosition(LogicalDirection.Forward); break; case TextPointerContext.Text: int textLength = textReader.GetTextRunLength(LogicalDirection.Forward); char[] text = new Char[textLength]; textLength = TextPointerBase.GetTextWithLimit(textReader, LogicalDirection.Forward, text, 0, textLength, rangeEnd); // XmlWriter will throw an ArgumentException if text contains // any invalid surrogates, so strip them out now. textLength = StripInvalidSurrogateChars(text, textLength); xmlWriter.WriteChars(text, 0, textLength); textReader.MoveToNextContextPosition(LogicalDirection.Forward); break; case TextPointerContext.EmbeddedElement: object embeddedObject = textReader.GetAdjacentElement(LogicalDirection.Forward); textReader.MoveToNextContextPosition(LogicalDirection.Forward); WriteEmbeddedObject(embeddedObject, xmlWriter, wpfPayload); break; default: Invariant.Assert(false, "unexpected value of runType"); textReader.MoveToNextContextPosition(LogicalDirection.Forward); break; } } }
// Token: 0x06002C1F RID: 11295 RVA: 0x000C8584 File Offset: 0x000C6784 private static bool xGapAwareScan(DocumentSequenceTextPointer thisTp, int distance) { ChildDocumentBlock childDocumentBlock = thisTp.ChildBlock; bool flag = true; ITextPointer textPointer = thisTp.ChildPointer; if (textPointer == null) { flag = false; textPointer = thisTp.ChildPointer.CreatePointer(); } LogicalDirection logicalDirection = (distance > 0) ? LogicalDirection.Forward : LogicalDirection.Backward; distance = Math.Abs(distance); while (distance > 0) { switch (textPointer.GetPointerContext(logicalDirection)) { case TextPointerContext.None: if ((childDocumentBlock.IsHead && logicalDirection == LogicalDirection.Backward) || (childDocumentBlock.IsTail && logicalDirection == LogicalDirection.Forward)) { return(false); } childDocumentBlock = ((logicalDirection == LogicalDirection.Forward) ? childDocumentBlock.NextBlock : childDocumentBlock.PreviousBlock); textPointer = ((logicalDirection == LogicalDirection.Forward) ? childDocumentBlock.ChildContainer.Start.CreatePointer(textPointer.LogicalDirection) : childDocumentBlock.ChildContainer.End.CreatePointer(textPointer.LogicalDirection)); break; case TextPointerContext.Text: { int textRunLength = textPointer.GetTextRunLength(logicalDirection); int num = (textRunLength < distance) ? textRunLength : distance; distance -= num; if (logicalDirection == LogicalDirection.Backward) { num *= -1; } textPointer.MoveByOffset(num); break; } case TextPointerContext.EmbeddedElement: textPointer.MoveToNextContextPosition(logicalDirection); distance--; break; case TextPointerContext.ElementStart: textPointer.MoveToNextContextPosition(logicalDirection); distance--; break; case TextPointerContext.ElementEnd: textPointer.MoveToNextContextPosition(logicalDirection); distance--; break; } } thisTp.ChildBlock = childDocumentBlock; if (flag) { thisTp.ChildPointer = textPointer; } else { thisTp.ChildPointer = textPointer.CreatePointer(); } return(true); }
// Returns true if pointer preceeds an Inline start or end edge. private bool IsAdjacentToFormatElement(ITextPointer pointer) { TextPointerContext context; bool isAdjacentToFormatElement; isAdjacentToFormatElement = false; context = pointer.GetPointerContext(LogicalDirection.Forward); if (context == TextPointerContext.ElementStart && TextSchema.IsFormattingType(pointer.GetElementType(LogicalDirection.Forward))) { isAdjacentToFormatElement = true; } else if (context == TextPointerContext.ElementEnd && TextSchema.IsFormattingType(pointer.ParentType)) { isAdjacentToFormatElement = true; } return isAdjacentToFormatElement; }
private void ExpandToWordBreakAndContext(ITextPointer position, LogicalDirection direction, XmlLanguage language, out ITextPointer contentPosition, out ITextPointer contextPosition) { ITextPointer start; ITextPointer end; ITextPointer outwardPosition; ITextPointer inwardPosition; TextMap textMap; ArrayList segments; SpellerInterop.STextRange sTextRange; LogicalDirection inwardDirection; int i; contentPosition = position; contextPosition = position; if (position.GetPointerContext(direction) == TextPointerContext.None) { // There is no following context, we're at document start/end. return; } // Disable spell checking functionality since we're only // interested in word breaks here. This greatly cuts down // the engine's workload. _spellerInterop.SetContextOption("IsSpellChecking", false); // // Build an array of wordbreak offsets surrounding the position. // // 1. Search outward, into surrounding text. We need MinWordBreaksForContext // word breaks to handle multi-word errors. outwardPosition = SearchForWordBreaks(position, direction, language, MinWordBreaksForContext, true /* stopOnError */); // 2. Search inward, towards content. We just need one word break inward. inwardDirection = direction == LogicalDirection.Forward ? LogicalDirection.Backward : LogicalDirection.Forward; inwardPosition = SearchForWordBreaks(position, inwardDirection, language, 1, false /* stopOnError */); // Get combined word breaks. This may not be the same as we calculated // in two parts above, since we don't know yet whether or not position is // on a word break. if (direction == LogicalDirection.Backward) { start = outwardPosition; end = inwardPosition; } else { start = inwardPosition; end = outwardPosition; } textMap = new TextMap(start, end, position, position); segments = new ArrayList(MinWordBreaksForContext + 1); _spellerInterop.EnumTextSegments(textMap.Text, textMap.TextLength, null, new SpellerInterop.EnumTextSegmentsCallback(ExpandToWordBreakCallback), segments); // // Use our table of word breaks to calculate context and content positions. // if (segments.Count == 0) { // No segments. This can happen if position is surrounded by // nothing but white space. We've already initialized contentPosition // and contextPosition so there's nothing to do. } else { int leftWordBreak; int rightWordBreak; int contentOffset; int contextOffset; // Figure out where position lives in the segment list. i = FindPositionInSegmentList(textMap, direction, segments, out leftWordBreak, out rightWordBreak); // contentPosition should be an edge on the segment we found. if (direction == LogicalDirection.Backward) { contentOffset = textMap.ContentStartOffset == rightWordBreak ? rightWordBreak : leftWordBreak; } else { contentOffset = textMap.ContentStartOffset == leftWordBreak ? leftWordBreak : rightWordBreak; } contentPosition = textMap.MapOffsetToPosition(contentOffset); // contextPosition should be MinWordBreaksForContext - 1 words away. if (direction == LogicalDirection.Backward) { i -= (MinWordBreaksForContext - 1); sTextRange = (SpellerInterop.STextRange)segments[Math.Max(i, 0)]; // We might actually follow contentOffset if we're at the document edge. // Don't let that happen. contextOffset = Math.Min(sTextRange.Start, contentOffset); } else { i += MinWordBreaksForContext; sTextRange = (SpellerInterop.STextRange)segments[Math.Min(i, segments.Count-1)]; // We might actually preceed contentOffset if we're at the document edge. // Don't let that happen. contextOffset = Math.Max(sTextRange.Start + sTextRange.Length, contentOffset); } contextPosition = textMap.MapOffsetToPosition(contextOffset); } // Final fixup: if the dirty range covers only formatting (which is not passed // to the speller engine) then we might actually "expand" in the wrong // direction, since the TextMap will jump over formatting. // Backup if necessary. if (direction == LogicalDirection.Backward) { if (position.CompareTo(contentPosition) < 0) { contentPosition = position; } if (position.CompareTo(contextPosition) < 0) { contextPosition = position; } } else { if (position.CompareTo(contentPosition) > 0) { contentPosition = position; } if (position.CompareTo(contextPosition) > 0) { contextPosition = position; } } }
// Part of plain text converter: called from GetTextInternal when processing ElementEnd for Paragraph elements. // Outputs \n - for regular paragraphs and TableRow ends or \t for TableCell ends. private static void PlainConvertParagraphEnd(StringBuilder textBuffer, ITextPointer navigator) { // Check for a special case for a single paragraph within a TableCell // which must be serialized as "\t" character. navigator.MoveToElementEdge(ElementEdge.BeforeStart); bool theParagraphIsTheFirstInCollection = navigator.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart; navigator.MoveToNextContextPosition(LogicalDirection.Forward); navigator.MoveToElementEdge(ElementEdge.AfterEnd); TextPointerContext symbolType = navigator.GetPointerContext(LogicalDirection.Forward); if (theParagraphIsTheFirstInCollection && symbolType == TextPointerContext.ElementEnd && typeof(TableCell).IsAssignableFrom(navigator.ParentType)) { // This is an end of a table cell navigator.MoveToNextContextPosition(LogicalDirection.Forward); symbolType = navigator.GetPointerContext(LogicalDirection.Forward); if (symbolType == TextPointerContext.ElementStart) { // Next table cell starts after this one. Use '\t' as a cell separator textBuffer.Append('\t'); } else { // This was the last cell in a row. Use '\r\n' as a line separator textBuffer.Append(Environment.NewLine); } } else { // Ordinary paragraph end textBuffer.Append(Environment.NewLine); } }
/// <summary> /// Retrieves the range of a child object. /// </summary> /// <param name="childElementProvider">The child element. A provider should check that the /// passed element is a child of the text container, and should throw an /// InvalidOperationException if it is not.</param> /// <returns>A range that spans the child element.</returns> ITextRangeProvider ITextProvider.RangeFromChild(IRawElementProviderSimple childElementProvider) { if (childElementProvider == null) { throw new ArgumentNullException("childElementProvider"); } // Retrieve DependencyObject from AutomationElement DependencyObject childElement; if (_textPeer is TextAutomationPeer) { childElement = ((TextAutomationPeer)_textPeer).ElementFromProvider(childElementProvider); } else { childElement = ((ContentTextAutomationPeer)_textPeer).ElementFromProvider(childElementProvider); } TextRangeAdaptor range = null; if (childElement != null) { ITextPointer rangeStart = null; ITextPointer rangeEnd = null; // Retrieve start and end positions for given element. // If element is TextElement, retrieve its Element Start and End positions. // If element is UIElement hosted by UIContainer (Inlien of Block), // retrieve content Start and End positions of the container. // Otherwise scan ITextContainer to find a range for given element. if (childElement is TextElement) { rangeStart = ((TextElement)childElement).ElementStart; rangeEnd = ((TextElement)childElement).ElementEnd; } else { DependencyObject parent = LogicalTreeHelper.GetParent(childElement); if (parent is InlineUIContainer || parent is BlockUIContainer) { rangeStart = ((TextElement)parent).ContentStart; rangeEnd = ((TextElement)parent).ContentEnd; } else { ITextPointer position = _textContainer.Start.CreatePointer(); while (position.CompareTo(_textContainer.End) < 0) { TextPointerContext context = position.GetPointerContext(LogicalDirection.Forward); if (context == TextPointerContext.ElementStart) { if (childElement == position.GetAdjacentElement(LogicalDirection.Forward)) { rangeStart = position.CreatePointer(LogicalDirection.Forward); position.MoveToElementEdge(ElementEdge.AfterEnd); rangeEnd = position.CreatePointer(LogicalDirection.Backward); break; } } else if (context == TextPointerContext.EmbeddedElement) { if (childElement == position.GetAdjacentElement(LogicalDirection.Forward)) { rangeStart = position.CreatePointer(LogicalDirection.Forward); position.MoveToNextContextPosition(LogicalDirection.Forward); rangeEnd = position.CreatePointer(LogicalDirection.Backward); break; } } position.MoveToNextContextPosition(LogicalDirection.Forward); } } } // Create range if (rangeStart != null && rangeEnd != null) { range = new TextRangeAdaptor(this, rangeStart, rangeEnd, _textPeer); } } if (range == null) { throw new InvalidOperationException(SR.Get(SRID.TextProvider_InvalidChildElement)); } return(range); }
// Position at row end (immediately before Row closing tag) is a valid stopper for a caret. // Editing operations are restricted here (e.g. typing should automatically jump // to the following character position. // This property identifies such special position. internal static bool IsAtRowEnd(ITextPointer thisPosition) { return typeof(TableRow).IsAssignableFrom(thisPosition.ParentType) && thisPosition.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd && thisPosition.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.ElementStart; // Note that only non-empty TableRows are good for insertion positions. // Totally empty TableRow is treated as any other incomplete content - not an insertion. }
// Token: 0x060039BC RID: 14780 RVA: 0x00106178 File Offset: 0x00104378 private static int SetFindTextAndFindTextPositionMap(ITextPointer startPosition, ITextPointer endPosition, ITextPointer navigator, LogicalDirection direction, bool matchLast, char[] findText, int[] findTextPositionMap) { Invariant.Assert(startPosition.CompareTo(navigator) <= 0); Invariant.Assert(endPosition.CompareTo(navigator) >= 0); int num = 0; int num2 = 0; if (matchLast && num2 == 0) { findTextPositionMap[findTextPositionMap.Length - 1] = 0; } while ((matchLast ? startPosition.CompareTo(navigator) : navigator.CompareTo(endPosition)) < 0) { switch (navigator.GetPointerContext(direction)) { case TextPointerContext.None: case TextPointerContext.ElementStart: case TextPointerContext.ElementEnd: if (TextFindEngine.IsAdjacentToFormatElement(navigator, direction)) { num++; } else if (!matchLast) { findText[num2] = '\n'; findTextPositionMap[num2] = num2 + num; num2++; } else { num2++; findText[findText.Length - num2] = '\n'; findTextPositionMap[findText.Length - num2] = num2 + num; } navigator.MoveToNextContextPosition(direction); break; case TextPointerContext.Text: { int num3 = navigator.GetTextRunLength(direction); num3 = Math.Min(num3, findText.Length - num2); if (!matchLast) { num3 = Math.Min(num3, navigator.GetOffsetToPosition(endPosition)); navigator.GetTextInRun(direction, findText, num2, num3); for (int i = num2; i < num2 + num3; i++) { findTextPositionMap[i] = i + num; } } else { num3 = Math.Min(num3, startPosition.GetOffsetToPosition(navigator)); navigator.GetTextInRun(direction, findText, findText.Length - num2 - num3, num3); int num4 = findText.Length - num2 - 1; for (int j = num2; j < num2 + num3; j++) { findTextPositionMap[num4--] = j + num + 1; } } navigator.MoveByOffset(matchLast ? (-num3) : num3); num2 += num3; break; } case TextPointerContext.EmbeddedElement: if (!matchLast) { findText[num2] = ''; findTextPositionMap[num2] = num2 + num; num2++; } else { num2++; findText[findText.Length - num2] = ''; findTextPositionMap[findText.Length - num2] = num2 + num; } navigator.MoveToNextContextPosition(direction); break; } if (num2 >= findText.Length) { break; } } if (!matchLast) { if (num2 > 0) { findTextPositionMap[num2] = findTextPositionMap[num2 - 1] + 1; } else { findTextPositionMap[0] = 0; } } return(num2); }
// Returns true if pointer is at the start of a paragraph. internal static bool IsAtParagraphOrBlockUIContainerStart(ITextPointer pointer) { // Is pointer at a potential paragraph position? if (IsAtPotentialParagraphPosition(pointer)) { return true; } // Can you find a <Paragraph> start tag looking backwards? // Loop to skip multiple formatting opening tags, never crossing parent element boundary. while (pointer.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart) { if (TextSchema.IsParagraphOrBlockUIContainer(pointer.ParentType)) { return true; } pointer = pointer.GetNextContextPosition(LogicalDirection.Backward); } return false; }
// Tests for the presence of a non-mergeable Inline bordering a position. // Helper for IsAtNonMergeableInlineEdge. private static BorderingElementCategory GetBorderingElementCategory(ITextPointer position, LogicalDirection direction) { TextPointerContext context = (direction == LogicalDirection.Forward) ? TextPointerContext.ElementEnd : TextPointerContext.ElementStart; BorderingElementCategory category; if (position.GetPointerContext(direction) != context || !typeof(Inline).IsAssignableFrom(position.ParentType)) { category = BorderingElementCategory.NotScopingInline; } else if (TextSchema.IsMergeableInline(position.ParentType)) { category = BorderingElementCategory.MergeableScopingInline; } else { category = BorderingElementCategory.NonMergeableScopingInline; } return category; }
// GetText handler for text runs. private static bool WalkTextRun(ITextPointer navigator, ITextPointer limit, char[] text, int cchReq, ref int charsCopied, UnsafeNativeMethods.TS_RUNINFO[] runInfo, int cRunInfoReq, ref int cRunInfoRcv) { int runCount; int offset; bool hitLimit; Invariant.Assert(navigator.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text); Invariant.Assert(limit == null || navigator.CompareTo(limit) <= 0); hitLimit = false; if (cchReq > 0) { runCount = TextPointerBase.GetTextWithLimit(navigator, LogicalDirection.Forward, text, charsCopied, Math.Min(cchReq, text.Length - charsCopied), limit); navigator.MoveByOffset(runCount); charsCopied += runCount; hitLimit = (text.Length == charsCopied) || (limit != null && navigator.CompareTo(limit) == 0); } else { // Caller doesn't want text, just run info. // Advance the navigator. runCount = navigator.GetTextRunLength(LogicalDirection.Forward); navigator.MoveToNextContextPosition(LogicalDirection.Forward); // If the caller passed in a non-null limit, backup to the limit if // we've passed it. if (limit != null) { if (navigator.CompareTo(limit) >= 0) { offset = limit.GetOffsetToPosition(navigator); Invariant.Assert(offset >= 0 && offset <= runCount, "Bogus offset -- extends past run!"); runCount -= offset; navigator.MoveToPosition(limit); hitLimit = true; } } } if (cRunInfoReq > 0 && runCount > 0) { // Be sure to merge this text run with the previous run, if they are both text runs. // (A good robustness fix would be to make cicero handle this, if we ever get the chance.) if (cRunInfoRcv > 0 && runInfo[cRunInfoRcv - 1].type == UnsafeNativeMethods.TsRunType.TS_RT_PLAIN) { runInfo[cRunInfoRcv - 1].count += runCount; } else { runInfo[cRunInfoRcv].count = runCount; runInfo[cRunInfoRcv].type = UnsafeNativeMethods.TsRunType.TS_RT_PLAIN; cRunInfoRcv++; } } return hitLimit; }
// Tests if a position is between structural symbols where Paragraph is potentially insertable, // but not present. // Positions in empty list item, table cell or flow document. internal static bool IsAtPotentialParagraphPosition(ITextPointer position) { Type parentType = position.ParentType; TextPointerContext backwardContext = position.GetPointerContext(LogicalDirection.Backward); TextPointerContext forwardContext = position.GetPointerContext(LogicalDirection.Forward); if (backwardContext == TextPointerContext.ElementStart && forwardContext == TextPointerContext.ElementEnd) { return typeof(ListItem).IsAssignableFrom(parentType) || typeof(TableCell).IsAssignableFrom(parentType); } else if (backwardContext == TextPointerContext.None && forwardContext == TextPointerContext.None) { return typeof(FlowDocumentView).IsAssignableFrom(parentType) || typeof(FlowDocument).IsAssignableFrom(parentType); } return false; }
// GetText handler for Blocks and TableCell to add '\n' or TS_CHAR_REGION. private static bool WalkRegionBoundary(ITextPointer navigator, ITextPointer limit, char[] text, int cchReq, ref int charsCopied, UnsafeNativeMethods.TS_RUNINFO[] runInfo, int cRunInfoReq, ref int cRunInfoRcv) { bool hitLimit; Invariant.Assert(navigator.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart || navigator.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd); Invariant.Assert(limit == null || navigator.CompareTo(limit) <= 0); // If the caller passed in a non-null limit, we don't do anything and just return true. // we've passed it. if (limit != null) { if (navigator.CompareTo(limit) >= 0) { return true; } } hitLimit = false; if (cchReq > 0) { // Add one TS_CHAR_REGION (TableCell) or '\n' (everything else) char. char ch = (navigator.GetAdjacentElement(LogicalDirection.Forward) is TableCell) ? UnsafeNativeMethods.TS_CHAR_REGION : '\n'; text[charsCopied] = ch; navigator.MoveByOffset(1); charsCopied += 1; hitLimit = (text.Length == charsCopied) || (limit != null && navigator.CompareTo(limit) == 0); } else { // Caller doesn't want text, just run info. // Advance the navigator. // Add one TS_CHAR_REGION char. navigator.MoveByOffset(1); } if (cRunInfoReq > 0) { // Be sure to merge this text run with the previous run, if they are both text runs. // (A good robustness fix would be to make cicero handle this, if we ever get the chance.) if (cRunInfoRcv > 0 && runInfo[cRunInfoRcv - 1].type == UnsafeNativeMethods.TsRunType.TS_RT_PLAIN) { runInfo[cRunInfoRcv - 1].count += 1; } else { runInfo[cRunInfoRcv].count = 1; runInfo[cRunInfoRcv].type = UnsafeNativeMethods.TsRunType.TS_RT_PLAIN; cRunInfoRcv++; } } return hitLimit; }
// Tests if position is before the first Table element in a collection of Blocks at that level. // We treat this as a potential insertion position to allow editing operations before the table. // This property identifies such a position. internal static bool IsBeforeFirstTable(ITextPointer position) { TextPointerContext forwardContext = position.GetPointerContext(LogicalDirection.Forward); TextPointerContext backwardContext = position.GetPointerContext(LogicalDirection.Backward); return (forwardContext == TextPointerContext.ElementStart && (backwardContext == TextPointerContext.ElementStart || backwardContext == TextPointerContext.None) && typeof(Table).IsAssignableFrom(position.GetElementType(LogicalDirection.Forward))); }
// Normalizes a range: // // -The start position is advanced over all element edges not visible // to the IMEs. // -Start and end positions are moved to insertion positions. private void GetNormalizedRange(int startCharOffset, int endCharOffset, out ITextPointer start, out ITextPointer end) { start = CreatePointerAtCharOffset(startCharOffset, LogicalDirection.Forward); end = (startCharOffset == endCharOffset) ? start : CreatePointerAtCharOffset(endCharOffset, LogicalDirection.Backward); // Skip over hidden element edges. while (start.CompareTo(end) < 0) { TextPointerContext forwardContext = start.GetPointerContext(LogicalDirection.Forward); if (forwardContext == TextPointerContext.ElementStart) { TextElement element = start.GetAdjacentElement(LogicalDirection.Forward) as TextElement; if (element == null) break; if (element.IMELeftEdgeCharCount != 0) break; } else if (forwardContext != TextPointerContext.ElementEnd) { break; } start.MoveToNextContextPosition(LogicalDirection.Forward); } // Move to insertion positions. // If the positions are already adjacent to text, we must respect // the IME's decision in regards to exact placement. // MoveToInsertionPosition will skip over surrogates and combining // marks, but the IME needs fine-grained control over these positions. if (start.CompareTo(end) == 0) { start = start.GetFormatNormalizedPosition(LogicalDirection.Backward); end = start; } else { start = start.GetFormatNormalizedPosition(LogicalDirection.Backward); end = end.GetFormatNormalizedPosition(LogicalDirection.Backward); } }
internal static bool IsAtBlockUIContainerStart(ITextPointer position) { return IsInBlockUIContainer(position) && position.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart; }
//...................................................... // // Selection Building With Mouse // //...................................................... // Moves the selection to the mouse cursor position. // If the cursor is facing a UIElement, select the UIElement. // Sets new selection anchor to a given cursorPosition. private void MoveSelectionByMouse(ITextPointer cursorPosition, Point cursorMousePoint) { ITextSelection thisSelection = (ITextSelection)this; if (this.TextView == null) { return; } Invariant.Assert(this.TextView.IsValid); // We just checked RenderScope. We'll use TextView below ITextPointer movingPosition = null; if (cursorPosition.GetPointerContext(cursorPosition.LogicalDirection) == TextPointerContext.EmbeddedElement) { Rect objectEdgeRect = this.TextView.GetRectangleFromTextPosition(cursorPosition); // Check for embedded object. // If the click happend inside of it we need to select it as a whole, when content is not read-only. if (!_textEditor.IsReadOnly && ShouldSelectEmbeddedObject(cursorPosition, cursorMousePoint, objectEdgeRect)) { movingPosition = cursorPosition.GetNextContextPosition(cursorPosition.LogicalDirection); } } // Move selection to this position if (movingPosition == null) { thisSelection.SetCaretToPosition(cursorPosition, cursorPosition.LogicalDirection, /*allowStopAtLineEnd:*/true, /*allowStopNearSpace:*/false); } else { thisSelection.Select(cursorPosition, movingPosition); } }
internal static bool IsAtBlockUIContainerEnd(ITextPointer position) { return IsInBlockUIContainer(position) && position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd; }
// Worker implementing IsAtPotentialRunPosition(position) method. // It is used for testing whether an empty Run element is at potential run potision. // For this purpose the method is supposed to be called with // backwardPosition==run.ElementStart and forwardPosition==run.ElementEnd. private static bool IsAtPotentialRunPosition(ITextPointer backwardPosition, ITextPointer forwardPosition) { Invariant.Assert(backwardPosition.HasEqualScope(forwardPosition)); if (TextSchema.IsValidChild(/*position*/backwardPosition, /*childType*/typeof(Run))) { Type forwardType = forwardPosition.GetElementType(LogicalDirection.Forward); Type backwardType = backwardPosition.GetElementType(LogicalDirection.Backward); if (forwardType != null && backwardType != null) { TextPointerContext forwardContext = forwardPosition.GetPointerContext(LogicalDirection.Forward); TextPointerContext backwardContext = backwardPosition.GetPointerContext(LogicalDirection.Backward); if (// Test if the position inside empty Paragraph or Span backwardContext == TextPointerContext.ElementStart && forwardContext == TextPointerContext.ElementEnd || // Test if the position between opening tag and an embedded object backwardContext == TextPointerContext.ElementStart && TextSchema.IsNonFormattingInline(forwardType) && !IsAtNonMergeableInlineStart(backwardPosition) || // Test if the position between an embedded object and a closing tag forwardContext == TextPointerContext.ElementEnd && TextSchema.IsNonFormattingInline(backwardType) && !IsAtNonMergeableInlineEnd(forwardPosition) || // Test if the position between two embedded objects backwardContext == TextPointerContext.ElementEnd && forwardContext == TextPointerContext.ElementStart && TextSchema.IsNonFormattingInline(backwardType) && TextSchema.IsNonFormattingInline(forwardType) || // Test if the position is adjacent to a non-mergeable inline (Hyperlink). backwardContext == TextPointerContext.ElementEnd && typeof(Inline).IsAssignableFrom(backwardType) && !TextSchema.IsMergeableInline(backwardType) && !typeof(Run).IsAssignableFrom(forwardType) && (forwardContext != TextPointerContext.ElementEnd || !IsAtNonMergeableInlineEnd(forwardPosition)) || forwardContext == TextPointerContext.ElementStart && typeof(Inline).IsAssignableFrom(forwardType) && !TextSchema.IsMergeableInline(forwardType) && !typeof(Run).IsAssignableFrom(backwardType) && (backwardContext != TextPointerContext.ElementStart || !IsAtNonMergeableInlineStart(backwardPosition)) ) { return true; } } } return false; }
/// <summary> /// Determine paragraph type at the current TextPointer and /// create it. Only ListItem elements are considered. Any other /// content is skipped. /// </summary> /// <param name="textPointer"> /// TextPointer at which paragraph is to be created /// </param> /// <param name="fEmptyOk"> /// True if empty paragraph is acceptable /// </param> /// <returns> /// BaseParagraph that was created /// </returns> protected override BaseParagraph GetParagraph(ITextPointer textPointer, bool fEmptyOk) { Invariant.Assert(textPointer is TextPointer); BaseParagraph paragraph = null; while (paragraph == null) { TextPointerContext runType = textPointer.GetPointerContext(LogicalDirection.Forward); if (runType == TextPointerContext.ElementStart) { TextElement element = ((TextPointer)textPointer).GetAdjacentElementFromOuterPosition(LogicalDirection.Forward); if (element is ListItem) { // Need to handle visibility collapsed. //Visibility visibility = Retriever.Visibility(treePtr.CPPtr.Element); //if (visibility != Visibility.Collapsed) //{ // para = new //} //else skip the element paragraph = new ListItemParagraph(element, StructuralCache); break; } else if (element is List) { // Need to handle visibility collapsed. //Visibility visibility = Retriever.Visibility(treePtr.CPPtr.Element); //if (visibility != Visibility.Collapsed) //{ // para = new //} //else skip the element paragraph = new ListParagraph(element, StructuralCache); break; } // Skip all elements, which are not valid list item children if (((TextPointer)textPointer).IsFrozen) { // Need to clone TextPointer before moving it. textPointer = textPointer.CreatePointer(); } textPointer.MoveToPosition(element.ElementEnd); } else if (runType == TextPointerContext.ElementEnd) { // End of list, if the same as Owner of associated element // Skip content otherwise if (Element == ((TextPointer)textPointer).Parent) { break; } if (((TextPointer)textPointer).IsFrozen) { // Need to clone TextPointer before moving it. textPointer = textPointer.CreatePointer(); } textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } else { // Skip content if (((TextPointer)textPointer).IsFrozen) { // Need to clone TextPointer before moving it. textPointer = textPointer.CreatePointer(); } textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } } if (paragraph != null) { StructuralCache.CurrentFormatContext.DependentMax = (TextPointer)textPointer; } return(paragraph); }
/// <summary> /// Set the find text content from reading the text on the current text position. /// </summary> /// <returns> /// Returns the number of characters actually loaded into the findText array. /// </returns> private static int SetFindTextAndFindTextPositionMap( ITextPointer startPosition, ITextPointer endPosition, ITextPointer navigator, LogicalDirection direction, bool matchLast, char[] findText, int[] findTextPositionMap) { Invariant.Assert(startPosition.CompareTo(navigator) <= 0); Invariant.Assert(endPosition.CompareTo(navigator) >= 0); int runCount; int inlineCount = 0; int findTextLength = 0; // Set the first offset which is zero on TextBufferSize + 1 location of // the text position map in case of the backward searching if (matchLast && findTextLength == 0) { findTextPositionMap[findTextPositionMap.Length - 1] = 0; } while ((matchLast ? startPosition.CompareTo(navigator) : navigator.CompareTo(endPosition)) < 0) { switch (navigator.GetPointerContext(direction)) { case TextPointerContext.Text: runCount = navigator.GetTextRunLength(direction); runCount = Math.Min(runCount, findText.Length - findTextLength); if (!matchLast) { runCount = Math.Min(runCount, navigator.GetOffsetToPosition(endPosition)); navigator.GetTextInRun(direction, findText, findTextLength, runCount); for (int i = findTextLength; i < findTextLength + runCount; i++) { findTextPositionMap[i] = i + inlineCount; } } else { runCount = Math.Min(runCount, startPosition.GetOffsetToPosition(navigator)); navigator.GetTextInRun( direction, findText, findText.Length - findTextLength - runCount, runCount); // Set the text offest for the amount of runCount from the last index // of text position map int mapIndex = findText.Length - findTextLength - 1; for (int i = findTextLength; i < findTextLength + runCount; i++) { findTextPositionMap[mapIndex--] = i + inlineCount + 1; } } // Move the navigator position for the amount of runCount navigator.MoveByOffset(matchLast ? -runCount : runCount); findTextLength += runCount; break; case TextPointerContext.None: case TextPointerContext.ElementStart: case TextPointerContext.ElementEnd: if (IsAdjacentToFormatElement(navigator, direction)) { // Filter out formatting tags since find text content is plain. inlineCount++; } else { if (!matchLast) { // Stick in a line break to account for the block element. findText[findTextLength] = '\n'; findTextPositionMap[findTextLength] = findTextLength + inlineCount; findTextLength++; } else { // Increse the find text length first since adding text and map reversely findTextLength++; // Stick in a line break to account for the block element and // add text offset on the last index of text position map findText[findText.Length - findTextLength] = '\n'; findTextPositionMap[findText.Length - findTextLength] = findTextLength + inlineCount; } } navigator.MoveToNextContextPosition(direction); break; case TextPointerContext.EmbeddedElement: if (!matchLast) { findText[findTextLength] = '\xf8ff'; // Unicode private use. findTextPositionMap[findTextLength] = findTextLength + inlineCount; findTextLength++; } else { // Increse the find text length first since adding text and map reversely findTextLength++; // Set the private unicode value and text offset findText[findText.Length - findTextLength] = '\xf8ff'; findTextPositionMap[findText.Length - findTextLength] = findTextLength + inlineCount; } navigator.MoveToNextContextPosition(direction); break; } if (findTextLength >= findText.Length) { break; } } // Complete the adding the find text position to the position map for only the forward finding. // The backward finding(matchLast) is already added initially as the zero offset at the end of // text position map. if (!matchLast) { if (findTextLength > 0) { findTextPositionMap[findTextLength] = findTextPositionMap[findTextLength - 1] + 1; } else { findTextPositionMap[0] = 0; } } return(findTextLength); }
/// <summary> /// Retrieves the height and offset, in pixels, of the edge of /// the object/character represented by position. /// </summary> /// <param name="position"> /// Position of an object/character. /// </param> /// <param name="transform"> /// Transform to be applied to returned rect /// </param> /// <returns> /// The height, in pixels, of the edge of the object/character /// represented by position. /// </returns> /// <exception cref="System.InvalidOperationException"> /// Throws InvalidOperationException if IsValid is false. /// If IsValid returns false, Validate method must be called before /// calling this method. /// </exception> /// <remarks> /// Rect.Width is always 0. /// Output parameter Transform is always Identity. It is not expected that editing scenarios /// will require speparate transform with raw rectangle for this case. /// If the document is empty, then this method returns the expected /// height of a character, if placed at the specified position. /// </remarks> internal override Rect GetRawRectangleFromTextPosition(ITextPointer position, out Transform transform) { #if DEBUG DocumentsTrace.FixedTextOM.TextView.Trace(string.Format("GetRectFromTextPosition {0}, {1}", (FixedTextPointer)position, position.LogicalDirection)); #endif FixedTextPointer ftp = Container.VerifyPosition(position); FixedPosition fixedp; // need a default caret size, otherwise infinite corners cause text editor and MultiPageTextView problems. // Initialize transform to Identity. This function always returns Identity transform. Rect designRect = new Rect(0, 0, 0, 10); transform = Transform.Identity; Debug.Assert(ftp != null); if (ftp.FlowPosition.IsBoundary) { if (!_GetFirstFixedPosition(ftp, out fixedp)) { return designRect; } } else if (!_GetFixedPosition(ftp, out fixedp)) { // // This is the start/end element, we need to find out the next element and return the next element // start offset/height. // if (position.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None) { ITextPointer psNext = position.CreatePointer(1); FixedTextPointer ftpNext = Container.VerifyPosition(psNext); if (!_GetFixedPosition(ftpNext, out fixedp)) { return designRect; } } else { return designRect; } } if (fixedp.Page != this.PageIndex) { return designRect; } DependencyObject element = this.FixedPage.GetElement(fixedp.Node); if (element is Glyphs) { Glyphs g = (Glyphs)element; designRect = _GetGlyphRunDesignRect(g, fixedp.Offset, fixedp.Offset); // need to do transform GeneralTransform tran = g.TransformToAncestor(this.FixedPage); designRect = _GetTransformedCaretRect(tran, designRect.TopLeft, designRect.Height); } else if (element is Image) { Image image = (Image)element; GeneralTransform tran = image.TransformToAncestor(this.FixedPage); Point offset = new Point(0, 0); if (fixedp.Offset > 0) { offset.X += image.ActualWidth; } designRect = _GetTransformedCaretRect(tran, offset, image.ActualHeight); } else if (element is Path) { Path path = (Path)element; GeneralTransform tran = path.TransformToAncestor(this.FixedPage); Rect bounds = path.Data.Bounds; Point offset = bounds.TopLeft; if (fixedp.Offset > 0) { offset.X += bounds.Width; } designRect = _GetTransformedCaretRect(tran, offset, bounds.Height); } return designRect; }
/// <summary> /// Return true if Hyperlink range is invalid. /// Hyperlink is invalid if it include a UiElement except Image or the range end position /// is stated before the end position of hyperlink. /// This must be called before Hyperlink start element position. /// </summary> private static bool IsHyperlinkInvalid(ITextPointer textReader, ITextPointer rangeEnd) { // TextRead must be on the position before the element start position of Hyperlink Invariant.Assert(textReader.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart); Invariant.Assert(typeof(Hyperlink).IsAssignableFrom(textReader.GetElementType(LogicalDirection.Forward))); bool hyperlinkInvalid = false; // Get the forward adjacent element and cast Hyperlink hardly since it must be Hyperlink Hyperlink hyperlink = (Hyperlink)textReader.GetAdjacentElement(LogicalDirection.Forward); ITextPointer hyperlinkNavigation = textReader.CreatePointer(); ITextPointer hyperlinkEnd = textReader.CreatePointer(); hyperlinkEnd.MoveToNextContextPosition(LogicalDirection.Forward); // Find the hyperlink end position hyperlinkEnd.MoveToElementEdge(ElementEdge.AfterEnd); // Hyperlink end position is stated after the range end position. if (hyperlinkEnd.CompareTo(rangeEnd) > 0) { hyperlinkInvalid = true; } else { // Check whether the hyperlink having a UiElement except Image until hyperlink end position while (hyperlinkNavigation.CompareTo(hyperlinkEnd) < 0) { InlineUIContainer inlineUIContainer = hyperlinkNavigation.GetAdjacentElement(LogicalDirection.Forward) as InlineUIContainer; if (inlineUIContainer != null && !(inlineUIContainer.Child is Image)) { hyperlinkInvalid = true; break; } hyperlinkNavigation.MoveToNextContextPosition(LogicalDirection.Forward); } } return hyperlinkInvalid; }
/// <summary> /// Determine paragraph type at the current TextPointer and /// create it. Only ListItem elements are considered. Any other /// content is skipped. /// </summary> /// <param name="textPointer"> /// TextPointer at which paragraph is to be created /// </param> /// <param name="fEmptyOk"> /// True if empty paragraph is acceptable /// </param> /// <returns> /// BaseParagraph that was created /// </returns> protected override BaseParagraph GetParagraph(ITextPointer textPointer, bool fEmptyOk) { Invariant.Assert(textPointer is TextPointer); BaseParagraph paragraph = null; while (paragraph == null) { TextPointerContext runType = textPointer.GetPointerContext(LogicalDirection.Forward); if (runType == TextPointerContext.ElementStart) { TextElement element = ((TextPointer)textPointer).GetAdjacentElementFromOuterPosition(LogicalDirection.Forward); if (element is ListItem) { // paragraph = new ListItemParagraph(element, StructuralCache); break; } else if (element is List) { // paragraph = new ListParagraph(element, StructuralCache); break; } // Skip all elements, which are not valid list item children if (((TextPointer)textPointer).IsFrozen) { // Need to clone TextPointer before moving it. textPointer = textPointer.CreatePointer(); } textPointer.MoveToPosition(element.ElementEnd); } else if (runType == TextPointerContext.ElementEnd) { // End of list, if the same as Owner of associated element // Skip content otherwise if (Element == ((TextPointer)textPointer).Parent) { break; } if (((TextPointer)textPointer).IsFrozen) { // Need to clone TextPointer before moving it. textPointer = textPointer.CreatePointer(); } textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } else { // Skip content if (((TextPointer)textPointer).IsFrozen) { // Need to clone TextPointer before moving it. textPointer = textPointer.CreatePointer(); } textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } } if (paragraph != null) { StructuralCache.CurrentFormatContext.DependentMax = (TextPointer)textPointer; } return paragraph; }
// Move this TP by distance, and respect virtualization of child TextContainer // Return true if distance is within boundary of the aggregated container, false otherwise private static bool xGapAwareScan(DocumentSequenceTextPointer thisTp, int distance) { // // Note: To calculate distance between thisTp.ChildPointer to // it container Start/End position would have been devastating // for those who implemented vitualization. // Ideally we would need a new API on ITextPointer // ITextPointer.IsDistanceOutOfRange ChildDocumentBlock cdb = thisTp.ChildBlock; bool isNavigator = true; ITextPointer childTn = thisTp.ChildPointer; if (childTn == null) { isNavigator = false; childTn = thisTp.ChildPointer.CreatePointer(); } LogicalDirection scanDir = (distance > 0 ? LogicalDirection.Forward : LogicalDirection.Backward); distance = Math.Abs(distance); while (distance > 0) { TextPointerContext tst = childTn.GetPointerContext(scanDir); switch (tst) { case TextPointerContext.ElementStart: childTn.MoveToNextContextPosition(scanDir); distance--; break; case TextPointerContext.ElementEnd: childTn.MoveToNextContextPosition(scanDir); distance--; break; case TextPointerContext.EmbeddedElement: childTn.MoveToNextContextPosition(scanDir); distance--; break; case TextPointerContext.Text: int runLength = childTn.GetTextRunLength(scanDir); int moveLength = runLength < distance ? runLength : distance; distance -= moveLength; //agurcan: Fix for 1098225 //We need to propagate direction info to MoveByOffset if (scanDir == LogicalDirection.Backward) { moveLength *= -1; } childTn.MoveByOffset(moveLength); break; case TextPointerContext.None: if (!((cdb.IsHead && scanDir == LogicalDirection.Backward) || (cdb.IsTail && scanDir == LogicalDirection.Forward) ) ) { cdb = (scanDir == LogicalDirection.Forward ? cdb.NextBlock : cdb.PreviousBlock); childTn = (scanDir == LogicalDirection.Forward ? cdb.ChildContainer.Start.CreatePointer(childTn.LogicalDirection) : cdb.ChildContainer.End.CreatePointer(childTn.LogicalDirection) ); } else { return(false); } break; default: Debug.Assert(false, "invalid TextPointerContext"); break; } } // Re-position thisTp to the new location. thisTp.ChildBlock = cdb; if (isNavigator) { thisTp.ChildPointer = childTn; } else { thisTp.ChildPointer = childTn.CreatePointer(); } return(true); }
//------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods // Worker for MoveToNextFormatNormalizedPosition/MoveToNextInsertionPosition. private static bool NormalizePosition(ITextPointer thisNavigator, LogicalDirection direction, bool respectCaretUnitBoundaries) { Invariant.Assert(!thisNavigator.IsFrozen, "Can't reposition a frozen pointer!"); int symbolCount = 0; int increment; LogicalDirection oppositeDirection; TextPointerContext directEnterScope; TextPointerContext oppositeEnterScope; if (direction == LogicalDirection.Forward) { increment = +1; oppositeDirection = LogicalDirection.Backward; directEnterScope = TextPointerContext.ElementStart; oppositeEnterScope = TextPointerContext.ElementEnd; } else { increment = -1; oppositeDirection = LogicalDirection.Forward; directEnterScope = TextPointerContext.ElementEnd; oppositeEnterScope = TextPointerContext.ElementStart; } // When the pointer appears in between structural tags we need to start // from sliding into the deepest possible position without // leaving any structural units. We need to do that only // if we are not at insertion position already. if (!IsAtNormalizedPosition(thisNavigator, respectCaretUnitBoundaries)) { // Go inside an innermost structured element (non-inline) while ( thisNavigator.GetPointerContext(direction) == directEnterScope && !typeof(Inline).IsAssignableFrom(thisNavigator.GetElementType(direction)) && !IsAtNormalizedPosition(thisNavigator, respectCaretUnitBoundaries)) { thisNavigator.MoveToNextContextPosition(direction); symbolCount += increment; } while ( thisNavigator.GetPointerContext(oppositeDirection) == oppositeEnterScope && !typeof(Inline).IsAssignableFrom(thisNavigator.GetElementType(oppositeDirection)) && !IsAtNormalizedPosition(thisNavigator, respectCaretUnitBoundaries)) { thisNavigator.MoveToNextContextPosition(oppositeDirection); symbolCount -= increment; } } // Get out of a Hyperlink, etc. inner edge. symbolCount = LeaveNonMergeableInlineBoundary(thisNavigator, direction, symbolCount); // Get out of a compound sequence if any. if (respectCaretUnitBoundaries) { while (!IsAtCaretUnitBoundary(thisNavigator)) { symbolCount += increment; thisNavigator.MoveByOffset(increment); } } // Here is the core part of this method's logic - skipping all formatting tags in the given direction. // Skip character formatting tags if they are present in this direction. // Even if an insertion position can be in the middle of this formatting sequence, // we want to skip it all and reach the farthest possible insertion position in that direction. // Such approach guarantees that repeated calls of this normalization will give the same reauls. // In case if there is an inserrtion position in the middle (say, in empty Run), // the loop moving in opposite direction below will find it if needed. while (TextSchema.IsMergeableInline(thisNavigator.GetElementType(direction))) { thisNavigator.MoveToNextContextPosition(direction); symbolCount += increment; } if (!IsAtNormalizedPosition(thisNavigator, respectCaretUnitBoundaries)) { // If still not at insertion point, try skipping inline tags in the opposite direction // now possibly stopping inside of empty element while (!IsAtNormalizedPosition(thisNavigator, respectCaretUnitBoundaries) && TextSchema.IsMergeableInline(thisNavigator.GetElementType(oppositeDirection))) { thisNavigator.MoveToNextContextPosition(oppositeDirection); symbolCount -= increment; } // If still not at insertion point, then try harder - skipping block tags // First in "preferred" direction while (!IsAtNormalizedPosition(thisNavigator, respectCaretUnitBoundaries) && thisNavigator.MoveToNextContextPosition(direction)) { symbolCount += increment; } // And finally in apposite direction while (!IsAtNormalizedPosition(thisNavigator, respectCaretUnitBoundaries) && thisNavigator.MoveToNextContextPosition(oppositeDirection)) { symbolCount -= increment; } if (!IsAtNormalizedPosition(thisNavigator, respectCaretUnitBoundaries)) { // When there is no insertion positions in the whole document // we return the position back to its original place. thisNavigator.MoveByOffset(-symbolCount); } } return symbolCount != 0; }
/// <summary> /// Retrieves the height and offset, in pixels, of the edge of /// the object/character represented by position. /// </summary> /// <param name="position"> /// Position of an object/character. /// </param> /// <param name="transform"> /// Transform to be applied to returned rect /// </param> /// <returns> /// The height, in pixels, of the edge of the object/character /// represented by position. /// </returns> /// <exception cref="System.InvalidOperationException"> /// Throws InvalidOperationException if IsValid is false. /// If IsValid returns false, Validate method must be called before /// calling this method. /// </exception> /// <remarks> /// Rect.Width is always 0. /// Output parameter Transform is always Identity. It is not expected that editing scenarios /// will require speparate transform with raw rectangle for this case. /// If the document is empty, then this method returns the expected /// height of a character, if placed at the specified position. /// </remarks> internal override Rect GetRawRectangleFromTextPosition(ITextPointer position, out Transform transform) { #if DEBUG DocumentsTrace.FixedTextOM.TextView.Trace(string.Format("GetRectFromTextPosition {0}, {1}", (FixedTextPointer)position, position.LogicalDirection)); #endif FixedTextPointer ftp = Container.VerifyPosition(position); FixedPosition fixedp; // need a default caret size, otherwise infinite corners cause text editor and MultiPageTextView problems. // Initialize transform to Identity. This function always returns Identity transform. Rect designRect = new Rect(0, 0, 0, 10); transform = Transform.Identity; Debug.Assert(ftp != null); if (ftp.FlowPosition.IsBoundary) { if (!_GetFirstFixedPosition(ftp, out fixedp)) { return(designRect); } } else if (!_GetFixedPosition(ftp, out fixedp)) { // // This is the start/end element, we need to find out the next element and return the next element // start offset/height. // if (position.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None) { ITextPointer psNext = position.CreatePointer(1); FixedTextPointer ftpNext = Container.VerifyPosition(psNext); if (!_GetFixedPosition(ftpNext, out fixedp)) { return(designRect); } } else { return(designRect); } } if (fixedp.Page != this.PageIndex) { return(designRect); } DependencyObject element = this.FixedPage.GetElement(fixedp.Node); if (element is Glyphs) { Glyphs g = (Glyphs)element; designRect = _GetGlyphRunDesignRect(g, fixedp.Offset, fixedp.Offset); // need to do transform GeneralTransform tran = g.TransformToAncestor(this.FixedPage); designRect = _GetTransformedCaretRect(tran, designRect.TopLeft, designRect.Height); } else if (element is Image) { Image image = (Image)element; GeneralTransform tran = image.TransformToAncestor(this.FixedPage); Point offset = new Point(0, 0); if (fixedp.Offset > 0) { offset.X += image.ActualWidth; } designRect = _GetTransformedCaretRect(tran, offset, image.ActualHeight); } else if (element is Path) { Path path = (Path)element; GeneralTransform tran = path.TransformToAncestor(this.FixedPage); Rect bounds = path.Data.Bounds; Point offset = bounds.TopLeft; if (fixedp.Offset > 0) { offset.X += bounds.Width; } designRect = _GetTransformedCaretRect(tran, offset, bounds.Height); } return(designRect); }
// Returns true if the position is on the caret unit boundary. // Call TextView's IsAtCaretUnitBoundary if TextView is valid for this position // and it appears strictly within text run. // We consider all markup-boundary positions as caret unit boundaries. // If TextView information is not available call IsInsideCompoundSequence. private static bool IsAtCaretUnitBoundary(ITextPointer position) { bool isAtCaretUnitBoundary; TextPointerContext forwardContext = position.GetPointerContext(LogicalDirection.Forward); TextPointerContext backwardContext = position.GetPointerContext(LogicalDirection.Backward); if (backwardContext == TextPointerContext.Text && forwardContext == TextPointerContext.Text) { if (position.HasValidLayout) { // Check the insertion position with TextView's IsAtCaretUnitBoundary // that will acurately check the caret unit bounday for surrogate and international // characters isAtCaretUnitBoundary = position.IsAtCaretUnitBoundary; } else { // Check the insertion position with the internal compound sequence isAtCaretUnitBoundary = !IsInsideCompoundSequence(position); } } else { isAtCaretUnitBoundary = true; } return isAtCaretUnitBoundary; }