TextRangeEditLists.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Framework / System / Windows / Documents / TextRangeEditLists.cs / 1 / TextRangeEditLists.cs

                            //---------------------------------------------------------------------------- 
//
// File TextRangeEditLists.cs
//
// Copyright (C) Microsoft Corporation.  All rights reserved. 
//
// Description: Internal static class representing a group of methods 
//              for list editing 
//
//--------------------------------------------------------------------------- 

namespace System.Windows.Documents
{
    using System; 
    using MS.Internal;
    using System.Windows.Controls; 
 
    /// 
    /// The TextRange class represents a pair of TextPositions, with many 
    /// rich text editing operations exposed.
    /// 
    internal static class TextRangeEditLists
    { 
        // -------------------------------------------------------------------
        // 
        // Internal Methods 
        //
        // ------------------------------------------------------------------- 

        #region Internal Methods

        ///  
        /// Merges two paragraphs followinng one another.
        /// The content of a second paragraph is moved into the end 
        /// of the first one. 
        /// 
        ///  
        /// First of two merged paragraphs or BlockUIContainer.
        /// 
        /// 
        /// Second of two mered paragraphs or BlockUIContainer. 
        /// 
        ///  
        /// true if paragraphs have been merged; false if no actions where made. 
        /// 
        internal static bool MergeParagraphs(Block firstParagraphOrBlockUIContainer, Block secondParagraphOrBlockUIContainer) 
        {
            if (!ParagraphsAreMergeable(firstParagraphOrBlockUIContainer, secondParagraphOrBlockUIContainer))
            {
                return false; // Cannot mearge these paragraphs. 
            }
 
            // Store parent list item of a second paragraph - 
            // to correct its structure after the merge
            ListItem secondListItem = secondParagraphOrBlockUIContainer.PreviousBlock == null ? secondParagraphOrBlockUIContainer.Parent as ListItem : null; 

            if (secondListItem != null && secondListItem.PreviousListItem == null && secondParagraphOrBlockUIContainer.NextBlock is List)
            {
                // The second paragraph is a first list item in some list. 
                // It has a sublists in it, so this sublist must be unindented
                // to avoid double bulleted line. 
                List sublistOfSecondParagraph = (List)secondParagraphOrBlockUIContainer.NextBlock; 
                if (sublistOfSecondParagraph.ElementEnd.CompareTo(secondListItem.ContentEnd) == 0)
                { 
                    secondListItem.Reposition(null, null);
                }
                else
                { 
                    secondListItem.Reposition(sublistOfSecondParagraph.ElementEnd, secondListItem.ContentEnd);
                } 
                // At this point the schema is temporaty broken: the secondParagraph and the sublistOfSecondParagraph have List as a parent 
                sublistOfSecondParagraph.Reposition(null, null);
                // The schema is repared as to sublistOfSecondParagraph concern, but still broken for secondParagraph - must be corrected in the following code 
            }

            // Move the second paragraph out of its wrappers separating from the first paragraph (if any).
            // We can not use RepositionWithContent because it would destroy 
            // all pointers and ranges within a moved paragraph.
            // Instead we reposition elements around the two paragraphs. 
            while (secondParagraphOrBlockUIContainer.ElementStart.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart) 
            {
                TextElement parentBlock = (TextElement)secondParagraphOrBlockUIContainer.Parent; 
                Invariant.Assert(parentBlock != null);
                Invariant.Assert(TextSchema.AllowsParagraphMerging(parentBlock.GetType()));

                if (secondParagraphOrBlockUIContainer.ElementEnd.CompareTo(parentBlock.ContentEnd) == 0) 
                {
                    // Remove ancestor block if it becomes empty 
                    parentBlock.Reposition(null, null); 
                }
                else 
                {
                    // Move ancestor's Start after the end of our paragraph
                    parentBlock.Reposition(secondParagraphOrBlockUIContainer.ElementEnd, parentBlock.ContentEnd);
                } 
            }
 
            // Store a position after the second paragraph where list merging may be needed 
            TextPointer positionAfterSecondParagraph = secondParagraphOrBlockUIContainer.ElementEnd.GetFrozenPointer(LogicalDirection.Forward);
 
            // Move the second paragraph to become an immediate following sibling of the first paragraph
            while (true)
            {
                TextElement previousBlock = secondParagraphOrBlockUIContainer.ElementStart.GetAdjacentElement(LogicalDirection.Backward) as TextElement; 
                // Note: We cannot use Block.NextSibling property, because the structure is invalid during this process
 
                Invariant.Assert(previousBlock != null); 

                if (previousBlock is Paragraph || previousBlock is BlockUIContainer) 
                {
                    break;
                }
 
                Invariant.Assert(TextSchema.AllowsParagraphMerging(previousBlock.GetType()));
                previousBlock.Reposition(previousBlock.ContentStart, secondParagraphOrBlockUIContainer.ElementEnd); 
            } 

            // Now that paragraphs are next to each other merge them. 

            // If one of paragraphs is empty we will apply special logic - to preserve a formatting from a non-empty one
            if (secondParagraphOrBlockUIContainer.TextRange.IsEmpty)
            { 
                secondParagraphOrBlockUIContainer.RepositionWithContent(null);
            } 
            else if (firstParagraphOrBlockUIContainer.TextRange.IsEmpty) 
            {
                firstParagraphOrBlockUIContainer.RepositionWithContent(null); 
            }
            else if (firstParagraphOrBlockUIContainer is Paragraph && secondParagraphOrBlockUIContainer is Paragraph)
            {
                // Do reposition magic for merging paragraph content 
                // without destroying any pointers positioned in them.
                // Pull the second paragraph into the first one 
                Invariant.Assert(firstParagraphOrBlockUIContainer.ElementEnd.CompareTo(secondParagraphOrBlockUIContainer.ElementStart) == 0); 
                firstParagraphOrBlockUIContainer.Reposition(firstParagraphOrBlockUIContainer.ContentStart, secondParagraphOrBlockUIContainer.ElementEnd);
 
                // Store inline merging position
                TextPointer inlineMergingPosition = secondParagraphOrBlockUIContainer.ElementStart;

                // Now we can delete the second paragraph 
                secondParagraphOrBlockUIContainer.Reposition(null, null);
 
                // Merge formatting elements at the point of paragraphs merging 
                TextRangeEdit.MergeFormattingInlines(inlineMergingPosition);
            } 

            // Merge ListItems wrapping first and second paragraphs.
            ListItem followingListItem = positionAfterSecondParagraph.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart
                ? positionAfterSecondParagraph.GetAdjacentElement(LogicalDirection.Forward) as ListItem : null; 
            if (followingListItem != null && followingListItem == secondListItem)
            { 
                ListItem precedingListItem = positionAfterSecondParagraph.GetAdjacentElement(LogicalDirection.Backward) as ListItem; 
                if (precedingListItem != null)
                { 
                    // Merge the second list item with the preceding one
                    Invariant.Assert(positionAfterSecondParagraph.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart);
                    Invariant.Assert(positionAfterSecondParagraph.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementEnd);
                    precedingListItem.Reposition(precedingListItem.ContentStart, followingListItem.ElementEnd); 
                    followingListItem.Reposition(null, null);
                } 
            } 

            // Merge lists at merge position 
            MergeLists(positionAfterSecondParagraph);

            return true;
        } 

