internal SelectionPositionPreservationCookie(MshtmlMarkupServices markupServices, MarkupRange selection, MarkupRange bounds) { if (!selection.IsEmpty()) { return; } initialMarkup = bounds.HtmlText; NormalizeBounds(ref bounds); MarkupPointer p = bounds.Start.Clone(); movesRight = 0; while (p.IsLeftOf(selection.Start)) { movesRight++; p.Right(true); if (p.IsRightOf(bounds.End)) { movesRight = int.MaxValue; p.MoveToPointer(bounds.End); break; } } charsLeft = 0; while (p.IsRightOf(selection.Start)) { charsLeft++; p.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_PREVCHAR); } }
private bool IsEditableSelection() { MarkupRange selectedRange = SelectedMarkupRange; if (MarkupHelpers.GetEditableRange(selectedRange.Start.CurrentScope, MarkupServices) != null) { if (selectedRange.IsEmpty() || MarkupHelpers.GetEditableRange(selectedRange.Start.CurrentScope, MarkupServices) != null) { return(true); } } return(false); }
/// <summary> /// Notify the data format handler that data was dropped and should be inserted into /// the document at whatever insert location the handler has internally tracked. /// </summary> /// <param name="action"></param> public override bool DataDropped(DataAction action) { if (currentCaretLocation == null) { return(false); } // create two markup pointers that map to the location of the caret MarkupPointer begin = EditorContext.MarkupServices.CreateMarkupPointer(); MarkupPointer end = EditorContext.MarkupServices.CreateMarkupPointer(); EditorContext.MarkupServices.MoveMarkupPointerToCaret(currentCaretLocation, begin); MarkupPointerMoveHelper.PerformImageBreakout(begin); //optimize the drop location to keep it from being in an unexpected location (fixes bug 395224) if (EditorContext.ShouldMoveDropLocationRight(begin)) { begin.Right(true); } //synchronize the end pointer with the being pointer end.MoveToPointer(begin); MarkupRange selectedRange = EditorContext.SelectedMarkupRange; // WinLive 91888 Photomail image drag drop loses images //if (!selectedRange.IsEmpty() && selectedRange.InRange(end)) if (!selectedRange.IsEmpty() && selectedRange.InRange(end, false)) { //the drop location is over the drag source location, so don't so anything. return(false); } // Forces a SelectionChanged event so that the correct behaviors around the drop location are activated. // For example, one side effect of this call is that the OnEditableRegionFocusChanged event is fired, which // sets whether the current drop location in the canvas supports images, html and/or text. MarkupRange dropRange = EditorContext.MarkupServices.CreateMarkupRange(begin, end); dropRange.ToTextRange().select(); try { // insert the data at the current insertion point return(InsertData(action, begin, end)); } catch (Exception e) { Trace.Fail(e.Message, e.StackTrace); return(false); } }
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(); } }
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); }
private void buttonLoad_Click(object sender, EventArgs e) { MarkupRange selection = GetSelectedMarkupRange(); if (selection.IsEmpty()) { this.listViewStyle.Enabled = true; IHTMLElement2 element = (IHTMLElement2)selection.Start.CurrentScope; IHTMLCurrentStyle style = element.currentStyle; RefreshStyleItems(style); } else { this.listViewStyle.Enabled = false; } }
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 HandleEnterKey(HtmlEventArgs e) { //pressing the enter key on an empty line is used as a gesture for exiting the blockquote //If this situation is encountered, move the current empty block element outside of the blockquote MarkupRange selection = EditorContext.Selection.SelectedMarkupRange; if (selection.IsEmpty()) { MarkupPointer selectionPoint = EditorContext.MarkupServices.CreateMarkupPointer(selection.Start); selectionPoint.Cling = true; IHTMLElement currBlock = selection.Start.CurrentBlockScope(); MarkupRange currBlockRange = EditorContext.MarkupServices.CreateMarkupRange(currBlock, false); if (currBlockRange.IsEmptyOfContent()) { currBlockRange.MoveToElement(currBlock, true); // Make sure there is no content between the end of this block range and the end of the blockquote. MarkupPointer afterEndCurrBlock = EditorContext.MarkupServices.CreateMarkupPointer(currBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); MarkupPointer beforeEndBlockQuote = EditorContext.MarkupServices.CreateMarkupPointer(HTMLElement, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd); MarkupRange restOfBlockQuote = EditorContext.MarkupServices.CreateMarkupRange(afterEndCurrBlock, beforeEndBlockQuote); if (!restOfBlockQuote.IsEmpty() || !restOfBlockQuote.IsEmptyOfContent()) { return; } //create a pointer for the new location that the block element will be moved to. MarkupPointer insertionPoint = EditorContext.MarkupServices.CreateMarkupPointer(HTMLElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); //move the current empty block to the DOM location after the blockquote EditorContext.MarkupServices.Move(currBlockRange.Start, currBlockRange.End, insertionPoint); currBlockRange.MoveToElement(currBlock, false); //adjust the selection to the new location of the block element. currBlockRange.Start.MoveToPointer(selectionPoint); currBlockRange.End.MoveToPointer(selectionPoint); currBlockRange.ToTextRange().select(); //cancel the key down event so that the editor doesn't try to handle it e.Cancel(); } } }
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); } }
public static MarkupRange ApplyInlineTag(MshtmlMarkupServices markupServices, _ELEMENT_TAG_ID tagId, string attributes, MarkupRange selection, bool toggle) { HtmlStyleHelper htmlStyleHelper = new HtmlStyleHelper(markupServices); // Aligning the with the behavior of Word, we will make <SUP> and <SUB> mutually exclusive. // That is, if you are applying <SUB> to a selection that has <SUP> applied already, we will first remove the <SUP>, and vice versa. // Wait, if empty and we're on the very end of the already existing formatting, then we just want to jump out and apply... MarkupRange selectionToApply = selection.Clone(); if (toggle) { // If already entirely inside the tag // If empty and just inside of the closing tag, then jump outside the closing tag // Else remove the tag // If already entirely outside the tag // If empty, apply the tag and put selection inside // If non-empty, then apply tag and reselect // If partially inside the tag // Remove the tag _ELEMENT_TAG_ID mutuallyExclusiveTagId = _ELEMENT_TAG_ID.TAGID_NULL; switch (tagId) { case _ELEMENT_TAG_ID.TAGID_SUP: mutuallyExclusiveTagId = _ELEMENT_TAG_ID.TAGID_SUB; break; case _ELEMENT_TAG_ID.TAGID_SUB: mutuallyExclusiveTagId = _ELEMENT_TAG_ID.TAGID_SUP; break; default: break; } if (selection.IsEmpty()) { // If the selection is empty and we're just inside the tagId closing tag (meaning that there is no text before the closing tag), // then we just hop outside of the tagId closing tag. bool exitScopeMatchesTagIdToBeApplied; MarkupPointer pointerOutsideTagIdScope = htmlStyleHelper.NextExitScopeWithoutInterveningText(selection, tagId, mutuallyExclusiveTagId, out exitScopeMatchesTagIdToBeApplied); if (pointerOutsideTagIdScope != null) { selectionToApply = markupServices.CreateMarkupRange(pointerOutsideTagIdScope, pointerOutsideTagIdScope); if (exitScopeMatchesTagIdToBeApplied) { return(selectionToApply); } // else we still need to apply tagId } } // If a mutually exclusive tag is applied, then remove it. if (selectionToApply.IsTagId(mutuallyExclusiveTagId, true)) { selectionToApply.RemoveElementsByTagId(mutuallyExclusiveTagId, false); } // If this tag is already applied, then remove it and return. if (selectionToApply.IsTagId(tagId, true)) { selectionToApply.RemoveElementsByTagId(tagId, false); return(selectionToApply); } } return(htmlStyleHelper.ApplyInlineTag(tagId, attributes, selectionToApply)); }
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); }
public void ClearRange(MarkupRange clear) { if (words.Count == 0) { return; } // Just being defensive here if (!clear.Positioned) { return; } // Debug.WriteLine(string.Format("ClearRange:\r\n{0}\r\n{1}", clear.Start.PositionTextDetail, clear.End.PositionTextDetail)); /* * Start from the first range in the list where the start is left of * the Clear range's end. Take a look at each range until you get to * one where the range's end is left of the Clear range's start. */ int idx = BinarySearch(words.Keys, clear.Start.PointerRaw, new MarkupPointerComparer()); if (idx < 0) { idx = ~idx; idx--; idx = Math.Max(0, idx); } for (; idx < words.Count; idx++) { MarkupRange range = words.Values[idx]; // Debug.WriteLine("Testing range: " + range.Text); if (!range.Positioned) { // Debug.WriteLine("ClearRange: Removing unpositioned word"); words.RemoveAt(idx--); } else if (clear.End.IsLeftOfOrEqualTo(range.Start)) { // Debug.WriteLine("We've gone far enough--all done"); return; } else if (clear.InRange(range)) { // Debug.WriteLine("ClearRange: Removing contained range"); words.RemoveAt(idx--); } else if (range.InRange(clear)) { // Debug.WriteLine("ClearRange: Splitting range"); MarkupRange trailingRange = range.Clone(); trailingRange.Start.MoveToPointer(clear.End); range.End.MoveToPointer(clear.Start); if (range.IsEmpty()) { words.RemoveAt(idx--); } if (!trailingRange.IsEmpty()) { Add(trailingRange); } } else if (range.InRange(clear.End, false)) { // Debug.WriteLine("ClearRange: Partial overlap, trimming from start of range"); words.RemoveAt(idx--); range.Start.MoveToPointer(clear.End); if (!range.IsEmpty()) { Add(range); } } else if (range.InRange(clear.Start, false)) { // Debug.WriteLine("ClearRange: Partial overlap, trimming from end of range"); range.End.MoveToPointer(clear.End); if (range.IsEmpty()) { words.RemoveAt(idx--); } } } // Debug.WriteLine("ClearRange: Remaining words in ignore list: " + words.Count); }