/// <summary>
        /// Adjust the start and end of the range to match the offset/length, in characters.
        /// if the offset/length adjustment fails to produce the expected value,
        /// then the adjustment is cancelled and false is returned.
        /// </summary>
        public static bool AdjustMarkupRange(ref IHTMLTxtRange stagingTextRange, MarkupRange range, int offset, int length)
        {
            string currentText = GetRangeTextFast(range) ?? "";

            if (offset == 0 && length == currentText.Length)
            {
                return(true);
            }

            string expectedText;

            try
            {
                expectedText = currentText.Substring(offset, length);
            }
            catch (ArgumentOutOfRangeException)
            {
                return(false);
            }

            MarkupRange testRange = range.Clone();

            AdjustMarkupRangeCore(testRange, offset, length, currentText);
            if (GetRangeTextFast(testRange) != expectedText)
            {
                return(false);
            }

            range.MoveToRange(testRange);
            return(true);
        }
        public static void RelinquishTxtRange(IHTMLTxtRange txtRange, MarkupRange range)
        {
            if (cache == null)
            {
                return;
            }

            try
            {
                lock (cache.SyncRoot)
                {
                    Queue queue = (Queue)cache[GetDocumentKey(range.MarkupServices.MarkupServicesRaw)];

                    if (queue != null)
                    {
                        lock (queue.SyncRoot)
                        {
                            queue.Enqueue(txtRange);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Failure in IHTMLTxtRangePool: " + ex);
                cache = null;
            }
        }
 public static String ToFormattedHtml(MshtmlMarkupServices markupServices, MarkupRange bounds)
 {
     StringBuilder sb = new StringBuilder();
     HtmlWriter xmlWriter = new HtmlWriter(sb);
     PrintHtml(xmlWriter, markupServices, bounds);
     return sb.ToString();
 }
Beispiel #4
0
        /// <summary>
        /// Find the most logic direction to move the cursor so that it matches the where has clicked
        /// </summary>
        /// <param name="Selection"></param>
        /// <returns></returns>
        public static Direction FindSelectionToLogicalPosition(MarkupRange Selection, IHTMLElement body, bool?forward)
        {
            // There is a selection, not just a click
            if (!Selection.Start.IsEqualTo(Selection.End))
            {
                return(Direction.None);
            }

            Direction dir = ImageBreakout(Selection.Start);

            if (dir != Direction.None)
            {
                return(dir);
            }

            MarkupContext contextRight = Selection.Start.Right(false);
            MarkupContext contextLeft  = Selection.Start.Left(false);

            // there is text or some other type of content around the click
            if (!((contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope || contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope) &&
                  (contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope || contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope)))
            {
                return(Direction.None);
            }

            // The click is not between two block elements, so it should be fine where it is
            if (!ElementFilters.IsBlockElement(contextLeft.Element) || !ElementFilters.IsBlockElement(contextRight.Element))
            {
                return(Direction.None);
            }

            // </blockElement>|</postBody>
            if (contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope &&
                !IsSmartContent(contextLeft) &&
                contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope &&
                contextRight.Element.id == body.id)
            {
                return(Direction.Left);
            }

            // <postBody>|<blockElement>
            if (contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope &&
                !IsSmartContent(contextRight) &&
                contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope &&
                contextLeft.Element.id == body.id)
            {
                return(Direction.Right);
            }

            // </blockElement>|<blockElement>
            if (contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope &&
                !IsSmartContent(contextLeft) &&
                contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope &&
                !IsSmartContent(contextRight))
            {
                return(forward == null || forward == true ? Direction.Right : Direction.Left);
            }

            return(Direction.None);
        }
        private void Init(IHTMLDocument document, MshtmlMarkupServices markupServices, MarkupRange selectionRange, MarkupRangeFilter filter, DamageFunction damageFunction, bool expandRange)
        {
            // save references
            this.htmlDocument = document;
            this.markupServices = markupServices;
            this.selectionRange = selectionRange;
            this.filter = filter;
            this.damageFunction = damageFunction;

            // If the range is already the body, don't expand it or else it will be the whole document
            if (expandRange)
                ExpandRangeToWordBoundaries(selectionRange);

            // initialize pointer to beginning of selection range
            MarkupPointer wordStart = MarkupServices.CreateMarkupPointer(selectionRange.Start);
            MarkupPointer wordEnd = MarkupServices.CreateMarkupPointer(selectionRange.Start);

            //create the range for holding the current word.
            //Be sure to set its gravity so that it stays around text that get replaced.
            currentWordRange = MarkupServices.CreateMarkupRange(wordStart, wordEnd);
            currentWordRange.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left;
            currentWordRange.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right;

            currentVirtualPosition = currentWordRange.End.Clone();
        }
        public static string GetRangeHtmlFast(MarkupRange range)
        {
            IHTMLTxtRange stagingTextRange = IHTMLTxtRangePool.AquireTxtRange(range);
            string        returnValue      = UseStagingTextRange(ref stagingTextRange, range, rng => rng.htmlText);

            IHTMLTxtRangePool.RelinquishTxtRange(stagingTextRange, range);
            return(returnValue);
        }
Beispiel #7
0
        /// <summary>
        /// Insert html at the given pointer.
        /// </summary>
        /// <param name="html"></param>
        /// <param name="insertionPoint"></param>
        public void InsertHtml(string html, MarkupPointer insertionPoint)
        {
            MarkupRange content = CreateMarkupRange();

            ParseString(html, content.Start, content.End);

            Move(content.Start, content.End, insertionPoint);
        }
Beispiel #8
0
        /// <summary>
        /// Replaces an element with a new element while preserving the content inside the old element.
        /// </summary>
        /// <param name="oldElement"></param>
        /// <param name="newElement"></param>
        public void ReplaceElement(IHTMLElement oldElement, IHTMLElement newElement)
        {
            MarkupRange range = CreateMarkupRange();

            range.MoveToElement(oldElement, true);
            InsertElement(newElement, range.Start, range.End);
            RemoveElement(oldElement);
        }
        /// <summary>
        /// Walk through the markup range in reverse, letting the walker visit each position.
        /// </summary>
        /// <param name="walker">the delegate walking navigating the the markup range</param>
        /// <param name="inScopeElementsOnly">if true, enter/exit notifications about out-of-scope elements will be suppressed.</param>
        /// <returns></returns>
        public void WalkRangeReverse(MarkupRangeWalker walker, bool inScopeContextsOnly)
        {
            MarkupPointer p1 = MarkupServices.CreateMarkupPointer(End);
            MarkupPointer p2 = MarkupServices.CreateMarkupPointer(End);

            p1.Cling = false;
            p2.Cling = false;
            MarkupContext context         = new MarkupContext();
            bool          continueWalking = true;
            MarkupRange   currentRange    = null;

            while (continueWalking && p2.IsRightOf(Start))
            {
                string text      = null;
                bool   isInScope = true;

                p2.Left(true, context);
                currentRange = new MarkupRange(p2.Clone(), p1.Clone(), MarkupServices);

                if (inScopeContextsOnly)
                {
                    if (context.Element != null)
                    {
                        if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope)
                        {
                            p1.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);
                            isInScope = InRange(p1);
                        }
                        else if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope)
                        {
                            p1.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin);
                            isInScope = InRange(p1);
                        }
                    }
                    else if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text)
                    {
                        // It's possible part of the text is out of scope, so only return the in-scope text.
                        if (currentRange.Start.IsLeftOf(Start))
                        {
                            currentRange.Start.MoveToPointer(Start);
                        }
                    }
                }

                if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text)
                {
                    text = currentRange.Text;
                }

                if (!inScopeContextsOnly || isInScope)
                {
                    continueWalking = walker(currentRange, context, text);
                }

                p1.MoveToPointer(p2);
            }
        }
