/// <summary> /// Makes sure that whole (not parts of) lists are included in the source of a paste. /// </summary> /// <param name="range">The original source range. The range may be modified.</param> /// <param name="markupServices">MarkupServices for the range.</param> private void ExpandToIncludeLists(MarkupRange range, MshtmlMarkupServices markupServices) { MarkupPointer pointer = markupServices.CreateMarkupPointer(); IHTMLElementFilter listFilter = ElementFilters.CreateCompoundElementFilter(ElementFilters.LIST_ELEMENTS, ElementFilters.LIST_ITEM_ELEMENTS); IHTMLElement[] listElements = range.GetElements(listFilter, false); foreach (IHTMLElement element in listElements) { IHTMLElement parentList = element; while (parentList != null && !ElementFilters.IsListElement(parentList)) { parentList = parentList.parentElement; } if (parentList != null) { pointer.MoveAdjacentToElement(parentList, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); if (range.Start.IsRightOf(pointer)) { range.Start.MoveToPointer(pointer); } pointer.MoveAdjacentToElement(parentList, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); if (range.End.IsLeftOf(pointer)) { range.End.MoveToPointer(pointer); } } } }
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 void ApplyBlockFormatToEmptySelection(MarkupRange selection, _ELEMENT_TAG_ID styleTagId, MarkupRange maximumBounds) { bool deleteParentBlock = false; //expand the selection to include the parent content block. If the expansion can cover the block element //without exceeding the maximum bounds, then delete the parent element and wrap the selection in the //new block element. If the maximum bounds are exceeded, then just wrap the selection around the bounds. IHTMLElementFilter stopFilter = ElementFilters.CreateCompoundElementFilter(ElementFilters.BLOCK_ELEMENTS, new IHTMLElementFilter(IsSplitStopElement)); MovePointerLeftUntilRegionBreak(selection.Start, stopFilter, maximumBounds.Start); MovePointerRightUntilRegionBreak(selection.End, stopFilter, maximumBounds.End); MarkupRange tmpRange = selection.Clone(); tmpRange.End.MoveToPointer(selection.Start); IHTMLElement startStopParent = tmpRange.End.GetParentElement(stopFilter); if (startStopParent != null) { tmpRange.Start.MoveAdjacentToElement(startStopParent, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); if (tmpRange.IsEmptyOfContent()) //the range from the selection the the block start is empty { tmpRange.Start.MoveToPointer(selection.End); IHTMLElement endStopParent = tmpRange.Start.GetParentElement(stopFilter); if (endStopParent != null && startStopParent.sourceIndex == endStopParent.sourceIndex) { tmpRange.Start.MoveAdjacentToElement(endStopParent, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd); if (tmpRange.IsEmptyOfContent()) //the range from the selection the the block end is empty { tmpRange.MoveToElement(endStopParent, true); if (maximumBounds.InRange(tmpRange) && !(endStopParent is IHTMLTableCell)) { deleteParentBlock = true; //the parent has no useful content outside the selection, so it's safe to delete } } } } } //delete the block parent (if appropriate) and wrap the selection in the new block element. if (deleteParentBlock) { (startStopParent as IHTMLDOMNode).removeNode(false); } IHTMLElement newBlock = WrapRangeInBlockElement(selection, styleTagId); selection.MoveToElement(newBlock, false); }
private void DeleteInsertionTargetBlockIfEmpty(MarkupPointer insertionPoint) { //locate the parent block element (stop at post body element) IHTMLElement parent = insertionPoint.GetParentElement( ElementFilters.CreateCompoundElementFilter(ElementFilters.CreateElementEqualsFilter(HTMLElement), ElementFilters.BLOCK_ELEMENTS)); if (parent != null && parent.sourceIndex != HTMLElement.sourceIndex && //never remove the post body block EditorContext.MarkupServices.CreateMarkupRange(parent, false).IsEmptyOfContent()) { //delete the empty parent block element (parent as IHTMLDOMNode).removeNode(true); } }
public static void RemoveAttributes(MshtmlMarkupServices markupServices, MarkupRange selection, string[] attributesToRemove) { IHTMLElementFilter[] filters = new IHTMLElementFilter[attributesToRemove.Length]; for (int i = 0; i < attributesToRemove.Length; i++) { filters[i] = ElementFilters.CreateElementAttributeFilter(attributesToRemove[i]); } IHTMLElement[] elements = selection.GetElements(ElementFilters.CreateCompoundElementFilter(filters), false); foreach (IHTMLElement element in elements) { foreach (string attribute in attributesToRemove) { element.removeAttribute(attribute, 0); } } }
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 void ApplyBlockStyleToRange(_ELEMENT_TAG_ID styleTagId, MarkupRange range, MarkupRange maximumBounds) { //update the range cling and gravity so it will stick with the re-arranged block content range.Start.PushCling(false); range.Start.PushGravity(_POINTER_GRAVITY.POINTER_GRAVITY_Left); range.End.PushCling(false); range.End.PushGravity(_POINTER_GRAVITY.POINTER_GRAVITY_Right); try { MarkupPointer deeperPoint = GetDeeperPoint(range.Start, range.End); MarkupPointer insertionPoint = _markupServices.CreateMarkupPointer(deeperPoint); insertionPoint.Cling = false; //if the insertion point parent block contains content, split the block. If the parent //block is now empty, then just delete the parent block. IHTMLElement parentBlock = insertionPoint.GetParentElement( ElementFilters.CreateCompoundElementFilter(ElementFilters.BLOCK_ELEMENTS, new IHTMLElementFilter(IsSplitStopElement))); //temporarily stage the range content at the end of the document so that the split //operation doesn't damage the original range content MarkupRange stagedBlockContent = _markupServices.CreateMarkupRange(); stagedBlockContent.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left; stagedBlockContent.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right; stagedBlockContent.Start.MoveAdjacentToElement(GetBodyElement(), _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd); stagedBlockContent.End.MoveToPointer(stagedBlockContent.Start); MarkupPointer stagedBlockInsertionPoint = _markupServices.CreateMarkupPointer(GetBodyElement(), _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd); stagedBlockInsertionPoint.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right; // Pass over all opening elements between parent's adj_beforeend and the start of selection (range.start) // Any element (_enterscope) that is not closed before the close of selection is essentially // containing the selection completely, and needs to be copied into the staging area. // ex: <p><a href="http://msn.com">abc[selection]def</a></p> // Here, the <a> element encloses completely the selection and needs to be explicitly copied to the // staging area. for (MarkupPointer i = _markupServices.CreateMarkupPointer(parentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); i.IsLeftOf(range.Start); i.Right(true)) { MarkupContext iContext = i.Right(false); if (iContext.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope && iContext.Element is IHTMLAnchorElement) { MarkupPointer j = _markupServices.CreateMarkupPointer(iContext.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd); if (j.IsRightOfOrEqualTo(range.End)) { // Copy the tag at posn. i to location stagedBlockInsertionPoint // This is openning tag. Closing tag will be // automatically added by MSHTML. MarkupPointer i1 = i.Clone(); i1.Right(true); _markupServices.Copy(i, i1, stagedBlockInsertionPoint); // Skip over the closing tag, so stagedBlockInsertionPoint points between the openning and the closing stagedBlockInsertionPoint.Left(true); } j.Unposition(); } } //move the range content into the staged position _markupServices.Move(range.Start, range.End, stagedBlockInsertionPoint); stagedBlockInsertionPoint.Unposition(); bool splitBlock = !RemoveEmptyParentBlock(range.Clone(), parentBlock, maximumBounds); // If the range endpoint NOT chosen as the insertion point lies in a different block element, and // that parent block element is now empty, then just delete the parent block. MarkupPointer shallowerPoint = (deeperPoint.IsEqualTo(range.Start)) ? range.End : range.Start; MarkupPointer nonInsertionPoint = _markupServices.CreateMarkupPointer(shallowerPoint); IHTMLElement otherParentBlock = nonInsertionPoint.GetParentElement( ElementFilters.CreateCompoundElementFilter(ElementFilters.BLOCK_ELEMENTS, new IHTMLElementFilter(IsSplitStopElement))); if (otherParentBlock.sourceIndex != parentBlock.sourceIndex) { RemoveEmptyParentBlock(range.Clone(), otherParentBlock, maximumBounds); } if (splitBlock) { //split the block at the insertion point SplitBlockForApplyingBlockStyles(insertionPoint, maximumBounds); } //move the staged block content back to the insertion point and setup the range pointers //to wrap the re-inserted block content. range.Start.MoveToPointer(insertionPoint); range.End.MoveToPointer(insertionPoint); _markupServices.Move(stagedBlockContent.Start, stagedBlockContent.End, insertionPoint); //Note: the range is now re-positioned around the same content, but all of the parent //elements have been closed to prepare for getting new parent block elements around the //range //convert the range's content into block regions and remove block elements that were //parenting the regions. InnerBlockRegion[] blockRegions = GetNormalizedBlockContentRegions(range); //update all of the block regions with the desired new block style element foreach (InnerBlockRegion blockRegion in blockRegions) { IHTMLElement newParentElement = WrapRangeInBlockElement(blockRegion.ContentRange, styleTagId); // The old parent element may have had an alignment set on it. IHTMLElement oldParentElement = blockRegion.OldParentElement ?? parentBlock; if (oldParentElement != null) { string oldAlignment = oldParentElement.getAttribute("align", 2) as string; if (!String.IsNullOrEmpty(oldAlignment)) { newParentElement.setAttribute("align", oldAlignment, 0); } } } } finally { range.Start.PopCling(); range.Start.PopGravity(); range.End.PopCling(); range.End.PopGravity(); } }