        ///  
        /// Like MergeLists, but will search over formatting elements when 
        /// looking for Lists to merge.
        ///  
        internal static bool MergeListsAroundNormalizedPosition(TextPointer mergePosition)
        {
            // Search forward for a List to merge with.
            TextPointer navigator = mergePosition.CreatePointer(); 

            while (navigator.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd) 
            { 
                navigator.MoveToNextContextPosition(LogicalDirection.Forward);
            } 

            bool merged = MergeLists(navigator);

            // Search backward for a List to merge with. 
            if (!merged)
            { 
                navigator.MoveToPosition(mergePosition); 

                while (navigator.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart) 
                {
                    navigator.MoveToNextContextPosition(LogicalDirection.Backward);
                }
 
                merged = MergeLists(navigator);
            } 
 
            return merged;
        } 

        /// 
        /// Merges two naighboring lists ending and starting at position mergePosition
        ///  
        /// 
        /// Position at with two List elements are expected to appear next to each other 
        ///  
        /// 
        /// true if there were two mergeable List elements and merge happened. 
        /// false if there is no pair of List elements at the mergePosition.
        /// 
        internal static bool MergeLists(TextPointer mergePosition)
        { 
            if (mergePosition.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.ElementEnd ||
                mergePosition.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.ElementStart) 
            { 
                return false;
            } 

            List precedingList = mergePosition.GetAdjacentElement(LogicalDirection.Backward) as List;
            List followingList = mergePosition.GetAdjacentElement(LogicalDirection.Forward) as List;
 
            if (precedingList == null || followingList == null)
            { 
                return false; 
            }
 
            precedingList.Reposition(precedingList.ContentStart, followingList.ElementEnd);
            followingList.Reposition(null, null);

            // We need to set appropriate FlowDirection property on the new List and its paragraph children. 
            // We take the FlowDirection value from the preceding list.
            TextRangeEdit.SetParagraphProperty(precedingList.ElementStart, precedingList.ElementEnd, 
                Paragraph.FlowDirectionProperty, precedingList.GetValue(Paragraph.FlowDirectionProperty)); 

            return true; 
        }

        // Returns true if all paragraphs in a range belong to the same block,
        // so they can be easily grouped and ungrouped. 
        internal static bool IsListOperationApplicable(TextRange range)
        { 
            // First check, if range start/end are parented by ListItems within the same parent list. 
            if (IsRangeWithinSingleList(range))
            { 
                return true;
            }

            // Adjust range end so that it does not affect a following paragraph. 
            TextPointer end = (TextPointer)TextRangeEdit.GetAdjustedRangeEnd(range.Start, range.End);
 
            // Now try plain Paragraphs 
            Block firstBlock = range.Start.ParagraphOrBlockUIContainer;
            Block lastBlock = end.ParagraphOrBlockUIContainer; 

            if (firstBlock != null && lastBlock != null && firstBlock.Parent == lastBlock.Parent)
            {
                return true; 
            }
 
            // Allow list editing at potential paragraph positions, this includes 
            // positions in initial RichTextBox, empty TableCell, empty ListItem where paragraphs were not yet created.
            if (range.IsEmpty && TextPointerBase.IsAtPotentialParagraphPosition(range.Start)) 
            {
                return true;
            }
 
            return false;
        } 
 
        internal static bool ConvertParagraphsToListItems(TextRange range, TextMarkerStyle markerStyle)
        { 
            if (range.IsEmpty && TextPointerBase.IsAtPotentialParagraphPosition(range.Start))
            {
                TextPointer insertionPosition = TextRangeEditTables.EnsureInsertionPosition(range.Start);
                ((ITextRange)range).Select(insertionPosition, insertionPosition); 
            }
 
            Block firstBlock = range.Start.ParagraphOrBlockUIContainer; 

            TextPointer end = (TextPointer)TextRangeEdit.GetAdjustedRangeEnd(range.Start, range.End); 
            Block lastBlock = end.ParagraphOrBlockUIContainer;

            // We assume that a range contains a sequence of one-level paragraphs.
            // Otherwise the operation is disabled. 
            if (firstBlock == null || lastBlock == null || firstBlock.Parent != lastBlock.Parent ||
                firstBlock.Parent is ListItem && firstBlock.PreviousBlock == null) 
            { 
                // Either the paragraphs belong to different scopes or first of them has a bullet already.
                // We cannot convert them into bulleted lists. 
                return false;
            }

            // Check that all top-level elements of selection are Paragraphs. 
            // We do not apply the command to Tables or Sections.
            for (Block block = firstBlock; block != lastBlock && block != null; block = block.NextBlock) 
            { 
                if (block is Table || block is Section)
                { 
                    return false;
                }
            }
 
            ListItem parentListItem = firstBlock.Parent as ListItem;
            if (parentListItem != null) 
            { 
                // Paragraphs are inside of ListItem already.
 
                // Split a current ListItem before each of selected blocks
                Block block = firstBlock;
                while (block != null)
                { 
                    Block nextBlock = block == lastBlock ? null : block.ElementEnd.GetAdjacentElement(LogicalDirection.Forward) as Block;
 
                    Invariant.Assert(block.Parent is ListItem); 
                    TextRangeEdit.SplitElement(block.ElementStart);
 
                    block = nextBlock;
                }
            }
            else 
            {
                // Create a list around all paragraphs 
                List list = new List(); 
                list.MarkerStyle = markerStyle;
                list.Apply(firstBlock, lastBlock); 

                // Merge with neighboring lists
                //MergeLists(list.ElementEnd);  // start with End to not loose an instance of "list" during merging with the following list
                //MergeLists(list.ElementStart); 
            }
 
            return true; 
        }
 
        // Assumes that a range contains a sequence of same-level ListItems.
        // Converts all these ListItems into Paragraphs and
        // either adds them to preceding ListItem (as non-bulleted continuation)
        // or pulls them out of a List if they start in the beginning of a List 
        internal static void ConvertListItemsToParagraphs(TextRange range)
        { 
            ListItem firstListItem = TextPointerBase.GetListItem(range.Start); 
            ListItem lastListItem = TextPointerBase.GetListItem((TextPointer)TextRangeEdit.GetAdjustedRangeEnd(range.Start, range.End));
 
            // The range must be in a sequence of ListItems belonging to one List wrapper
            if (firstListItem == null || lastListItem == null || firstListItem.Parent != lastListItem.Parent || !(firstListItem.Parent is List))
            {
                return; 
            }
 
            List listToRemove = null; 

            ListItem leadingListItem = firstListItem.PreviousListItem; 
            if (leadingListItem != null)
            {
                // We have a leading ListItem, so pull selected items into it
                leadingListItem.Reposition(leadingListItem.ContentStart, lastListItem.ElementEnd); 
            }
            else 
            { 
                // We do not have a leading ListItem. So pull selected items out of a list
 
                // Cut wrapping list after endListItem
                if (lastListItem.NextListItem != null)
                {
                    TextRangeEdit.SplitElement(lastListItem.ElementEnd); 
                }
 
                // Set list to remove 
                listToRemove = firstListItem.List;
            } 

            // Remove ListItems from all selected blocks
            ListItem listItem = firstListItem;
            while (listItem != null) 
            {
                ListItem nextListItem = listItem.ElementEnd.GetAdjacentElement(LogicalDirection.Forward) as ListItem; 
 
                // If this is an empty , insert an explicit paragraph in it before deleting the list item.
                if (listItem.ContentStart.CompareTo(listItem.ContentEnd) == 0) 
                {
                    TextRangeEditTables.EnsureInsertionPosition(listItem.ContentStart);
                }
 
                listItem.Reposition(null, null);
                listItem = listItem == lastListItem ? null : nextListItem; 
            } 

            // If we have a list to remove, remove it and set its FlowDirection to its children 
            if (listToRemove != null)
            {
                FlowDirection flowDirection = (FlowDirection)listToRemove.GetValue(Paragraph.FlowDirectionProperty);
                listToRemove.Reposition(null, null); 
                TextRangeEdit.SetParagraphProperty(range.Start, range.End, Paragraph.FlowDirectionProperty, flowDirection);
            } 
        } 