Beispiel #10
0
        /// <summary>
        /// Create a MarkupRange from a TextRange.
        /// </summary>
        /// <param name="textRange"></param>
        /// <returns></returns>
        public MarkupRange CreateMarkupRange(IHTMLTxtRange textRange)
        {
            MarkupPointer Begin = CreateMarkupPointer();
            MarkupPointer End   = CreateMarkupPointer();

            End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right;
            MovePointersToRange(textRange, Begin, End);
            MarkupRange markupRange = new MarkupRange(Begin, End, this);

            return(markupRange);
        }
        public static IHTMLTxtRange AquireTxtRange(MarkupRange range)
        {
            try
            {
                if (cache != null)
                {
                    int documentId = GetDocumentKey(range.MarkupServices.MarkupServicesRaw);

                    Queue         queue       = null;
                    IHTMLTxtRange returnRange = null;

                    lock (cache.SyncRoot)
                    {
                        queue = (Queue)cache[documentId];

                        if (queue == null)
                        {
                            queue = Queue.Synchronized(new Queue());
                            cache.Add(documentId, queue);
                        }

                        if (queue.Count > 0)
                        {
                            lock (queue.SyncRoot)
                            {
                                returnRange = (IHTMLTxtRange)queue.Dequeue();
                            }
                        }
                        else
                        {
                            returnRange = range.ToTextRange();
                        }
                    }

                    return(returnRange);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Failure in IHTMLTxtRangePool: " + ex);
                cache = null;
            }

            try
            {
                return(range.ToTextRange());
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Failure in IHTMLTxtRangePool: " + ex);
                return(null);
            }
        }
Beispiel #12
0
        /// <summary>
        /// Create a MarkupRange from that surrounds an Element.
        /// </summary>
        /// <returns></returns>
        public MarkupRange CreateMarkupRange(IHTMLElement element, bool outside)
        {
            _ELEMENT_ADJACENCY beginAdj = outside ? _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin : _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin;
            _ELEMENT_ADJACENCY endAdj   = outside ? _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd : _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd;
            MarkupPointer      Begin    = CreateMarkupPointer(element, beginAdj);
            MarkupPointer      End      = CreateMarkupPointer(element, endAdj);

            End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right;
            MarkupRange markupRange = new MarkupRange(Begin, End, this);

            return(markupRange);
        }
        /// <summary>
        /// Creates a clone that spans the same range as this MarkupRange.
        /// Note: The clone can be manipulated without changing the position of this range.
        /// </summary>
        /// <returns></returns>
        public MarkupRange Clone()
        {
            MarkupRange clone = MarkupServices.CreateMarkupRange();

            clone.Start.MoveToPointer(Start);
            clone.Start.Cling   = Start.Cling;
            clone.Start.Gravity = Start.Gravity;
            clone.End.MoveToPointer(End);
            clone.End.Cling   = End.Cling;
            clone.End.Gravity = End.Gravity;
            return(clone);
        }
        public TypographicCharacterHandler(MarkupRange currentSelection, InsertHtml insertHtml, IBlogPostImageEditingContext imageEditingContext, IHTMLElement postBodyElement, char c, string htmlText, MarkupPointer blockBoundary)
        {
            _currentSelection = currentSelection.Clone();
            _currentSelection.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right;
            _currentSelection.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left;
            _insertHtml = insertHtml;
            _imageEditingContext = imageEditingContext;
            _postBodyElement = postBodyElement;

            this.c = c;
            this.htmlText = htmlText;
            this.blockBoundary = blockBoundary;
        }
        private static void AdjustMarkupRangeCore(MarkupRange range, int offset, int length, string currentText)
        {
            MarkupPointer start = range.Start;
            MarkupPointer end   = range.End;

            if (offset > 0)
            {
                start.MoveToMarkupPosition(start.Container, start.MarkupPosition + offset);
            }
            if (length < (offset + currentText.Length))
            {
                end.MoveToMarkupPosition(start.Container, start.MarkupPosition + length);
            }
        }
        private static void PrintHtml(HtmlWriter writer, MshtmlMarkupServices MarkupServices, MarkupRange bounds)
        {
            //create a range to span a single position while walking the doc
            MarkupRange range = MarkupServices.CreateMarkupRange();
            range.Start.MoveToPointer(bounds.Start);
            range.End.MoveToPointer(bounds.Start);

            //create a context that can be reused while walking the document.
            MarkupContext context = new MarkupContext();

            //move the range.End to the right and print out each element along the way
            range.End.Right(true, context);
            while (context.Context != _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_None && range.Start.IsLeftOf(bounds.End))
            {
                string text = null;
                if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text)
                {
                    //if this is a text context, then get the text that is between the start and end points.
                    text = range.HtmlText;

                    //the range.HtmlText operation sometimes returns the outer tags for a text node,
                    //so we need to strip the tags.
                    //FIXME: if the Right/Left operations returned the available text value, this wouldn't be necessary.
                    if (text != null)
                        text = StripSurroundingTags(text);
                }
                else if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope)
                {
                    string htmlText = range.HtmlText;
                    if (context.Element.innerHTML == null && htmlText != null && htmlText.IndexOf("&nbsp;") != -1)
                    {
                        //HACK: Under these conditions, there was a was an invisible NBSP char in the
                        //document that is not detectable by walking through the document with MarkupServices.
                        //So, we force the text of the element to be the &nbsp; char to ensure that the
                        //whitespace that was visible in the editor is visible in the final document.
                        text = "&nbsp;";
                    }
                }

                //print the context.
                printContext(writer, context, text, range);

                //move the start element to the spot where the end currently is so tht there is
                //only ever a single difference in position
                range.Start.MoveToPointer(range.End);

                //move the end to the next position
                range.End.Right(true, context);
            }
        }
        private void ApplyBlockStyle(_ELEMENT_TAG_ID styleTagId, MarkupRange selection, MarkupRange maximumBounds, MarkupRange postOpSelection)
        {
            Debug.Assert(selection != maximumBounds, "selection and maximumBounds must be distinct objects");
            SelectionPositionPreservationCookie selectionPreservationCookie = null;

            //update the range cling and gravity so it will stick with the re-arranged block content
            selection.Start.PushCling(false);
            selection.Start.PushGravity(_POINTER_GRAVITY.POINTER_GRAVITY_Left);
            selection.End.PushCling(false);
            selection.End.PushGravity(_POINTER_GRAVITY.POINTER_GRAVITY_Right);

            try
            {
                if (selection.IsEmpty())
                {
                    //nothing is selected, so expand the selection to cover the entire parent block element
                    IHTMLElementFilter stopFilter =
                        ElementFilters.CreateCompoundElementFilter(ElementFilters.BLOCK_ELEMENTS,
                                                                   new IHTMLElementFilter(IsSplitStopElement));
                    MovePointerLeftUntilRegionBreak(selection.Start, stopFilter, maximumBounds.Start);
                    MovePointerRightUntilRegionBreak(selection.End, stopFilter, maximumBounds.End);
                }

                using (IUndoUnit undo = _editor.CreateSelectionUndoUnit(selection))
                {
                    selectionPreservationCookie = SelectionPositionPreservationHelper.Save(_markupServices, postOpSelection, selection);
                    if (selection.IsEmptyOfContent())
                    {
                        ApplyBlockFormatToEmptySelection(selection, styleTagId, maximumBounds);
                    }
                    else
                    {
                        ApplyBlockFormatToContentSelection(selection, styleTagId, maximumBounds);
                    }
                    undo.Commit();
                }
            }
            finally
            {
                selection.Start.PopCling();
                selection.Start.PopGravity();
                selection.End.PopCling();
                selection.End.PopGravity();
            }

            if (!SelectionPositionPreservationHelper.Restore(selectionPreservationCookie, selection, selection.Clone()))
                selection.ToTextRange().select();
        }
