protected virtual void Deselect() { MarkupRange range = EditorContext.MarkupServices.CreateMarkupRange(HTMLElement); range.Start.MoveAdjacentToElement(HTMLElement, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); range.Collapse(true); range.ToTextRange().select(); }
private string GetHtmlText(out MarkupPointer blockBoundary) { MarkupRange blockRange = _blogPostHtmlEditorControl.SelectedMarkupRange.Clone(); MarkupRange textRange = blockRange.Clone(); IHTMLElement ele = blockRange.ParentElement(ElementFilters.IsBlockOrTableCellOrBodyElement); if (ele == null) { blockBoundary = null; return(String.Empty); } blockRange.MoveToElement(ele, false); blockBoundary = blockRange.Start; // Fix Bug 616152 - We want the start and end pointer to match so // we can look back from the start of the insertion point (not the end, // which would include the selection that is going to be overwritten) textRange.Collapse(true); // Fix bug WinLive 59172: Specific characters of mixed characters are broken in mail body after press the 'Enter' key // Caused by using MOVEUNIT_PREVCHAR to navigate into Unicode surrogate (32-bit) characters. We work around this by moving // only at word or block boundaries. string html = null; do { int startPos = textRange.Start.MarkupPosition; textRange.Start.MoveUnitBounded(_MOVEUNIT_ACTION.MOVEUNIT_PREVWORDBEGIN, blockBoundary); if (textRange.Start.MarkupPosition == startPos) { // PREVWORDBEGIN didn't actually move us, due to no word being available. // To avoid an infinite loop, just move the text range to include the whole // block and move on. textRange.Start.MoveToPointer(blockRange.Start); break; } if (textRange.Positioned && textRange.Start.IsLeftOfOrEqualTo(textRange.End)) { html = MarkupHelpers.GetRangeTextFast(textRange); } html = html ?? string.Empty; } while (html.Length < MinLookBack && textRange.Start.IsRightOf(blockRange.Start)); if (html == null && textRange.Positioned && textRange.Start.IsLeftOfOrEqualTo(textRange.End)) { html = MarkupHelpers.GetRangeTextFast(textRange); } return(html ?? string.Empty); }
private MarkupRange AdjustSelection() { MarkupRange selection = CreateMaxSafeRange(SelectedMarkupRange); if (selection.IsEmpty()) { MarkupRange editableRange = MarkupHelpers.GetEditableRange(selection.Start.CurrentScope, MarkupServices); MarkupPointerMoveHelper.MoveUnitBounded(selection.Start, MarkupPointerMoveHelper.MoveDirection.LEFT, MarkupPointerAdjacency.BeforeVisible | MarkupPointerAdjacency.BeforeEnterScope, editableRange.Start); selection.Collapse(true); } selection.ToTextRange().select(); return(selection); }
internal bool Restore(MarkupRange selection, MarkupRange bounds) { if (initialMarkup == null) { return(false); } NormalizeBounds(ref bounds); /* * if (initialMarkup != bounds.HtmlText) * { * Trace.Fail("Unexpected markup"); * Trace.WriteLine(initialMarkup); * Trace.WriteLine(bounds.HtmlText); * return false; * } */ selection.Start.MoveToPointer(bounds.Start); if (movesRight == int.MaxValue) { selection.Start.MoveToPointer(bounds.End); } else { for (int i = 0; i < movesRight; i++) { selection.Start.Right(true); } } for (int i = 0; i < charsLeft; i++) { selection.Start.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_PREVCHAR); } selection.Collapse(true); selection.ToTextRange().select(); Debug.Assert(bounds.InRange(selection, true), "Selection was out of bounds"); return(true); }
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); } } }
public static void ChangeElementTagIds(MshtmlMarkupServices markupServices, MarkupRange selection, _ELEMENT_TAG_ID[] tagBefore, _ELEMENT_TAG_ID tagAfter) { HtmlStyleHelper htmlStyleHelper = new HtmlStyleHelper(markupServices); int parentStartDiff = 0; MarkupRange rangeToChange; bool selectionStartedEmpty = selection.IsEmpty(); if (selectionStartedEmpty) { // Operate on parent block element rangeToChange = markupServices.CreateMarkupRange(selection.ParentBlockElement()); parentStartDiff = selection.Start.MarkupPosition - rangeToChange.Start.MarkupPosition; } else { rangeToChange = selection; // If expanding the selection would not include any new text, then expand it. // <h1>|abc|</h1> --> |<h1>abc</h1>| rangeToChange.MoveOutwardIfNoText(); } IHTMLElementFilter[] filters = new IHTMLElementFilter[tagBefore.Length]; for (int i = 0; i < tagBefore.Length; i++) { filters[i] = ElementFilters.CreateTagIdFilter(markupServices.GetNameForTagId(tagBefore[i])); } IHTMLElement[] elements = rangeToChange.GetElements(ElementFilters.CreateCompoundElementFilter(filters), false); foreach (IHTMLElement element in elements) { MarkupRange elementRange = markupServices.CreateMarkupRange(element); int startPositionDiff = rangeToChange.Start.MarkupPosition - elementRange.Start.MarkupPosition; int endPositionDiff = rangeToChange.End.MarkupPosition - elementRange.End.MarkupPosition; // @RIBBON TODO: Appropriately preserve element attributes when changing tag ids? MarkupRange newElementRange = markupServices.CreateMarkupRange(htmlStyleHelper.WrapRangeInSpanElement(tagAfter, null, elementRange)); markupServices.RemoveElement(element); MarkupPointer startPointer = rangeToChange.Start.Clone(); startPointer.MoveToMarkupPosition(startPointer.Container, newElementRange.Start.MarkupPosition + startPositionDiff); if (startPointer.IsLeftOf(rangeToChange.Start)) { rangeToChange.Start.MoveToPointer(startPointer); } MarkupPointer endPointer = rangeToChange.End.Clone(); endPointer.MoveToMarkupPosition(endPointer.Container, newElementRange.End.MarkupPosition + endPositionDiff); if (endPointer.IsLeftOf(elementRange.End)) { rangeToChange.End.MoveToPointer(endPointer); } } if (selectionStartedEmpty) { selection.Start.MoveToMarkupPosition(selection.Start.Container, rangeToChange.Start.MarkupPosition + parentStartDiff); selection.Collapse(true); } }
private MarkupRange ApplyInlineTag(_ELEMENT_TAG_ID tagId, string attributes, MarkupRange selection) { MarkupRange newSelection = _markupServices.CreateMarkupRange(); // If the selection is empty, then just insert the tag if (selection.IsEmpty()) { newSelection.MoveToElement(WrapRangeInSpanElement(tagId, attributes, selection), false); return(newSelection); } // Start at the beginning of the selection move forward until you hit a block start/exit context or the end of the selection bool keepApplying = true; MarkupContext contextStart = new MarkupContext(); MarkupRange blockFreeRange = _markupServices.CreateMarkupRange(selection.Start.Clone(), selection.Start.Clone()); MarkupPointer currentPointer = _markupServices.CreateMarkupPointer(blockFreeRange.Start); while (keepApplying) { // Check if moving right would be beyond the bounds of the selection. if (currentPointer.IsRightOfOrEqualTo(selection.End)) { // We've hit the end of the selection, so we're done. keepApplying = false; Debug.Assert(blockFreeRange.Start.IsLeftOfOrEqualTo(selection.End)); blockFreeRange.End.MoveToPointer(selection.End.IsLeftOf(currentPointer) ? selection.End : currentPointer); if (ShouldApplyInlineTagToBlockFreeSelection(blockFreeRange)) { newSelection.ExpandToInclude(ApplyInlineTagToBlockFreeSelection(tagId, attributes, blockFreeRange)); } break; } // Check if the next context is entering or exiting a block. currentPointer.Right(false, contextStart); if (contextStart.Element != null && ElementFilters.IsBlockElement(contextStart.Element)) { switch (contextStart.Context) { case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope: case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope: { blockFreeRange.End.MoveToPointer(selection.End.IsLeftOf(currentPointer) ? selection.End : currentPointer); if (ShouldApplyInlineTagToBlockFreeSelection(blockFreeRange)) { newSelection.ExpandToInclude(ApplyInlineTagToBlockFreeSelection(tagId, attributes, blockFreeRange)); } if (contextStart.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope) { blockFreeRange.Start.MoveAdjacentToElement(contextStart.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); } else if (contextStart.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope) { blockFreeRange.Start.MoveAdjacentToElement(contextStart.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); } blockFreeRange.Collapse(true); } break; case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_None: { keepApplying = false; blockFreeRange.End.MoveToPointer(selection.End.IsLeftOf(currentPointer) ? selection.End : currentPointer); if (ShouldApplyInlineTagToBlockFreeSelection(blockFreeRange)) { newSelection.ExpandToInclude(ApplyInlineTagToBlockFreeSelection(tagId, attributes, blockFreeRange)); } } break; default: break; } } // Finally, move our pointer currentPointer.Right(true); } if (newSelection.Positioned) { newSelection.Trim(); } return(newSelection); }
/// <summary> /// Advance to next word /// </summary> public void Next() { currentWordRange.End.MoveToPointer(currentVirtualPosition); do { //advance the start pointer to the beginning of next word if (!currentWordRange.End.IsEqualTo(selectionRange.Start)) //avoids skipping over the first word { //fix bug 1848 - move the start to the end pointer before advancing to the next word //this ensures that the "next begin" is after the current selection. currentWordRange.Start.MoveToPointer(currentWordRange.End); currentWordRange.Start.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_PREVWORDBEGIN); currentWordRange.Start.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_NEXTWORDBEGIN); } else { //Special case for putting the start pointer at the beginning of the //correct word when the selection may or may not be already adjacent //to the the beginning of the word. //Note: theoretically, the selection adjustment in the constructor //guarantees that we will be flush against the first word, so we could //probably do nothing, but it works, so we'll keep it. currentWordRange.Start.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_NEXTWORDEND); currentWordRange.Start.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_PREVWORDBEGIN); } //advance the end pointer to the end of next word currentWordRange.End.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_NEXTWORDEND); if (currentWordRange.Start.IsRightOf(currentWordRange.End)) { //Note: this was a condition that caused several bugs that caused us to stop //spell-checking correctly, so this logic is here in case we still have edge //cases that trigger it. //This should not occur anymore since we fixed several causes of this //condition by setting the currentWordRange gravity, and detecting case where //the selection may or may-not start at the beginning of a word. Debug.Fail("word start jumped past word end"); //if this occurred, it was probably because start was already at the beginning //of the correct word before it was moved. To resolve this situation, we //move the start pointer back to the beginning of the word that the end pointer //is at. Since the End pointer always advances on each iteration, this should not //cause an infinite loop. The worst case scenario is that we check the same word //more than once. currentWordRange.Start.MoveToPointer(currentWordRange.End); currentWordRange.Start.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_PREVWORDBEGIN); } } while(MarkupHelpers.GetRangeTextFast(currentWordRange) == null && currentWordRange.End.IsLeftOf(selectionRange.End)); currentVirtualPosition.MoveToPointer(currentWordRange.End); if (currentWordRange.End.IsRightOf(selectionRange.End)) { //then collapse the range so that CurrentWord returns Empty; currentWordRange.Start.MoveToPointer(currentWordRange.End); } else { MarkupRange testRange = currentWordRange.Clone(); testRange.Collapse(false); testRange.End.MoveUnitBounded(_MOVEUNIT_ACTION.MOVEUNIT_NEXTCHAR, selectionRange.End); if (MarkupHelpers.GetRangeHtmlFast(testRange) == ".") { currentWordRange.End.MoveToPointer(testRange.End); } } }
private void ReplaceDashes(MarkupPointer blockBoundary) { if (!AutoreplaceSettings.EnableHyphenReplacement) { return; } MarkupRange emRange = _currentSelection.Clone(); for (int i = 0; i < 3 && emRange.Start.IsRightOf(blockBoundary); i++) { emRange.Start.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_PREVWORDBEGIN); } string emText = emRange.Text ?? ""; if (emText.Contains("-")) { // \u00A0 = non breaking space Regex regex = new Regex(@"[^\s\u00A0\-]([ \u00A0]?(?>--?)[ \u00A0]?)[^\s\u00A0\-]"); Match match = regex.Match(emText); if (match.Success) { Debug.Assert(match.Groups.Count == 2, "Matched more than one set of dashes. Expecting only one match."); string matchValue = match.Groups[1].Value.Replace((char)160, ' '); MarkupRange findRange = _currentSelection.Clone(); // Since we're now doing this matching AFTER a character has been added, we need to jump back. findRange.End.MoveUnitBounded(_MOVEUNIT_ACTION.MOVEUNIT_PREVCHAR, emRange.Start); findRange.Collapse(false); for (int i = 0; i < matchValue.Length; i++) { findRange.Start.MoveUnitBounded(_MOVEUNIT_ACTION.MOVEUNIT_PREVCHAR, emRange.Start); } for (int i = 0; i < emText.Length; i++) { if (findRange.Text == matchValue) { string replaceText = null; switch (matchValue) { case ("--"): case ("-- "): replaceText = "—"; break; case (" --"): case (" -"): replaceText = " –"; break; case (" -- "): case (" - "): replaceText = " – "; break; case ("-"): case ("- "): break; } if (replaceText != null) { _insertHtml(findRange.Start, findRange.End, replaceText); break; } } findRange.Start.MoveUnitBounded(_MOVEUNIT_ACTION.MOVEUNIT_PREVCHAR, emRange.Start); findRange.End.MoveUnitBounded(_MOVEUNIT_ACTION.MOVEUNIT_PREVCHAR, emRange.Start); } } } }