        internal static void IndentListItems(TextRange range) 
        {
            ListItem firstListItem = TextPointerBase.GetImmediateListItem(range.Start);
            ListItem lastListItem = TextPointerBase.GetImmediateListItem((TextPointer)TextRangeEdit.GetAdjustedRangeEnd(range.Start, range.End));
 
            // The range must be in a sequence of ListItems belonging to one List wrapper
            if (firstListItem == null || lastListItem == null || 
                firstListItem.Parent != lastListItem.Parent || 
                !(firstListItem.Parent is List))
            { 
                return;
            }

            // Identify a ListItem which will become a leading item for this potential sublist 
            ListItem leadingListItem = firstListItem.PreviousListItem;
            if (leadingListItem == null) 
            { 
                // There is no leading list item for this group. Indentation is impossible
                return; 
            }

            // Get current List
            List list = (List)firstListItem.Parent; 

            // Wrap these items into a List - inheriting all properties from our current list 
            List indentedList = (List)TextRangeEdit.InsertElementClone(firstListItem.ElementStart, lastListItem.ElementEnd, list); 

            // Wrap the leading ListItem to include the sublist 
            leadingListItem.Reposition(leadingListItem.ContentStart, indentedList.ElementEnd);

            // Unwrap sublist from the last selected list item (to keep it on its level)
            Paragraph leadingParagraphOfLastItem = lastListItem.Blocks.FirstBlock as Paragraph; 
            if (leadingParagraphOfLastItem != null)
            { 
                // Unindenting all items of a sublist - if it is the only following element of a list 
                List nestedListOfLastItem = leadingParagraphOfLastItem.NextBlock as List;
                if (nestedListOfLastItem != null && nestedListOfLastItem.NextBlock == null) 
                {
                    lastListItem.Reposition(lastListItem.ContentStart, nestedListOfLastItem.ElementStart);
                    nestedListOfLastItem.Reposition(null, null);
                } 
            }
 
            // Merge with neighboring lists 
            MergeLists(indentedList.ElementStart);
            // No need in merging at nestedList.ElementEnd as ListItem ends there. 
        }

        internal static bool UnindentListItems(TextRange range)
        { 
            // If listitems in this range cross a list boundary, we cannot unindent them.
            if (!IsRangeWithinSingleList(range)) 
            { 
                return false;
            } 

            ListItem firstListItem = TextPointerBase.GetListItem(range.Start);
            ListItem lastListItem = TextPointerBase.GetListItem((TextPointer)TextRangeEdit.GetAdjustedRangeEnd(range.Start, range.End));
 
            // At this point it is possible that lastListItem is a child of
            // firstListItem. 
            // 
            // This is due to a special case in IsRangeWithinSingleList
            // which allows the input TextRange to cross List boundaries only 
            // in the case where the TextRange ends adjacent to an insertion
            // position at the same level as the start, e.g.:
            //
            //    - parent item 
            //      - start item (range starts here)
            //          - child item (range ends here) 
            //       
            //
            // Here we check for that special case and ensure that 
            // lastListItem is at the same level as firstListItem.

            TextElement parent = (TextElement)lastListItem.Parent;
 
            while (parent != firstListItem.Parent)
            { 
                lastListItem = parent as ListItem; 
                parent = (TextElement)parent.Parent;
            } 
            if (lastListItem == null)
            {
                // This can happen if the input is a fragment, a collection
                // of ListItems not parented by an outer List. 
                return false;
            } 
 
            // Cut wrapping list before startListItem
            if (firstListItem.PreviousListItem != null) 
            {
                TextRangeEdit.SplitElement(firstListItem.ElementStart);
            }
 
            // Cut wrapping list after endListItem
            if (lastListItem.NextListItem != null) 
            { 
                TextRangeEdit.SplitElement(lastListItem.ElementEnd);
            } 

            // Remove List wrapper from selected items
            List unindentedList = (List)firstListItem.Parent;
 
            // Check whether we have outer ListItem
            ListItem outerListItem = unindentedList.Parent as ListItem; 
            if (outerListItem != null) 
            {
                // Selected items belong to a nested list. 
                // So we need to pull them to the level of enclosing ListItem, i.e. cut this ListItem.
                // In this case we also need to include trailing list into the last of selected items

                // Remove a wrapping List from selected items 
                unindentedList.Reposition(null, null);
 
                // Remember end position of outerListItem to pull any trailing list or other blocks into the last of selected listitems 
                TextPointer outerListItemEnd = outerListItem.ContentEnd;
 
                if (outerListItem.ContentStart.CompareTo(firstListItem.ElementStart) == 0)
                {
                    // There is nothing before first list item; so outer list item would be empty - just delete it
                    outerListItem.Reposition(null, null); 
                }
                else 
                { 
                    // Wrap all stuff preceding firstListItem in outerListItem
                    outerListItem.Reposition(outerListItem.ContentStart, firstListItem.ElementStart); 
                }

                if (outerListItemEnd.CompareTo(lastListItem.ElementEnd) == 0)
                { 
                    // There are no following siblings to pull into last selected item; do nothing.
                } 
                else 
                {
                    // Pull trailing items (following siblings to the selected ones) into the last selected item 

                    // Remember a position to merge any trailing list after lastListItem
                    TextPointer mergePosition = lastListItem.ContentEnd;
 
                    // Reposition last selectd ListItem so that it includes trailing list (or other block) as its children
                    lastListItem.Reposition(lastListItem.ContentStart, outerListItemEnd); 
 
                    // Merge any trailing list with a sublist outdented with our listitem
                    MergeLists(mergePosition); 
                }
            }
            else
            { 
                // Selected items are not in nested list.
                // We need to simply unwrap them and convert to paragraphs 
 
                TextPointer start = unindentedList.ElementStart;
                TextPointer end = unindentedList.ElementEnd; 

                // Save the list's FlowDirection value, to apply later to its children.
                object listFlowDirectionValue = unindentedList.GetValue(Paragraph.FlowDirectionProperty);
 
                // Remove a wrapping List from selected items
                unindentedList.Reposition(null, null); 
 
                // Remove ListItems from all selected items
                ListItem listItem = firstListItem; 
                while (listItem != null)
                {
                    ListItem nextListItem = listItem.ElementEnd.GetAdjacentElement(LogicalDirection.Forward) as ListItem;
 
                    // If this is an empty , insert an explicit paragraph in it before deleting the list item.
                    if (listItem.ContentStart.CompareTo(listItem.ContentEnd) == 0) 
                    { 
                        TextRangeEditTables.EnsureInsertionPosition(listItem.ContentStart);
                    } 

                    listItem.Reposition(null, null);
                    listItem = listItem == lastListItem ? null : nextListItem;
                } 

                // Apply FlowDirection of the list just deleted to all its children. 
                TextRangeEdit.SetParagraphProperty(start, end, Paragraph.FlowDirectionProperty, listFlowDirectionValue); 

                // Merge lists on boundaries 
                MergeLists(start);
                MergeLists(end);
            }
 
            return true;
        } 
 