Beispiel #18
0
        public static void SplitBlockForInsertionOrBreakout(MshtmlMarkupServices markupServices, MarkupRange bounds, MarkupPointer insertAt)
        {
            IHTMLElement currentBlock = insertAt.GetParentElement(ElementFilters.BLOCK_OR_TABLE_CELL_ELEMENTS);

            if (currentBlock == null)
            {
                return;
            }

            if (ElementFilters.IsBlockQuoteElement(currentBlock) || ElementFilters.IsTableCellElement(currentBlock))
            {
                return;
            }


            MarkupPointer blockStart = markupServices.CreateMarkupPointer(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin);
            MarkupPointer blockEnd   = markupServices.CreateMarkupPointer(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);

            if (bounds != null && (blockStart.IsLeftOf(bounds.Start) || blockEnd.IsRightOf(bounds.End)))
            {
                return;
            }

            // Don't split if at the beginning or end of the visible content in the block.
            // Instead just move the insertion point outside the block.
            MarkupRange testRange = markupServices.CreateMarkupRange();

            testRange.Start.MoveToPointer(insertAt);
            testRange.End.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd);
            if (testRange.IsEmptyOfContent())
            {
                insertAt.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);
                return;
            }
            testRange.Start.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin);
            testRange.End.MoveToPointer(insertAt);
            if (testRange.IsEmptyOfContent())
            {
                insertAt.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin);
                return;
            }

            MarkupPointer moveTarget = markupServices.CreateMarkupPointer(blockEnd);

            markupServices.Move(insertAt, blockEnd, moveTarget);
            insertAt.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);
        }
        public KeepSourceFormatting(MarkupRange sourceRange, MarkupRange destinationRange)
        {
            Debug.Assert(sourceRange.Start.Container.GetOwningDoc() == destinationRange.Start.Container.GetOwningDoc(),
                "Ranges must share an owning document!");

            if (sourceRange == null)
            {
                throw new ArgumentNullException("sourceRange");
            }

            if (!sourceRange.Positioned)
            {
                throw new ArgumentException("sourceRange must be positioned.");
            }

            if (sourceRange.Start.IsRightOf(sourceRange.End))
            {
                throw new ArgumentException("sourceRange start must be before range end.");
            }

            if (destinationRange == null)
            {
                throw new ArgumentNullException("destinationRange");
            }

            if (!destinationRange.Positioned)
            {
                throw new ArgumentException("destinationRange must be positioned.");
            }

            if (destinationRange.Start.IsRightOf(destinationRange.End))
            {
                throw new ArgumentException("destinationRange start must be before range end.");
            }

            this.sourceDocument = sourceRange.Start.Container.Document;
            this.sourceMarkupServices = new MshtmlMarkupServices((IMarkupServicesRaw)this.sourceDocument);
            this.sourceRange = sourceRange.Clone();
            this.sourceRange.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left;
            this.sourceRange.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right;

            this.destinationDocument = destinationRange.Start.Container.Document;
            this.destinationMarkupServices = new MshtmlMarkupServices((IMarkupServicesRaw)this.destinationDocument);
            this.destinationRange = destinationRange.Clone();
            this.destinationRange.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left;
            this.destinationRange.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right;
        }
        /// <summary>
        /// Returns null if ranges do not intersect
        /// </summary>
        /// <param name="range"></param>
        /// <returns></returns>
        public MarkupRange Intersect(MarkupRange range)
        {
            MarkupPointer maxStart = Start.IsRightOf(range.Start) ? Start : range.Start;
            MarkupPointer minEnd   = End.IsLeftOf(range.End) ? End : range.End;

            if (minEnd.IsLeftOf(maxStart))
            {
                return(null);
            }

            MarkupRange intersection = MarkupServices.CreateMarkupRange();

            intersection.Start.MoveToPointer(maxStart);
            intersection.End.MoveToPointer(minEnd);

            return(intersection);
        }
        //this is similar to the GetTopLevelElements except will also return table cells if correct filter
        // is set and recurse is equal to true
        public IHTMLElement[] GetTopLevelBlocksAndCells(IHTMLElementFilter filter, bool recurse)
        {
            ArrayList list         = new ArrayList();
            Hashtable usedElements = new Hashtable();

            MarkupPointer p       = MarkupServices.CreateMarkupPointer(Start);
            MarkupContext context = p.Right(false);

            //move p through the range to locate each of the top level elements
            while (p.IsLeftOf(End))
            {
                if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope || context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_NoScope)
                {
                    p.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);
                    if (usedElements[context.Element] == null)
                    {
                        if (p.IsLeftOfOrEqualTo(End) && (filter == null || filter(context.Element)))
                        {
                            list.Add(context.Element);
                        }
                        //special case--inside of a table element, want to get out the cells inside
                        else if (recurse && ElementFilters.TABLE_ELEMENTS(context.Element))
                        {
                            MarkupRange newRange = MarkupServices.CreateMarkupRange(context.Element);
                            newRange.Start.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin);
                            if (newRange.Start.IsLeftOf(Start))
                            {
                                newRange.Start.MoveToPointer(Start);
                            }
                            if (newRange.End.IsRightOf(End))
                            {
                                newRange.End.MoveToPointer(End);
                            }
                            //recursively check inside table element for table cells
                            list.AddRange(newRange.GetTopLevelBlocksAndCells(filter, true));
                        }
                        //cache the fact that we've already tested this element.
                        usedElements[context.Element] = context.Element;
                    }
                }
                p.Right(true, context);
            }
            return(HTMLElementHelper.ToElementArray(list));
        }
        public OleUndoUnit(MshtmlMarkupServices markupServices, MarkupRange selection)
        {
            _markupServices = markupServices;

            //serialize the current position of the markup pointers so that they can be restored if
            //the DOM gets rolled back into the same state via an undo/redo operation.
            if (selection != null && selection.Positioned)
            {
                IMarkupPointer2Raw pointer2StartRaw = selection.Start.PointerRaw as IMarkupPointer2Raw;
                IMarkupPointer2Raw pointer2EndRaw = selection.End.PointerRaw as IMarkupPointer2Raw;
                pointer2StartRaw.GetMarkupPosition(out _startPosition);
                pointer2EndRaw.GetMarkupPosition(out _endPosition);
                pointer2StartRaw.GetContainer(out _markupContainer);
            }

            _description = "OleUndoUnit" + Guid.NewGuid().ToString();

            Undo = true;
        }
        public TableSelection(MarkupRange markupRange)
        {
            // calculate the begin and end cells
            IHTMLTableCell beginCell;
            IHTMLTableCell endCell;
            ArrayList selectedCells;
            FindCellRange(markupRange, out selectedCells, out beginCell, out endCell);

            // see if the two cells have a single containing table
            IHTMLTable table = GetSelectedTable(beginCell, endCell, markupRange) as IHTMLTable;

            // if we have a table then calculate the rest of our states
            if (table != null)
            {
                // validate the table selection
                if (ValidateTableSelection(table, markupRange, out _entireTableSelected))
                {
                    _table = table;

                    _beginCell = beginCell;
                    _endCell = endCell;

                    // filter selected cells to only include direct descendents of this table (no
                    // cells from nested tables)
                    _selectedCells = new ArrayList();
                    foreach (IHTMLElement cell in selectedCells)
                        if (HTMLElementHelper.ElementsAreEqual(TableHelper.GetContainingTableElement(cell) as IHTMLElement, _table as IHTMLElement))
                            _selectedCells.Add(cell);

                    _hasContiguousSelection = !HTMLElementHelper.ElementsAreEqual(_beginCell as IHTMLElement, _endCell as IHTMLElement);

                    _beginRow = GetContainingRowForCell(beginCell);
                    _endRow = GetContainingRowForCell(endCell);

                    _beginColumn = new HTMLTableColumn(_table, beginCell);
                    _endColumn = new HTMLTableColumn(_table, endCell);
                }
            }
        }
        public static IHTMLElement GetSelectedChildEditField(IHTMLElement parent, MarkupRange selection)
        {
            if (selection == null || !selection.Positioned)
            {
                Trace.Fail("Selection is invalid!");
                return null;
            }

            IHTMLElement element = selection.ParentElement();
            if (element == null || !HTMLElementHelper.IsChildOrSameElement(parent, element))
                return null;

            do
            {
                if (InlineEditField.IsEditField(element))
                    return element;

                element = element.parentElement;
            } while (element != null && element.sourceIndex != parent.sourceIndex);

            return null;
        }
        public static int GetParentContainerBlockWidth(MarkupRange markupRange)
        {
            IHTMLElement2 parentBlock = markupRange.Start.GetParentElement(ElementFilters.BLOCK_OR_TABLE_CELL_ELEMENTS) as IHTMLElement2;
            if (parentBlock != null)
            {
                // TODO: we would like to always clientWidth here however for an empty block element this will
                // be zero. So in this case we use scrollWidth which should be a proxy except in the case where
                // the parent element has a horizontal scroll bar (in which case we may insert a table which
                // is worst case too narrow). What we "should" do is insert and remove some bogus content
                // within the block elemet to force its clientWidth to the right value.
                int blockWidth = parentBlock.clientWidth;

                if (blockWidth == 0)
                    blockWidth = parentBlock.scrollWidth;

                return blockWidth;
            }
            else
            {
                return 0;
            }
        }
