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(); }
/// <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 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; }
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); } }