        // Predicate which returns true if list items in this range are within the scope of the same parent list.
        private static bool IsRangeWithinSingleList(TextRange range) 
        {
            ListItem startListItem = TextPointerBase.GetListItem(range.Start);

            // Adjust range end so that it does not affect a following paragraph. 
            TextPointer end = (TextPointer)TextRangeEdit.GetAdjustedRangeEnd(range.Start, range.End);
            ListItem endListItem = TextPointerBase.GetListItem(end); 
 
            // Check if the ListItems belong to one List wrapper.
            if (startListItem != null && endListItem != null && startListItem.Parent == endListItem.Parent) 
            {
                return true;
            }
 
            // In case of nested lists, it may be the case that start and end list item do not belong to one list wrapper,
            // yet no visual list boundary is crossed. 
            // e.g. 
            //  * aa
            //      * bb 
            //      * cc
            // Special case so that list operations are applicable in this scenario.
            if (startListItem != null && endListItem != null)
            { 
                while (end.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd)
                { 
                    if (end.Parent == startListItem.Parent) 
                    {
                        return true; 
                    }
                    end = end.GetNextContextPosition(LogicalDirection.Forward);
                }
            } 

            return false; 
        } 

        // Checks whether two paragraphs are meargeable. 
        // To be meargeable they need to be separated by s sequence of closing, then opening
        // tags only.
        // And all tags must be Sections/Lists/ListItems only.
        internal static bool ParagraphsAreMergeable(Block firstParagraphOrBlockUIContainer, Block secondParagraphOrBlockUIContainer) 
        {
            if (firstParagraphOrBlockUIContainer == null || secondParagraphOrBlockUIContainer == null || 
                firstParagraphOrBlockUIContainer == secondParagraphOrBlockUIContainer) 
            {
                return false; // nothing to merge 
            }

            TextPointer position = firstParagraphOrBlockUIContainer.ElementEnd;
            TextPointer startOfSecondParagraph = secondParagraphOrBlockUIContainer.ElementStart; 

            // Skip and check all closing tags (if any) 
            while (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd) 
            {
                if (!TextSchema.AllowsParagraphMerging(position.Parent.GetType())) 
                {
                    return false; // Crossing hard-structured element. Paragraphs are not meargeable.
                }
 
                position = position.GetNextContextPosition(LogicalDirection.Forward);
            } 
 
            // Skip and check all opening tags (if any)
            while (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart) 
            {
                if (position.CompareTo(startOfSecondParagraph) == 0)
                {
                    // Successfully skipped all tags, and reached the seconfParagraph. 
                    // The paragraphs are meargeable.
                    return true; 
                } 

                position = position.GetNextContextPosition(LogicalDirection.Forward); 

                if (!TextSchema.AllowsParagraphMerging(position.Parent.GetType()))
                {
                    return false; // Crossing hardd-structured element. Paragraphs are not meargeable. 
                }
            } 
 
            // Non-tag run found. Paragraphs are not meargeable.
            return false; 
        }

        // Checks if start and end positions are parented by a List.
        // If so, unindents list items between (start - start's list end) or (end's list start - end) 
        // until they are parented by a top level list.
        // Then, if needed, splits the list(s) at start and/or end positions. 
        // Returns false if splitting is not successful due to a failing unindent operation on any nested lists. 
        internal static bool SplitListsForFlowDirectionChange(TextPointer start, TextPointer end, object newFlowDirectionValue)
        { 
            ListItem startListItem = start.GetListAncestor();

            // Unindent startListItem's list to prepare for a split, if the List's FlowDirection value is different.
            if (startListItem != null && 
                startListItem.List != null && // Check for unparented list items
                !TextSchema.ValuesAreEqual(/*newValue*/newFlowDirectionValue, /*currentValue*/startListItem.List.GetValue(Paragraph.FlowDirectionProperty))) 
            { 
                while (startListItem != null &&
                    startListItem.List != null && 
                    startListItem.List.Parent is ListItem)
                {
                    // startListItem is within a nested List.
                    if (!UnindentListItems(new TextRange(start, GetPositionAfterList(startListItem.List)))) 
                    {
                        return false; 
                    } 
                    startListItem = start.GetListAncestor();
                } 
            }

            ListItem endListItem = end.GetListAncestor();
 
            // Unindent endListItem's list to prepare for a split, if the List's FlowDirection value is different.
            if (endListItem != null && 
                endListItem.List != null && 
                !TextSchema.ValuesAreEqual(/*newValue*/newFlowDirectionValue, /*currentValue*/endListItem.List.GetValue(Paragraph.FlowDirectionProperty)))
            { 
                if (startListItem != null && startListItem.List != null &&
                    endListItem.List.ElementEnd.CompareTo(startListItem.List.ElementEnd) < 0)
                {
                    // endListItem's List is contained within startListItem's List. 
                    // No need to unindent endListItem.
                } 
                else 
                {
                    while (endListItem != null && 
                        endListItem.List !=  null &&
                        endListItem.List.Parent is ListItem)
                    {
                        // endListItem is within a nested List. 
                        if (!UnindentListItems(new TextRange(endListItem.List.ContentStart, GetPositionAfterList(endListItem.List))))
                        { 
                            return false; 
                        }
                        endListItem = end.GetListAncestor(); 
                    }
                }
            }
 
            // Split list(s) at boundary position(s) if
            //  1. startListItem is not the first list item within its list (or endListItem is not the last one) 
            //  and 
            //  2. start/end's parent List's flow direction value is different than the new value being set
 
            if ((startListItem = start.GetListAncestor()) != null && startListItem.PreviousListItem != null &&
                startListItem.List != null && // Check for unparented list items
                (!TextSchema.ValuesAreEqual(/*newValue*/newFlowDirectionValue, /*currentValue*/startListItem.List.GetValue(Paragraph.FlowDirectionProperty))))
            { 
                Invariant.Assert(!(startListItem.List.Parent is ListItem), "startListItem's list must not be nested!");
                TextRangeEdit.SplitElement(startListItem.ElementStart); 
            } 

            if ((endListItem = end.GetListAncestor()) != null && 
                endListItem.List != null && // Check for unparented list items
                (!TextSchema.ValuesAreEqual(/*newValue*/newFlowDirectionValue, /*currentValue*/endListItem.List.GetValue(Paragraph.FlowDirectionProperty))))
            {
                // Walk up from endListItem to find the topmost listitem that contains it. 
                if (endListItem.List.Parent is ListItem)
                { 
                    while (endListItem.List != null && endListItem.List.Parent is ListItem) 
                    {
                        endListItem = (ListItem)endListItem.List.Parent; 
                    }
                }
                if (endListItem.List != null && endListItem.NextListItem != null)
                { 
                    Invariant.Assert(!(endListItem.List.Parent is ListItem), "endListItem's list must not be nested!");
                    TextRangeEdit.SplitElement(endListItem.ElementEnd); 
                } 
            }
 
            return true;
        }

        // Finds an insertion position after the list 
        private static TextPointer GetPositionAfterList(List list)
        { 
            Invariant.Assert(list != null, "list cannot be null"); 

            TextPointer adjustedEnd = list.ElementEnd.GetInsertionPosition(LogicalDirection.Backward); 
            if (adjustedEnd != null)
            {
                adjustedEnd = adjustedEnd.GetNextInsertionPosition(LogicalDirection.Forward);
            } 

            if (adjustedEnd == null) 
            { 
                adjustedEnd = list.ElementEnd.TextContainer.End;
            } 

            if (TextRangeEditTables.IsTableStructureCrossed(list.ElementEnd, adjustedEnd))
            {
                adjustedEnd = list.ContentEnd; 
            }
 
            return adjustedEnd; 
        }
 