Beispiel #26
0
        /// <summary>
        /// Find the most logic direction to move to visible location that is where it appears to the user.
        /// And then moves it to that location.  Returns true if it moved the pointer.
        /// </summary>
        /// <param name="Selection"></param>
        /// <returns></returns>
        public static bool DriveSelectionToLogicalPosition(MarkupRange Selection, IHTMLElement body, bool?forward)
        {
            Direction dir = FindSelectionToLogicalPosition(Selection, body, forward);

            if (dir == Direction.None)
            {
                return(false);
            }

            if (dir == Direction.Right)
            {
                Selection.End.Right(true);
                Selection.Start.Right(true);
            }
            else if (dir == Direction.Left)
            {
                Selection.End.Left(true);
                Selection.Start.Left(true);
            }

            return(true);
        }
Beispiel #27
0
        /// <summary>
        /// Create a MarkupRange from a selection object.
        /// </summary>
        public MarkupRange CreateMarkupRange(IHTMLSelectionObject selection)
        {
            if (selection == null)
            {
                return(null);
            }

            // see what type of range is selected
            object range = selection.createRange();

            if (range is IHTMLTxtRange)
            {
                return(CreateMarkupRange(range as IHTMLTxtRange));
            }
            else if (range is IHTMLControlRange)
            {
                // we only support single-selection so a "control-range" can always
                // be converted into a single-element text range
                IHTMLControlRange controlRange = range as IHTMLControlRange;
                if (controlRange.length == 1)
                {
                    IHTMLElement selectedElement = controlRange.item(0);
                    MarkupRange  markupRange     = CreateMarkupRange(selectedElement);

                    //return the precisely positioned text range
                    return(markupRange);
                }
                else
                {
                    Debug.Fail("Length of control range not equal to 1 (value was " + controlRange.length.ToString(CultureInfo.InvariantCulture));
                    return(null);
                }
            }
            else // null or unexpected range type
            {
                return(null);
            }
        }
        public void ExpandToInclude(MarkupRange range)
        {
            if (range == null)
            {
                return;
            }

            if (Positioned)
            {
                if (range.Start.IsLeftOf(Start))
                {
                    Start.MoveToPointer(range.Start);
                }

                if (range.End.IsRightOf(End))
                {
                    End.MoveToPointer(range.End);
                }
            }
            else
            {
                MoveToRange(range);
            }
        }
        public FixupSegment(MarkupRange rangeToFixup, TextStyle sourceTextStyle)
        {
            if (rangeToFixup == null)
            {
                throw new ArgumentNullException("rangeToFixup");
            }

            if (!rangeToFixup.Positioned)
            {
                throw new ArgumentException("rangeToFixup must be positioned!");
            }

            if (sourceTextStyle == null)
            {
                throw new ArgumentNullException("sourceTextStyle");
            }

            SourceTextStyle = sourceTextStyle;
            RangeToFixup = rangeToFixup.Clone();

            // We want the RangeToFixup to stick with the text its wrapped around.
            RangeToFixup.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right;
            RangeToFixup.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left;
        }
        private void ApplyBlockFormatToContentSelection(MarkupRange selection, _ELEMENT_TAG_ID styleTagId, MarkupRange maximumBounds)
        {
            MarkupRange[] stylableBlockRegions = GetSelectableBlockRegions(selection);
            if (stylableBlockRegions.Length > 0)
            {
                //
                // We want to make sure that the selection reflects only the
                // blocks that were changed. Unposition the start and end
                // pointers and then make sure they cover the stylable block
                // regions, no more, no less.
                selection.Start.Unposition();
                selection.End.Unposition();

                foreach (MarkupRange range in stylableBlockRegions)
                {
                    ApplyBlockStyleToRange(styleTagId, range, maximumBounds);

                    if (!selection.Start.Positioned || range.Start.IsLeftOf(selection.Start))
                        selection.Start.MoveToPointer(range.Start);
                    if (!selection.End.Positioned || range.End.IsRightOf(selection.End))
                        selection.End.MoveToPointer(range.End);
                }
            }
        }
        public IDisposable CreateDeleteDamageTracker(MarkupRange range)
        {
            if (DamageTrackingEnabled)
            {
                //Bug fix: The delete key triggers an extra selection change event that causes word damage to be
                //inappropriately committed, so we use a flag to suppress the selection change event for
                //this case.
                suppressSelectionChangeForDelete = true;

                return new DeleteDamageTracker(this);
            }
            else
                return null;
        }
 public void AddDamage(MarkupRange range, bool includeAdjacentWords)
 {
     if (DamageTrackingEnabled)
         wordRangeDamager.AddDamage(range, includeAdjacentWords);
 }
 public void AddDamage(MarkupRange range)
 {
     AddDamage(range, false);
 }
        /// <summary>
        /// Text ranges are extremely expensive to create, and readily reusable. UseStagingTextRange
        /// is a higher order function that makes it slightly easier to reuse text ranges, which can
        /// give much better performance if the lifetime of your stagingTextRange reference spans
        /// lots of calls.
        /// </summary>
        /// <typeparam name="TResult">The type of the result from the function we'll execute.</typeparam>
        /// <param name="stagingTextRange">A reference to a stagingTextRange that we can use, it can be null at first and we'll create on demand if necessary.</param>
        /// <param name="range">The markup range to move the stagingTextRange to.</param>
        /// <param name="func">The function to pass the stagingTextRange to after it's been created/positioned.</param>
        /// <returns>The value returned from func.</returns>
        public static TResult UseStagingTextRange <TResult>(ref IHTMLTxtRange stagingTextRange, MarkupRange range, TextRangeFunc <TResult> func)
        {
            Debug.Assert(range != null, "Range must not be null!");
            Debug.Assert(range.Positioned, "Range must be positioned!");
            Debug.Assert(range.Start.IsLeftOfOrEqualTo(range.End), "Range start must be left of or equal to range end!");

            if (stagingTextRange == null)
            {
                stagingTextRange = range.MarkupServices.CreateTextRange(range.Start, range.End);
            }
            else
            {
                range.MarkupServices.MoveRangeToPointers(range.Start, range.End, stagingTextRange);
            }

            return(func(stagingTextRange));
        }
        /// <summary>
        /// Safely removes the content within this range without leaving the document badly formed.
        /// </summary>
        public void RemoveContent()
        {
            //delete the selection by moving a delete range right (from the start).
            //Each time that a tag that does not entirely exist within this selection
            //is encountered, the range content will be deleted, the deleteRange will
            //skip over the element.
            MarkupRange deleteRange = this.Clone();

            Trace.Assert(deleteRange.Start.Positioned, "Trying to remove content from selection that contains pointers that are not positioned.");

            deleteRange.End.MoveToPointer(deleteRange.Start);
            MarkupPointer p = MarkupServices.CreateMarkupPointer();
            MarkupPointer previousPosition = MarkupServices.CreateMarkupPointer(deleteRange.End);
            MarkupContext context          = new MarkupContext();

            deleteRange.End.Right(true, context);
            while (deleteRange.End.IsLeftOf(End))
            {
                if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope)
                {
                    p.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);
                    if (p.IsRightOf(End))
                    {
                        //this element does not exist entirely in this selection, so we need to
                        //ignore it in the delete.

                        //save this position so that the delete range can be repositioned here
                        p.MoveToPointer(deleteRange.End);

                        //move the end left since we overstepped the valid delete range
                        deleteRange.End.MoveToPointer(previousPosition);

                        //delete the content in the deleteRange, and move it back to this position
                        deleteRangeContentAndMoveToPosition(deleteRange, p);
                    }
                    else
                    {
                        //this element exists entirely in this selection, so skip to its end (since
                        //we know it can be deleted)
                        deleteRange.End.MoveToPointer(p);
                    }
                }
                else if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope)
                {
                    p.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin);
                    if (p.IsLeftOf(Start))
                    {
                        //this element does not exist entirely in this selection, so we need to
                        //ignore it in the delete.

                        //save this position so that the delete range can be repositioned here
                        p.MoveToPointer(deleteRange.End);

                        //move the end left since we overstepped the valid delete range
                        deleteRange.End.MoveToPointer(previousPosition);

                        //delete the content in the deleteRange, and move it back to this position
                        deleteRangeContentAndMoveToPosition(deleteRange, p);
                    }
                    else
                    {
                        //this element exists entirely in this selection, so skip to its end (since
                        //we know it can be deleted)
                        deleteRange.End.MoveToPointer(p);
                    }
                }

                previousPosition.MoveToPointer(deleteRange.End);
                deleteRange.End.Right(true, context);
            }

            //delete the last part of the range
            deleteRange.End.MoveToPointer(End);
            if (!deleteRange.Start.Equals(deleteRange.End))
            {
                MarkupServices.Remove(deleteRange.Start, deleteRange.End);
            }
        }
        private void ExpandDamageToPreviousWord(MarkupRange damageRange)
        {
            MarkupRange previousWordRange = damageRange.Clone();
            previousWordRange.Start.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_PREVWORDBEGIN);

            // If we were already at the first word in the document, moving to the previous word would have put the
            // MarkupPointer outside the body element.
            if (previousWordRange.Start.GetParentElement(ElementFilters.BODY_ELEMENT) != null)
            {
                previousWordRange.Collapse(true);
                previousWordRange.End.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_NEXTWORDEND);
                if (!previousWordRange.IsEmpty() && !_editorControl.IgnoreRangeForSpellChecking(previousWordRange))
                {
                    damageRange.Start.MoveToPointer(previousWordRange.Start);
                }
            }
        }
 private void DumpDamagedRegions(MarkupRange[] damagedRegions)
 {
     Trace.WriteLine("---Damaged Regions---");
     foreach (MarkupRange damage in damagedRegions)
     {
         string text = damage.Text;
         if (text != null)
             Trace.WriteLine("  " + text.Replace('\r', ' ').Replace('\n', ' '));
     }
 }
        /// <summary>
        /// Expands this range out to the next parent shared by the start and end points
        /// if there there are no non-empty text elements between them.
        /// </summary>
        /// <returns></returns>
        public bool MoveOutwardIfNo(RangeFilter rangeFilter)
        {
            MarkupRange newRange = MarkupServices.CreateMarkupRange();

            IHTMLElement sharedParent = GetSharedParent(Start, End);

            // If share a common parent, we will take the shared parent's parent so we can see if we want to grab
            // all the html inside of it, unless the shared parent is the body element, in which case we don't want to
            // epxand outward anymore
            if (Start.CurrentScope == sharedParent && End.CurrentScope == sharedParent && !(sharedParent is IHTMLBodyElement))
            {
                sharedParent = sharedParent.parentElement;
            }

            //expand to the inside of the shared parent first.  If this matches the current placement
            //of the pointers, then expand to the outside of the parent. This allows the outter selection
            //to grow incrementally in such a way as to allow the shared parent to be tested between
            //each iteration of this operation.
            newRange.Start.MoveAdjacentToElement(sharedParent, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin);
            newRange.End.MoveAdjacentToElement(sharedParent, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd);
            if (newRange.IsEmpty() || newRange.Start.IsRightOf(Start) || newRange.End.IsLeftOf(End))
            {
                newRange.Start.MoveAdjacentToElement(sharedParent, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin);
                newRange.End.MoveAdjacentToElement(sharedParent, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);
            }

            if (!rangeFilter(newRange.Start, Start) && !rangeFilter(End, newRange.End) &&
                !(Start.IsEqualTo(newRange.Start) && End.IsEqualTo(newRange.End)))
            {
                Start.MoveToPointer(newRange.Start);
                End.MoveToPointer(newRange.End);
                return(true);
            }
            else
            {
                //span the start and end pointers as siblings by finding the parents of start and end
                //pointers that are direct children of the sharedParent
                IHTMLElement child = GetOuterMostChildOfParent(Start, true, sharedParent);
                if (child != null)
                {
                    newRange.Start.MoveAdjacentToElement(child, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin);
                }
                else
                {
                    newRange.Start = Start;
                }

                child = GetOuterMostChildOfParent(End, false, sharedParent);
                if (child != null)
                {
                    newRange.End.MoveAdjacentToElement(child, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);
                }
                else
                {
                    newRange.End = End;
                }

                if (!rangeFilter(newRange.Start, Start) && !rangeFilter(End, newRange.End) &&
                    !(Start.IsEqualTo(newRange.Start) && End.IsEqualTo(newRange.End)))
                {
                    Start.MoveToPointer(newRange.Start);
                    End.MoveToPointer(newRange.End);
                    return(true);
                }
                else
                {
                    //the range didn't change, so return false.
                    return(false);
                }
            }
        }
 public void Reset()
 {
     damageQueue.Clear();
     _wordHelper = new MarkupServicesWordHelper(_mshtmlEditor.MshtmlControl.MarkupServices);
     _currentSelectionDamage = _mshtmlEditor.MshtmlControl.MarkupServices.CreateMarkupRange();
     _currentSelectionDamage.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left;
     _currentSelectionDamage.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right;
 }
 /// <summary>
 /// Move this markup range to the specified location.
 /// </summary>
 /// <param name="textRange"></param>
 public void MoveToRange(MarkupRange range)
 {
     Start.MoveToPointer(range.Start);
     End.MoveToPointer(range.End);
 }
 public bool Intersects(MarkupRange range)
 {
     return(!(Start.IsRightOf(range.End) || End.IsLeftOf(range.Start)));
 }
 /// <summary>
 /// Returns true if the specified range is in a position between, or (if allowed) equal to this range's Start/End points.
 /// </summary>
 /// <returns></returns>
 public bool InRange(MarkupRange range, bool allowEquals)
 {
     return(InRange(range.Start, allowEquals) && InRange(range.End, allowEquals));
 }
 public IDisposable CreateDamageTracker(MarkupRange range, bool includeAdjacentWords)
 {
     if (DamageTrackingEnabled)
         return new DamageTracker(this, range.Start, range.End, includeAdjacentWords);
     else
         return null;
 }
        private void _AddDamage(MarkupRange range)
        {
            lock (damageQueue)
            {
                damageQueue.EnqueueDamage(range);
                //Debug.WriteLine("Word Damage:" + _currentSelectionDamage.Text);

                _mshtmlEditor.BeginInvoke(new ThreadStart(FireDamageOccurred));
            }
        }
 public DamageTracker(HtmlEditorControlDamageServices damageServices, MarkupPointer start, MarkupPointer end, bool includeAdjacentWords)
 {
     _damageServices = damageServices;
     _damageRange = damageServices.wordRangeDamager.CreateDamageTrackingRange(start, end, includeAdjacentWords);
 }
 /// <summary>
 /// Deletes (unsafely!) content within a range and repositions the range at a new position.
 /// </summary>
 /// <param name="range"></param>
 /// <param name="newPosition"></param>
 private void deleteRangeContentAndMoveToPosition(MarkupRange range, MarkupPointer newPosition)
 {
     MarkupServices.Remove(range.Start, range.End);
     range.Start.MoveToPointer(newPosition);
     range.End.MoveToPointer(newPosition);
 }
 public DamageEvent(MarkupRange[] damageRegions)
 {
     _damageRegions = damageRegions;
 }
