// Returns true when one or both ends of the error lies at the inner edge of non-mergeable inline
        // such as Hyperlink.  In this case, a TextRange will normalize its ends outside
        // the scope of the inline, and the corrected text will not be covered by it.
        //
        // We work around the common case, when the error is contained within a single
        // Run.  In more complex cases we'll fail and fall back to using a TextRange.
        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))
            {
                if (typeof(Run).IsAssignableFrom(textStart.ParentType) &&
                    textStart.HasEqualScope(textEnd))
                {
                    result = true;
                }
            }

            return(result);
        }
        /// <summary>
        /// Walks the tree up from current position and writes all scoping tags
        /// in their natural order - from root to leafs.
        /// </summary>
        /// <param name="range">
        /// Range identifying the whole selection.
        /// Needed for
        /// - table cell range case: proper column processing: to output only columns related to the selection
        /// - text segement case: hyperlink serialization heuristics
        /// </param>
        /// <param name="thisElement">
        /// ITextPointer identifying an element.
        /// </param>
        /// <param name="scope">
        /// A position identifying the scope which should be used for serialization.
        /// All tags outside of this scope will be ignored.
        /// </param>
        /// <param name="xmlWriter">
        /// XmlWriter to write element tags.
        /// </param>
        /// <param name="xamlTypeMapper"></param>
        /// <param name="reduceElement">
        /// <see cref="WriteStartXamlElement"/>
        /// </param>
        /// <param name="ignoreWriteHyperlinkEnd"></param>
        /// <param name="ignoreList"></param>
        /// <param name="preserveTextElements"></param>
        /// /// <returns>
        /// Number of opening tags written into XmlWriter.
        /// This number should be used afterwards to close all opened tags.
        /// </returns>
        private static int WriteOpeningTags(ITextRange range, ITextPointer thisElement, ITextPointer scope, XmlWriter xmlWriter, XamlTypeMapper xamlTypeMapper, bool reduceElement, out bool ignoreWriteHyperlinkEnd, ref List<int> ignoreList, bool preserveTextElements)
        {
            ignoreWriteHyperlinkEnd = false;

            // Recursion ends when we reach the scope level. We will write tags on returing path from the recursion
            if (thisElement.HasEqualScope(scope))
            {
                return 0; // no elements have opened at this level. Return elementCount==0.
            }

            Invariant.Assert(typeof(TextElement).IsAssignableFrom(thisElement.ParentType), "thisElement is expected to be a TextElement");

            ITextPointer previousLevel = thisElement.CreatePointer();
            previousLevel.MoveToElementEdge(ElementEdge.BeforeStart);

            // Recurse into the parent element
            int elementLevel = WriteOpeningTags(range, previousLevel, scope, xmlWriter, xamlTypeMapper, reduceElement, out ignoreWriteHyperlinkEnd, ref ignoreList, preserveTextElements);

            // After returning from the recursion - when all parent tags have been written,
            // write the opening tag for this element

            // Hyperlink open tag will be skipped since the range selection of Hyperlink is the partial
            // of Hyperlink range or Hyperlink include invalid UIElement except Image.
            bool ignoreHyperlink = false;
            bool isPartialNonTypographic = false;

            if (thisElement.ParentType == typeof(Hyperlink))
            {
                if (TextPointerBase.IsAtNonMergeableInlineStart(range.Start))
                {
                    ITextPointer position = thisElement.CreatePointer();
                    position.MoveToElementEdge(ElementEdge.BeforeStart);

                    ignoreHyperlink = IsHyperlinkInvalid(position, range.End);
                }
                else
                {
                    ignoreHyperlink = true;
                }
            }
            else
            {
                // 

                TextElementEditingBehaviorAttribute att = (TextElementEditingBehaviorAttribute)Attribute.GetCustomAttribute(thisElement.ParentType, typeof(TextElementEditingBehaviorAttribute));
                if (att != null && !att.IsTypographicOnly)
                {
                    if (TextPointerBase.IsAtNonMergeableInlineStart(range.Start))
                    {
                        ITextPointer position = thisElement.CreatePointer();
                        position.MoveToElementEdge(ElementEdge.BeforeStart);

                        isPartialNonTypographic = IsPartialNonTypographic(position, range.End);
                    }
                    else
                    {
                        isPartialNonTypographic = true;
                    }
                }
            }
            int count;

            if (ignoreHyperlink)
            {
                // Ignore writing Hyperlink opening tag
                ignoreWriteHyperlinkEnd = true;

                // Set elementLevel without adding it
                count = elementLevel;
            }
            else if (isPartialNonTypographic)
            {
                // Add the end pointer to the list
                ITextPointer position = thisElement.CreatePointer();
                position.MoveToElementEdge(ElementEdge.BeforeEnd);
                ignoreList.Add(position.Offset);

                // Set elementLevel without adding to it
                count = elementLevel;
            }
            else
            {
                // Write the opening tag 
                WriteStartXamlElement(range, thisElement, xmlWriter, xamlTypeMapper, reduceElement, preserveTextElements);

                // Each opening tag adds one to the level count
                count = elementLevel + 1;
            }

            // Return the opening tag count
            return count;
        }
        // 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;
        }
示例#4
0
        // Returns true when one or both ends of the error lies at the inner edge of non-mergeable inline
        // such as Hyperlink.  In this case, a TextRange will normalize its ends outside
        // the scope of the inline, and the corrected text will not be covered by it.
        //
        // We work around the common case, when the error is contained within a single
        // Run.  In more complex cases we'll fail and fall back to using a TextRange.
        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))
            {
                if (typeof(Run).IsAssignableFrom(textStart.ParentType) &&
                    textStart.HasEqualScope(textEnd))
                {
                    result = true;
                }
            }

            return result;
        }