        #endregion Internal Methods
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------------------- 
//
// File TextRangeEditLists.cs
//
// Copyright (C) Microsoft Corporation.  All rights reserved. 
//
// Description: Internal static class representing a group of methods 
//              for list editing 
//
//--------------------------------------------------------------------------- 

namespace System.Windows.Documents
{
    using System; 
    using MS.Internal;
    using System.Windows.Controls; 
 
    /// 
    /// The TextRange class represents a pair of TextPositions, with many 
    /// rich text editing operations exposed.
    /// 
    internal static class TextRangeEditLists
    { 
        // -------------------------------------------------------------------
        // 
        // Internal Methods 
        //
        // ------------------------------------------------------------------- 

        #region Internal Methods

        ///  
        /// Merges two paragraphs followinng one another.
        /// The content of a second paragraph is moved into the end 
        /// of the first one. 
        /// 
        ///  
        /// First of two merged paragraphs or BlockUIContainer.
        /// 
        /// 
        /// Second of two mered paragraphs or BlockUIContainer. 
        /// 
        ///  
        /// true if paragraphs have been merged; false if no actions where made. 
        /// 
        internal static bool MergeParagraphs(Block firstParagraphOrBlockUIContainer, Block secondParagraphOrBlockUIContainer) 
        {
            if (!ParagraphsAreMergeable(firstParagraphOrBlockUIContainer, secondParagraphOrBlockUIContainer))
            {
                return false; // Cannot mearge these paragraphs. 
            }
 
            // Store parent list item of a second paragraph - 
            // to correct its structure after the merge
            ListItem secondListItem = secondParagraphOrBlockUIContainer.PreviousBlock == null ? secondParagraphOrBlockUIContainer.Parent as ListItem : null; 

            if (secondListItem != null && secondListItem.PreviousListItem == null && secondParagraphOrBlockUIContainer.NextBlock is List)
            {
                // The second paragraph is a first list item in some list. 
                // It has a sublists in it, so this sublist must be unindented
                // to avoid double bulleted line. 
                List sublistOfSecondParagraph = (List)secondParagraphOrBlockUIContainer.NextBlock; 
                if (sublistOfSecondParagraph.ElementEnd.CompareTo(secondListItem.ContentEnd) == 0)
                { 
                    secondListItem.Reposition(null, null);
                }
                else
                { 
                    secondListItem.Reposition(sublistOfSecondParagraph.ElementEnd, secondListItem.ContentEnd);
                } 
                // At this point the schema is temporaty broken: the secondParagraph and the sublistOfSecondParagraph have List as a parent 
                sublistOfSecondParagraph.Reposition(null, null);
                // The schema is repared as to sublistOfSecondParagraph concern, but still broken for secondParagraph - must be corrected in the following code 
            }

            // Move the second paragraph out of its wrappers separating from the first paragraph (if any).
            // We can not use RepositionWithContent because it would destroy 
            // all pointers and ranges within a moved paragraph.
            // Instead we reposition elements around the two paragraphs. 
            while (secondParagraphOrBlockUIContainer.ElementStart.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart) 
            {
                TextElement parentBlock = (TextElement)secondParagraphOrBlockUIContainer.Parent; 
                Invariant.Assert(parentBlock != null);
                Invariant.Assert(TextSchema.AllowsParagraphMerging(parentBlock.GetType()));

                if (secondParagraphOrBlockUIContainer.ElementEnd.CompareTo(parentBlock.ContentEnd) == 0) 
                {
                    // Remove ancestor block if it becomes empty 
                    parentBlock.Reposition(null, null); 
                }
                else 
                {
                    // Move ancestor's Start after the end of our paragraph
                    parentBlock.Reposition(secondParagraphOrBlockUIContainer.ElementEnd, parentBlock.ContentEnd);
                } 
            }
 
            // Store a position after the second paragraph where list merging may be needed 
            TextPointer positionAfterSecondParagraph = secondParagraphOrBlockUIContainer.ElementEnd.GetFrozenPointer(LogicalDirection.Forward);
 
            // Move the second paragraph to become an immediate following sibling of the first paragraph
            while (true)
            {
                TextElement previousBlock = secondParagraphOrBlockUIContainer.ElementStart.GetAdjacentElement(LogicalDirection.Backward) as TextElement; 
                // Note: We cannot use Block.NextSibling property, because the structure is invalid during this process
 
                Invariant.Assert(previousBlock != null); 

                if (previousBlock is Paragraph || previousBlock is BlockUIContainer) 
                {
                    break;
                }
 
                Invariant.Assert(TextSchema.AllowsParagraphMerging(previousBlock.GetType()));
                previousBlock.Reposition(previousBlock.ContentStart, secondParagraphOrBlockUIContainer.ElementEnd); 
            } 

            // Now that paragraphs are next to each other merge them. 

            // If one of paragraphs is empty we will apply special logic - to preserve a formatting from a non-empty one
            if (secondParagraphOrBlockUIContainer.TextRange.IsEmpty)
            { 
                secondParagraphOrBlockUIContainer.RepositionWithContent(null);
            } 
            else if (firstParagraphOrBlockUIContainer.TextRange.IsEmpty) 
            {
                firstParagraphOrBlockUIContainer.RepositionWithContent(null); 
            }
            else if (firstParagraphOrBlockUIContainer is Paragraph && secondParagraphOrBlockUIContainer is Paragraph)
            {
                // Do reposition magic for merging paragraph content 
                // without destroying any pointers positioned in them.
                // Pull the second paragraph into the first one 
                Invariant.Assert(firstParagraphOrBlockUIContainer.ElementEnd.CompareTo(secondParagraphOrBlockUIContainer.ElementStart) == 0); 
                firstParagraphOrBlockUIContainer.Reposition(firstParagraphOrBlockUIContainer.ContentStart, secondParagraphOrBlockUIContainer.ElementEnd);
 
                // Store inline merging position
                TextPointer inlineMergingPosition = secondParagraphOrBlockUIContainer.ElementStart;

                // Now we can delete the second paragraph 
                secondParagraphOrBlockUIContainer.Reposition(null, null);
 
                // Merge formatting elements at the point of paragraphs merging 
                TextRangeEdit.MergeFormattingInlines(inlineMergingPosition);
            } 

            // Merge ListItems wrapping first and second paragraphs.
            ListItem followingListItem = positionAfterSecondParagraph.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart
                ? positionAfterSecondParagraph.GetAdjacentElement(LogicalDirection.Forward) as ListItem : null; 
            if (followingListItem != null && followingListItem == secondListItem)
            { 
                ListItem precedingListItem = positionAfterSecondParagraph.GetAdjacentElement(LogicalDirection.Backward) as ListItem; 
                if (precedingListItem != null)
                { 
                    // Merge the second list item with the preceding one
                    Invariant.Assert(positionAfterSecondParagraph.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart);
                    Invariant.Assert(positionAfterSecondParagraph.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementEnd);
                    precedingListItem.Reposition(precedingListItem.ContentStart, followingListItem.ElementEnd); 
                    followingListItem.Reposition(null, null);
                } 
            } 

            // Merge lists at merge position 
            MergeLists(positionAfterSecondParagraph);

            return true;
        } 