Beispiel #48
0
 internal void Add(OpenLiveWriter.Mshtml.MarkupRange SelectedMarkupRange)
 {
     listBoxSelection.Items.Insert(0, new SelectionItem(SelectedMarkupRange));
 }
 private void ExpandDamageToAdjacentWords(MarkupRange damageRange)
 {
     ExpandDamageToPreviousWord(damageRange);
     ExpandDamageToNextWord(damageRange);
 }
        private bool HasTextBetween(MarkupPointer start, MarkupPointer end)
        {
            MarkupRange range = MarkupServices.CreateMarkupRange(start, end);

            return(!range.IsEmptyOfText(false));
        }
 internal void AddDamage(MarkupRange range, bool includeAdjacentWords)
 {
     MarkupRange wordRange = range.Clone();
     _wordHelper.MoveToWordStart(wordRange.Start);
     _wordHelper.MoveToWordEnd(wordRange.End);
     if (includeAdjacentWords)
     {
         ExpandDamageToAdjacentWords(wordRange);
     }
     _AddDamage(wordRange);
 }
        public MarkupRange[] GetDamagedRegions()
        {
            lock (damageQueue)
            {
                MarkupRange[] damage = damageQueue.DequeueDamage();

                //reset the current selection damage
                MarkupRange newSelectionDamage = _mshtmlEditor.MshtmlControl.MarkupServices.CreateMarkupRange();
                newSelectionDamage.MoveToRange(_currentSelectionDamage);
                _currentSelectionDamage = newSelectionDamage;
                damaged = false;
                return damage;
            }
        }
        private void AdjustCurrentWordRange(MarkupRange range)
        {
            if (damaged)
            {
                _currentSelectionDamage = _mshtmlEditor.MshtmlControl.MarkupServices.CreateMarkupRange();
                damaged = false;
            }

            _currentSelectionDamage.MoveToRange(range);

            //Trace.WriteLine("adjacent to word start: " + IsAdjacentToWordStart(range.Start));
            //Trace.WriteLine("adjacent to word end: " + IsAdjacentToWordEnd(range.End));
            _wordHelper.MoveToWordStart(_currentSelectionDamage.Start);
            _wordHelper.MoveToWordEnd(_currentSelectionDamage.End);
            //Trace.WriteLine("Current Word:" + _currentSelectionDamage.Text);
        }
Beispiel #54
0
 /// <summary>
 /// Only edit tables that are not contained within SmartContent blocks and which
 /// are marked with the "unselectable"  attribute. Since this is an attribute which
 /// applies only to editing scenarios it is almost certain never to enter the editor
 /// "from the wild" so it is a  reasonable way to determine whether we created the
 /// table (and thus can guarantee that it conforms to our editing capabilities). The
 /// only other reasonable choice would be to mark the table up with some other
 /// pseudo-hidden metadata, which seems even more undesirable.
 /// </summary>
 public static bool TableElementIsEditable(IHTMLElement element, MarkupRange elementMarkupRange)
 {
     return TableElementContainsWriterEditingMark(element) &&
         !TableElementIsContainedInSmartContent(elementMarkupRange);
 }
 public void EnqueueDamage(MarkupRange range)
 {
     lock (damagedRanges)
     {
         damagedRanges.Add(range);
     }
 }
Beispiel #56
0
 public static bool DriveSelectionToLogicalPosition(MarkupRange Selection, IHTMLElement body)
 {
     return(DriveSelectionToLogicalPosition(Selection, body, null));
 }
Beispiel #57
0
 private static bool TableElementIsContainedInSmartContent(MarkupRange elementRange)
 {
     // table elements inside smart content regions are not editable
     IHTMLElement parentSmartContent = elementRange.Start.GetParentElement(ContentSourceManager.CreateSmartContentElementFilter());
     return parentSmartContent != null;
 }
Beispiel #58
0
        /// <summary>
        /// Create a MarkupRange from a set of MarkupPointers.
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        public MarkupRange CreateMarkupRange(MarkupPointer start, MarkupPointer end)
        {
            MarkupRange markupRange = new MarkupRange(start, end, this);

            return(markupRange);
        }
        private IHTMLElement GetNextElement(MarkupPointer start, MarkupRange boundaries, IHTMLElementFilter filter, bool forward)
        {
            start = start.Clone();
            MarkupPointer boundary = forward ? boundaries.End : boundaries.Start;
            MarkupContext moveResult = new MarkupContext();
            _MARKUP_CONTEXT_TYPE skipContext = _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope;

            //advance the pointer
            if (forward)
                start.Right(true, moveResult);
            else
                start.Left(true, moveResult);

            while (forward ? start.IsLeftOf(boundary) : start.IsRightOf(boundary))
            {
                if (moveResult.Element != null && moveResult.Context != skipContext && filter(moveResult.Element))
                {
                    return moveResult.Element;
                }
                //advance the pointer
                if (forward)
                    start.Right(true, moveResult);
                else
                    start.Left(true, moveResult);
            }
            return null;
        }
 /// <summary>
 /// Returns true if the specified range is in a position between, or equal to this range's Start/End points.
 /// </summary>
 /// <param name="p"></param>
 /// <returns></returns>
 public bool InRange(MarkupRange range)
 {
     return(InRange(range, true));
 }