        ///  
        /// Like MergeLists, but will search over formatting elements when 
        /// looking for Lists to merge.
        ///  
        internal static bool MergeListsAroundNormalizedPosition(TextPointer mergePosition)
        {
            // Search forward for a List to merge with.
            TextPointer navigator = mergePosition.CreatePointer(); 

            while (navigator.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd) 
            { 
                navigator.MoveToNextContextPosition(LogicalDirection.Forward);
            } 

            bool merged = MergeLists(navigator);

            // Search backward for a List to merge with. 
            if (!merged)
            { 
                navigator.MoveToPosition(mergePosition); 

                while (navigator.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart) 
                {
                    navigator.MoveToNextContextPosition(LogicalDirection.Backward);
                }
 
                merged = MergeLists(navigator);
            } 
 
            return merged;
        } 

        /// 
        /// Merges two naighboring lists ending and starting at position mergePosition
        ///  
        /// 
        /// Position at with two List elements are expected to appear next to each other 
        ///  
        /// 
        /// true if there were two mergeable List elements and merge happened. 
        /// false if there is no pair of List elements at the mergePosition.
        /// 
        internal static bool MergeLists(TextPointer mergePosition)
        { 
            if (mergePosition.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.ElementEnd ||
                mergePosition.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.ElementStart) 
            { 
                return false;
            } 

            List precedingList = mergePosition.GetAdjacentElement(LogicalDirection.Backward) as List;
            List followingList = mergePosition.GetAdjacentElement(LogicalDirection.Forward) as List;
 
            if (precedingList == null || followingList == null)
            { 
                return false; 
            }
 
            precedingList.Reposition(precedingList.ContentStart, followingList.ElementEnd);
            followingList.Reposition(null, null);

            // We need to set appropriate FlowDirection property on the new List and its paragraph children. 
            // We take the FlowDirection value from the preceding list.
            TextRangeEdit.SetParagraphProperty(precedingList.ElementStart, precedingList.ElementEnd, 
                Paragraph.FlowDirectionProperty, precedingList.GetValue(Paragraph.FlowDirectionProperty)); 

            return true; 
        }

        // Returns true if all paragraphs in a range belong to the same block,
        // so they can be easily grouped and ungrouped. 
        internal static bool IsListOperationApplicable(TextRange range)
        { 
            // First check, if range start/end are parented by ListItems within the same parent list. 
            if (IsRangeWithinSingleList(range))
            { 
                return true;
            }

            // Adjust range end so that it does not affect a following paragraph. 
            TextPointer end = (TextPointer)TextRangeEdit.GetAdjustedRangeEnd(range.Start, range.End);
 
            // Now try plain Paragraphs 
            Block firstBlock = range.Start.ParagraphOrBlockUIContainer;
            Block lastBlock = end.ParagraphOrBlockUIContainer; 

            if (firstBlock != null && lastBlock != null && firstBlock.Parent == lastBlock.Parent)
            {
                return true; 
            }
 
            // Allow list editing at potential paragraph positions, this includes 
            // positions in initial RichTextBox, empty TableCell, empty ListItem where paragraphs were not yet created.
            if (range.IsEmpty && TextPointerBase.IsAtPotentialParagraphPosition(range.Start)) 
            {
                return true;
            }
 
            return false;
        } 
 
        internal static bool ConvertParagraphsToListItems(TextRange range, TextMarkerStyle markerStyle)
        { 
            if (range.IsEmpty && TextPointerBase.IsAtPotentialParagraphPosition(range.Start))
            {
                TextPointer insertionPosition = TextRangeEditTables.EnsureInsertionPosition(range.Start);
                ((ITextRange)range).Select(insertionPosition, insertionPosition); 
            }
 
            Block firstBlock = range.Start.ParagraphOrBlockUIContainer; 

            TextPointer end = (TextPointer)TextRangeEdit.GetAdjustedRangeEnd(range.Start, range.End); 
            Block lastBlock = end.ParagraphOrBlockUIContainer;

            // We assume that a range contains a sequence of one-level paragraphs.
            // Otherwise the operation is disabled. 
            if (firstBlock == null || lastBlock == null || firstBlock.Parent != lastBlock.Parent ||
                firstBlock.Parent is ListItem && firstBlock.PreviousBlock == null) 
            { 
                // Either the paragraphs belong to different scopes or first of them has a bullet already.
                // We cannot convert them into bulleted lists. 
                return false;
            }

            // Check that all top-level elements of selection are Paragraphs. 
            // We do not apply the command to Tables or Sections.
            for (Block block = firstBlock; block != lastBlock && block != null; block = block.NextBlock) 
            { 
                if (block is Table || block is Section)
                { 
                    return false;
                }
            }
 
            ListItem parentListItem = firstBlock.Parent as ListItem;
            if (parentListItem != null) 
            { 
                // Paragraphs are inside of ListItem already.
 
                // Split a current ListItem before each of selected blocks
                Block block = firstBlock;
                while (block != null)
                { 
                    Block nextBlock = block == lastBlock ? null : block.ElementEnd.GetAdjacentElement(LogicalDirection.Forward) as Block;
 
                    Invariant.Assert(block.Parent is ListItem); 
                    TextRangeEdit.SplitElement(block.ElementStart);
 
                    block = nextBlock;
                }
            }
            else 
            {
                // Create a list around all paragraphs 
                List list = new List(); 
                list.MarkerStyle = markerStyle;
                list.Apply(firstBlock, lastBlock); 

                // Merge with neighboring lists
                //MergeLists(list.ElementEnd);  // start with End to not loose an instance of "list" during merging with the following list
                //MergeLists(list.ElementStart); 
            }
 
            return true; 
        }
 
        // Assumes that a range contains a sequence of same-level ListItems.
        // Converts all these ListItems into Paragraphs and
        // either adds them to preceding ListItem (as non-bulleted continuation)
        // or pulls them out of a List if they start in the beginning of a List 
        internal static void ConvertListItemsToParagraphs(TextRange range)
        { 
            ListItem firstListItem = TextPointerBase.GetListItem(range.Start); 
            ListItem lastListItem = TextPointerBase.GetListItem((TextPointer)TextRangeEdit.GetAdjustedRangeEnd(range.Start, range.End));
 
            // The range must be in a sequence of ListItems belonging to one List wrapper
            if (firstListItem == null || lastListItem == null || firstListItem.Parent != lastListItem.Parent || !(firstListItem.Parent is List))
            {
                return; 
            }
 
            List listToRemove = null; 

            ListItem leadingListItem = firstListItem.PreviousListItem; 
            if (leadingListItem != null)
            {
                // We have a leading ListItem, so pull selected items into it
                leadingListItem.Reposition(leadingListItem.ContentStart, lastListItem.ElementEnd); 
            }
            else 
            { 
                // We do not have a leading ListItem. So pull selected items out of a list
 
                // Cut wrapping list after endListItem
                if (lastListItem.NextListItem != null)
                {
                    TextRangeEdit.SplitElement(lastListItem.ElementEnd); 
                }
 
                // Set list to remove 
                listToRemove = firstListItem.List;
            } 

            // Remove ListItems from all selected blocks
            ListItem listItem = firstListItem;
            while (listItem != null) 
            {
                ListItem nextListItem = listItem.ElementEnd.GetAdjacentElement(LogicalDirection.Forward) as ListItem; 
 
                // If this is an empty , insert an explicit paragraph in it before deleting the list item.
                if (listItem.ContentStart.CompareTo(listItem.ContentEnd) == 0) 
                {
                    TextRangeEditTables.EnsureInsertionPosition(listItem.ContentStart);
                }
 
                listItem.Reposition(null, null);
                listItem = listItem == lastListItem ? null : nextListItem; 
            } 

            // If we have a list to remove, remove it and set its FlowDirection to its children 
            if (listToRemove != null)
            {
                FlowDirection flowDirection = (FlowDirection)listToRemove.GetValue(Paragraph.FlowDirectionProperty);
                listToRemove.Reposition(null, null); 
                TextRangeEdit.SetParagraphProperty(range.Start, range.End, Paragraph.FlowDirectionProperty, flowDirection);
            } 
        } 

        internal static void IndentListItems(TextRange range) 
        {
            ListItem firstListItem = TextPointerBase.GetImmediateListItem(range.Start);
            ListItem lastListItem = TextPointerBase.GetImmediateListItem((TextPointer)TextRangeEdit.GetAdjustedRangeEnd(range.Start, range.End));
 
            // The range must be in a sequence of ListItems belonging to one List wrapper
            if (firstListItem == null || lastListItem == null || 
                firstListItem.Parent != lastListItem.Parent || 
                !(firstListItem.Parent is List))
            { 
                return;
            }

            // Identify a ListItem which will become a leading item for this potential sublist 
            ListItem leadingListItem = firstListItem.PreviousListItem;
            if (leadingListItem == null) 
            { 
                // There is no leading list item for this group. Indentation is impossible
                return; 
            }

            // Get current List
            List list = (List)firstListItem.Parent; 

            // Wrap these items into a List - inheriting all properties from our current list 
            List indentedList = (List)TextRangeEdit.InsertElementClone(firstListItem.ElementStart, lastListItem.ElementEnd, list); 

            // Wrap the leading ListItem to include the sublist 
            leadingListItem.Reposition(leadingListItem.ContentStart, indentedList.ElementEnd);

            // Unwrap sublist from the last selected list item (to keep it on its level)
            Paragraph leadingParagraphOfLastItem = lastListItem.Blocks.FirstBlock as Paragraph; 
            if (leadingParagraphOfLastItem != null)
            { 
                // Unindenting all items of a sublist - if it is the only following element of a list 
                List nestedListOfLastItem = leadingParagraphOfLastItem.NextBlock as List;
                if (nestedListOfLastItem != null && nestedListOfLastItem.NextBlock == null) 
                {
                    lastListItem.Reposition(lastListItem.ContentStart, nestedListOfLastItem.ElementStart);
                    nestedListOfLastItem.Reposition(null, null);
                } 
            }
 
            // Merge with neighboring lists 
            MergeLists(indentedList.ElementStart);
            // No need in merging at nestedList.ElementEnd as ListItem ends there. 
        }

        internal static bool UnindentListItems(TextRange range)
        { 
            // If listitems in this range cross a list boundary, we cannot unindent them.
            if (!IsRangeWithinSingleList(range)) 
            { 
                return false;
            } 

            ListItem firstListItem = TextPointerBase.GetListItem(range.Start);
            ListItem lastListItem = TextPointerBase.GetListItem((TextPointer)TextRangeEdit.GetAdjustedRangeEnd(range.Start, range.End));
 
            // At this point it is possible that lastListItem is a child of
            // firstListItem. 
            // 
            // This is due to a special case in IsRangeWithinSingleList
            // which allows the input TextRange to cross List boundaries only 
            // in the case where the TextRange ends adjacent to an insertion
            // position at the same level as the start, e.g.:
            //
            //    - parent item 
            //      - start item (range starts here)
            //          - child item (range ends here) 
            //       
            //
            // Here we check for that special case and ensure that 
            // lastListItem is at the same level as firstListItem.

            TextElement parent = (TextElement)lastListItem.Parent;
 
            while (parent != firstListItem.Parent)
            { 
                lastListItem = parent as ListItem; 
                parent = (TextElement)parent.Parent;
            } 
            if (lastListItem == null)
            {
                // This can happen if the input is a fragment, a collection
                // of ListItems not parented by an outer List. 
                return false;
            } 
 
            // Cut wrapping list before startListItem
            if (firstListItem.PreviousListItem != null) 
            {
                TextRangeEdit.SplitElement(firstListItem.ElementStart);
            }
 
            // Cut wrapping list after endListItem
            if (lastListItem.NextListItem != null) 
            { 
                TextRangeEdit.SplitElement(lastListItem.ElementEnd);
            } 

            // Remove List wrapper from selected items
            List unindentedList = (List)firstListItem.Parent;
 
            // Check whether we have outer ListItem
            ListItem outerListItem = unindentedList.Parent as ListItem; 
            if (outerListItem != null) 
            {
                // Selected items belong to a nested list. 
                // So we need to pull them to the level of enclosing ListItem, i.e. cut this ListItem.
                // In this case we also need to include trailing list into the last of selected items

                // Remove a wrapping List from selected items 
                unindentedList.Reposition(null, null);
 
                // Remember end position of outerListItem to pull any trailing list or other blocks into the last of selected listitems 
                TextPointer outerListItemEnd = outerListItem.ContentEnd;
 
                if (outerListItem.ContentStart.CompareTo(firstListItem.ElementStart) == 0)
                {
                    // There is nothing before first list item; so outer list item would be empty - just delete it
                    outerListItem.Reposition(null, null); 
                }
                else 
                { 
                    // Wrap all stuff preceding firstListItem in outerListItem
                    outerListItem.Reposition(outerListItem.ContentStart, firstListItem.ElementStart); 
                }

                if (outerListItemEnd.CompareTo(lastListItem.ElementEnd) == 0)
                { 
                    // There are no following siblings to pull into last selected item; do nothing.
                } 
                else 
                {
                    // Pull trailing items (following siblings to the selected ones) into the last selected item 

                    // Remember a position to merge any trailing list after lastListItem
                    TextPointer mergePosition = lastListItem.ContentEnd;
 
                    // Reposition last selectd ListItem so that it includes trailing list (or other block) as its children
                    lastListItem.Reposition(lastListItem.ContentStart, outerListItemEnd); 
 
                    // Merge any trailing list with a sublist outdented with our listitem
                    MergeLists(mergePosition); 
                }
            }
            else
            { 
                // Selected items are not in nested list.
                // We need to simply unwrap them and convert to paragraphs 
 
                TextPointer start = unindentedList.ElementStart;
                TextPointer end = unindentedList.ElementEnd; 

                // Save the list's FlowDirection value, to apply later to its children.
                object listFlowDirectionValue = unindentedList.GetValue(Paragraph.FlowDirectionProperty);
 
                // Remove a wrapping List from selected items
                unindentedList.Reposition(null, null); 
 
                // Remove ListItems from all selected items
                ListItem listItem = firstListItem; 
                while (listItem != null)
                {
                    ListItem nextListItem = listItem.ElementEnd.GetAdjacentElement(LogicalDirection.Forward) as ListItem;
 
                    // If this is an empty , insert an explicit paragraph in it before deleting the list item.
                    if (listItem.ContentStart.CompareTo(listItem.ContentEnd) == 0) 
                    { 
                        TextRangeEditTables.EnsureInsertionPosition(listItem.ContentStart);
                    } 

                    listItem.Reposition(null, null);
                    listItem = listItem == lastListItem ? null : nextListItem;
                } 

                // Apply FlowDirection of the list just deleted to all its children. 
                TextRangeEdit.SetParagraphProperty(start, end, Paragraph.FlowDirectionProperty, listFlowDirectionValue); 

                // Merge lists on boundaries 
                MergeLists(start);
                MergeLists(end);
            }
 
            return true;
        } 
 
        // Predicate which returns true if list items in this range are within the scope of the same parent list.
        private static bool IsRangeWithinSingleList(TextRange range) 
        {
            ListItem startListItem = TextPointerBase.GetListItem(range.Start);

            // Adjust range end so that it does not affect a following paragraph. 
            TextPointer end = (TextPointer)TextRangeEdit.GetAdjustedRangeEnd(range.Start, range.End);
            ListItem endListItem = TextPointerBase.GetListItem(end); 
 
            // Check if the ListItems belong to one List wrapper.
            if (startListItem != null && endListItem != null && startListItem.Parent == endListItem.Parent) 
            {
                return true;
            }
 
            // In case of nested lists, it may be the case that start and end list item do not belong to one list wrapper,
            // yet no visual list boundary is crossed. 
            // e.g. 
            //  * aa
            //      * bb 
            //      * cc
            // Special case so that list operations are applicable in this scenario.
            if (startListItem != null && endListItem != null)
            { 
                while (end.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd)
                { 
                    if (end.Parent == startListItem.Parent) 
                    {
                        return true; 
                    }
                    end = end.GetNextContextPosition(LogicalDirection.Forward);
                }
            } 

            return false; 
        } 

        // Checks whether two paragraphs are meargeable. 
        // To be meargeable they need to be separated by s sequence of closing, then opening
        // tags only.
        // And all tags must be Sections/Lists/ListItems only.
        internal static bool ParagraphsAreMergeable(Block firstParagraphOrBlockUIContainer, Block secondParagraphOrBlockUIContainer) 
        {
            if (firstParagraphOrBlockUIContainer == null || secondParagraphOrBlockUIContainer == null || 
                firstParagraphOrBlockUIContainer == secondParagraphOrBlockUIContainer) 
            {
                return false; // nothing to merge 
            }

            TextPointer position = firstParagraphOrBlockUIContainer.ElementEnd;
            TextPointer startOfSecondParagraph = secondParagraphOrBlockUIContainer.ElementStart; 

            // Skip and check all closing tags (if any) 
            while (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd) 
            {
                if (!TextSchema.AllowsParagraphMerging(position.Parent.GetType())) 
                {
                    return false; // Crossing hard-structured element. Paragraphs are not meargeable.
                }
 
                position = position.GetNextContextPosition(LogicalDirection.Forward);
            } 
 
            // Skip and check all opening tags (if any)
            while (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart) 
            {
                if (position.CompareTo(startOfSecondParagraph) == 0)
                {
                    // Successfully skipped all tags, and reached the seconfParagraph. 
                    // The paragraphs are meargeable.
                    return true; 
                } 

                position = position.GetNextContextPosition(LogicalDirection.Forward); 

                if (!TextSchema.AllowsParagraphMerging(position.Parent.GetType()))
                {
                    return false; // Crossing hardd-structured element. Paragraphs are not meargeable. 
                }
            } 
 
            // Non-tag run found. Paragraphs are not meargeable.
            return false; 
        }

        // Checks if start and end positions are parented by a List.
        // If so, unindents list items between (start - start's list end) or (end's list start - end) 
        // until they are parented by a top level list.
        // Then, if needed, splits the list(s) at start and/or end positions. 
        // Returns false if splitting is not successful due to a failing unindent operation on any nested lists. 
        internal static bool SplitListsForFlowDirectionChange(TextPointer start, TextPointer end, object newFlowDirectionValue)
        { 
            ListItem startListItem = start.GetListAncestor();

            // Unindent startListItem's list to prepare for a split, if the List's FlowDirection value is different.
            if (startListItem != null && 
                startListItem.List != null && // Check for unparented list items
                !TextSchema.ValuesAreEqual(/*newValue*/newFlowDirectionValue, /*currentValue*/startListItem.List.GetValue(Paragraph.FlowDirectionProperty))) 
            { 
                while (startListItem != null &&
                    startListItem.List != null && 
                    startListItem.List.Parent is ListItem)
                {
                    // startListItem is within a nested List.
                    if (!UnindentListItems(new TextRange(start, GetPositionAfterList(startListItem.List)))) 
                    {
                        return false; 
                    } 
                    startListItem = start.GetListAncestor();
                } 
            }

            ListItem endListItem = end.GetListAncestor();
 
            // Unindent endListItem's list to prepare for a split, if the List's FlowDirection value is different.
            if (endListItem != null && 
                endListItem.List != null && 
                !TextSchema.ValuesAreEqual(/*newValue*/newFlowDirectionValue, /*currentValue*/endListItem.List.GetValue(Paragraph.FlowDirectionProperty)))
            { 
                if (startListItem != null && startListItem.List != null &&
                    endListItem.List.ElementEnd.CompareTo(startListItem.List.ElementEnd) < 0)
                {
                    // endListItem's List is contained within startListItem's List. 
                    // No need to unindent endListItem.
                } 
                else 
                {
                    while (endListItem != null && 
                        endListItem.List !=  null &&
                        endListItem.List.Parent is ListItem)
                    {
                        // endListItem is within a nested List. 
                        if (!UnindentListItems(new TextRange(endListItem.List.ContentStart, GetPositionAfterList(endListItem.List))))
                        { 
                            return false; 
                        }
                        endListItem = end.GetListAncestor(); 
                    }
                }
            }
 
            // Split list(s) at boundary position(s) if
            //  1. startListItem is not the first list item within its list (or endListItem is not the last one) 
            //  and 
            //  2. start/end's parent List's flow direction value is different than the new value being set
 
            if ((startListItem = start.GetListAncestor()) != null && startListItem.PreviousListItem != null &&
                startListItem.List != null && // Check for unparented list items
                (!TextSchema.ValuesAreEqual(/*newValue*/newFlowDirectionValue, /*currentValue*/startListItem.List.GetValue(Paragraph.FlowDirectionProperty))))
            { 
                Invariant.Assert(!(startListItem.List.Parent is ListItem), "startListItem's list must not be nested!");
                TextRangeEdit.SplitElement(startListItem.ElementStart); 
            } 

            if ((endListItem = end.GetListAncestor()) != null && 
                endListItem.List != null && // Check for unparented list items
                (!TextSchema.ValuesAreEqual(/*newValue*/newFlowDirectionValue, /*currentValue*/endListItem.List.GetValue(Paragraph.FlowDirectionProperty))))
            {
                // Walk up from endListItem to find the topmost listitem that contains it. 
                if (endListItem.List.Parent is ListItem)
                { 
                    while (endListItem.List != null && endListItem.List.Parent is ListItem) 
                    {
                        endListItem = (ListItem)endListItem.List.Parent; 
                    }
                }
                if (endListItem.List != null && endListItem.NextListItem != null)
                { 
                    Invariant.Assert(!(endListItem.List.Parent is ListItem), "endListItem's list must not be nested!");
                    TextRangeEdit.SplitElement(endListItem.ElementEnd); 
                } 
            }
 
            return true;
        }

        // Finds an insertion position after the list 
        private static TextPointer GetPositionAfterList(List list)
        { 
            Invariant.Assert(list != null, "list cannot be null"); 

            TextPointer adjustedEnd = list.ElementEnd.GetInsertionPosition(LogicalDirection.Backward); 
            if (adjustedEnd != null)
            {
                adjustedEnd = adjustedEnd.GetNextInsertionPosition(LogicalDirection.Forward);
            } 

            if (adjustedEnd == null) 
            { 
                adjustedEnd = list.ElementEnd.TextContainer.End;
            } 

            if (TextRangeEditTables.IsTableStructureCrossed(list.ElementEnd, adjustedEnd))
            {
                adjustedEnd = list.ContentEnd; 
            }
 
            return adjustedEnd; 
        }
 
        #endregion Internal Methods
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK