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); } } } }
/// <summary> /// Creates an element with the specified tag. /// </summary> /// <param name="tagID">specifies the type of tag to create</param> /// <param name="attributes">specifies the attributes of the element</param> /// <returns>the newly created element</returns> public IHTMLElement CreateElement(_ELEMENT_TAG_ID tagID, string attributes) { IHTMLElement element; MarkupServices.CreateElement(tagID, attributes, out element); return(element); }
/// <summary> /// Returns the tag name given the identifier (ID). /// </summary> /// <param name="e"></param> /// <returns></returns> public string GetNameForTagId(_ELEMENT_TAG_ID tagId) { IntPtr p; MarkupServices.GetNameForTagID(tagId, out p); return(Marshal.PtrToStringBSTR(p)); }
internal SemanticHtmlElementInfo(string htmlName, _ELEMENT_TAG_ID tagId, string displayName, string htmlId, CommandId commandId) { _htmlName = htmlName; _tagId = tagId; _displayName = displayName; _htmlId = htmlId; _commandId = commandId; }
public void RemoveElementsByTagId(_ELEMENT_TAG_ID tagId, bool onlyIfNoAttributes) { if (tagId == _ELEMENT_TAG_ID.TAGID_NULL) { return; } // Remove the tagId up the parent chain IHTMLElement currentElement = ParentElement(); while (currentElement != null) { if (MarkupServices.GetElementTagId(currentElement) == tagId && (!onlyIfNoAttributes || !HTMLElementHelper.HasMeaningfulAttributes(currentElement))) { try { MarkupServices.RemoveElement(currentElement); } catch (COMException e) { Trace.Fail(String.Format("Failed to remove element ({0}) with error: {1}", currentElement.outerHTML, // {0} e // {1} )); } } currentElement = currentElement.parentElement; } // Remove any other instances IHTMLElement[] elements = GetElements(ElementFilters.CreateTagIdFilter(MarkupServices.GetNameForTagId(tagId)), false); foreach (IHTMLElement e in elements) { if (MarkupServices.GetElementTagId(e) == tagId && (!onlyIfNoAttributes || !HTMLElementHelper.HasMeaningfulAttributes(e))) { try { MarkupServices.RemoveElement(e); } catch (COMException ex) { Trace.Fail(String.Format("Failed to remove element ({0}) with error: {1}", e.outerHTML, // {0} ex // {1} )); } } } }
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 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 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 IHTMLElement WrapRangeInBlockElement(MarkupRange blockRegion, _ELEMENT_TAG_ID styleTagId) { MarkupRange insertionRange = _markupServices.CreateMarkupRange(); //create the new block element IHTMLElement newBlockElement = _markupServices.CreateElement(styleTagId, null); //insert the new block element in front of the block content insertionRange.Start.MoveToPointer(blockRegion.Start); insertionRange.End.MoveToPointer(blockRegion.Start); _markupServices.InsertElement(newBlockElement, insertionRange.Start, insertionRange.End); //move the block content inside the new block element insertionRange.Start.MoveAdjacentToElement(newBlockElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); blockRegion.Start.MoveAdjacentToElement(newBlockElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); _markupServices.Move(blockRegion.Start, blockRegion.End, insertionRange.Start); return(newBlockElement); }
private IHTMLElement WrapRangeInSpanElement(_ELEMENT_TAG_ID tagId, string attributes, MarkupRange spanRange) { MarkupRange insertionRange = _markupServices.CreateMarkupRange(); IHTMLElement newSpanElement = _markupServices.CreateElement(tagId, attributes); //insert the new span element in front of the span content insertionRange.Start.MoveToPointer(spanRange.Start); insertionRange.End.MoveToPointer(spanRange.Start); _markupServices.InsertElement(newSpanElement, insertionRange.Start, insertionRange.End); //move the span content inside the new span element insertionRange.Start.MoveAdjacentToElement(newSpanElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); spanRange.Start.MoveAdjacentToElement(newSpanElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); _markupServices.Move(spanRange.Start, spanRange.End, insertionRange.Start); return(newSpanElement); }
/// <summary> /// Initializes a new instance of the DefaultBlockElement class. /// </summary> /// <param name="tagId">The tag id associated with the default block element.</param> /// <param name="tagName">The tag name associated with the default block element.</param> /// <param name="numberOfNewLinesToReplace">The number of plain-text CRLFs that should be replaced when /// inserting the default block element. This is used when converting plain-text to HTML in order to maintain /// similar rendering between plain-text and HTML. Must be greater than zero.</param> protected DefaultBlockElement(_ELEMENT_TAG_ID tagId, string tagName, int numberOfNewLinesToReplace) { if (!Enum.IsDefined(typeof(_ELEMENT_TAG_ID), tagId)) { throw new ArgumentException("tagId"); } if (String.IsNullOrEmpty(tagName)) { throw new ArgumentException("tagName"); } if (numberOfNewLinesToReplace < 1) { throw new ArgumentOutOfRangeException("numberOfNewLinesToReplace"); } this.BeginTag = String.Format("<{0}>", tagName); this.EndTag = String.Format("</{0}>", tagName); this.TagId = tagId; this.TagName = tagName; this.NumberOfNewLinesToReplace = numberOfNewLinesToReplace; }
/// <summary> /// Determines if a range has a particular _ELEMENT_TAG_ID applied. /// </summary> /// <param name="tagId"></param> /// <param name="partially">If true, then IsTagId will return true if any part of it is contained within a tagId element. /// If false, then IsTagId will return true only if the range is entirely contained within a tagId element.</param> /// <returns></returns> public bool IsTagId(_ELEMENT_TAG_ID tagId, bool partially) { // This first block of code will return true if the range is entirely contained within an element with the given tagId. IHTMLElement currentElement = ParentElement(); while (currentElement != null) { if (MarkupServices.GetElementTagId(currentElement) == tagId) { return(true); } currentElement = currentElement.parentElement; } // This second block of code will return true if the range is partially contained within an element with the given tagId. if (partially) { IHTMLElement[] elements = GetElements(ElementFilters.CreateTagIdFilter(MarkupServices.GetNameForTagId(tagId)), false); return(elements.Length > 0); } return(false); }
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); } } }
private IHTMLElement WrapRangeInBlockElement(MarkupRange blockRegion, _ELEMENT_TAG_ID styleTagId) { MarkupRange insertionRange = _markupServices.CreateMarkupRange(); //create the new block element IHTMLElement newBlockElement = _markupServices.CreateElement(styleTagId, null); //insert the new block element in front of the block content insertionRange.Start.MoveToPointer(blockRegion.Start); insertionRange.End.MoveToPointer(blockRegion.Start); _markupServices.InsertElement(newBlockElement, insertionRange.Start, insertionRange.End); //move the block content inside the new block element insertionRange.Start.MoveAdjacentToElement(newBlockElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); blockRegion.Start.MoveAdjacentToElement(newBlockElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); _markupServices.Move(blockRegion.Start, blockRegion.End, insertionRange.Start); return newBlockElement; }
private MarkupRange ApplyInlineTagToBlockFreeSelection(_ELEMENT_TAG_ID tagId, string attributes, MarkupRange blockFreeSelection) { // @RIBBON TODO: May want to be a bit more sophisticated and eliminate redundant tags return(_markupServices.CreateMarkupRange(WrapRangeInSpanElement(tagId, attributes, blockFreeSelection))); }
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(); } }
/// <summary> /// Returns true if the element is not natively supported by MSHTML's editing commands and should therefore be /// replaced. /// </summary> /// <param name="element">The element to consider replacing.</param> /// <param name="tagId">The tag to replace the element with.</param> /// <returns>true if the element should be replaced and false otherwise.</returns> private bool TryGetReplacement(IHTMLElement element, out _ELEMENT_TAG_ID tagId) { if (element != null) { if (IsNonSupportedStrikeThroughElement(element)) { tagId = _ELEMENT_TAG_ID.TAGID_STRIKE; return true; } } tagId = _ELEMENT_TAG_ID.TAGID_UNKNOWN; return false; }
private void ApplyInlineTag(_ELEMENT_TAG_ID tagId, string attributes, bool toggle) { if (_initialDocumentLoaded) { MarkupRange selectedMarkupRange = SelectedMarkupRange; if (IsValidContentInsertionPoint(selectedMarkupRange)) { using (IUndoUnit undo = CreateUndoUnit()) { MarkupRange selection = SelectedMarkupRange; MarkupRange newSelection; using (DamageServices.CreateDamageTracker(selection, true)) { newSelection = HtmlStyleHelper.ApplyInlineTag(MarkupServices, tagId, attributes, selection, toggle); if (!newSelection.Positioned) { Debug.Fail("Shouldn't have applied inline tag because there was nothing to apply it to."); return; } } if (newSelection.IsEmpty()) { DisplayServices.TraceMoveToMarkupPointer(CaretPositionDisplayPointer, newSelection.Start); SynchronizeCaretWithDisplayPointer(CaretPositionDisplayPointer, true); } else { //reselect the selected text. newSelection.ToTextRange().select(); } undo.Commit(); } } } }
/// <summary> /// Returns the markup pointer to the position of the first exit scope of type tagId or type terminatingTagId which follows this markup range. /// Returns null if text exists between the range and such an exit scope, or if there is no such exit scope. /// </summary> /// <param name="terminatingTagId"></param> /// <returns></returns> internal MarkupPointer NextExitScopeWithoutInterveningText(MarkupRange selection, _ELEMENT_TAG_ID tagId, _ELEMENT_TAG_ID terminatingTagId, out bool primaryTagIdMatch) { MarkupContext context = new MarkupContext(); MarkupPointer pointer = selection.End.Clone(); primaryTagIdMatch = false; while (true) { pointer.Right(true, context); if (context.Element == null) { return(null); } switch (context.Context) { case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_None: return(null); case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope: { if (_markupServices.GetElementTagId(context.Element) == tagId) { primaryTagIdMatch = true; return(pointer); } if (terminatingTagId != _ELEMENT_TAG_ID.TAGID_NULL && terminatingTagId == _markupServices.GetElementTagId(context.Element)) { return(pointer); } } break; case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope: case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_NoScope: break; case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text: return(null); } } }
/// <summary> /// Creates an element with the specified tag. /// </summary> /// <param name="tagID">specifies the type of tag to create</param> /// <param name="attributes">specifies the attributes of the element</param> /// <returns>the newly created element</returns> public IHTMLElement CreateElement(_ELEMENT_TAG_ID tagID, string attributes) { IHTMLElement element; MarkupServices.CreateElement(tagID, attributes, out element); return element; }
/// <summary> /// Returns the markup pointer to the position of the first exit scope of type tagId or type terminatingTagId which follows this markup range. /// Returns null if text exists between the range and such an exit scope, or if there is no such exit scope. /// </summary> /// <param name="terminatingTagId"></param> /// <returns></returns> internal MarkupPointer NextExitScopeWithoutInterveningText(MarkupRange selection, _ELEMENT_TAG_ID tagId, _ELEMENT_TAG_ID terminatingTagId, out bool primaryTagIdMatch) { MarkupContext context = new MarkupContext(); MarkupPointer pointer = selection.End.Clone(); primaryTagIdMatch = false; while (true) { pointer.Right(true, context); if (context.Element == null) return null; switch (context.Context) { case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_None: return null; case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope: { if (_markupServices.GetElementTagId(context.Element) == tagId) { primaryTagIdMatch = true; return pointer; } if (terminatingTagId != _ELEMENT_TAG_ID.TAGID_NULL && terminatingTagId == _markupServices.GetElementTagId(context.Element)) { return pointer; } } break; case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope: case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_NoScope: break; case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text: return null; } } }
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 IHTMLElement WrapRangeInSpanElement(_ELEMENT_TAG_ID tagId, string attributes, MarkupRange spanRange) { MarkupRange insertionRange = _markupServices.CreateMarkupRange(); IHTMLElement newSpanElement = _markupServices.CreateElement(tagId, attributes); //insert the new span element in front of the span content insertionRange.Start.MoveToPointer(spanRange.Start); insertionRange.End.MoveToPointer(spanRange.Start); _markupServices.InsertElement(newSpanElement, insertionRange.Start, insertionRange.End); //move the span content inside the new span element insertionRange.Start.MoveAdjacentToElement(newSpanElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); spanRange.Start.MoveAdjacentToElement(newSpanElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); _markupServices.Move(spanRange.Start, spanRange.End, insertionRange.Start); return newSpanElement; }
private MarkupRange ApplyInlineTagToBlockFreeSelection(_ELEMENT_TAG_ID tagId, string attributes, MarkupRange blockFreeSelection) { // @RIBBON TODO: May want to be a bit more sophisticated and eliminate redundant tags return _markupServices.CreateMarkupRange(WrapRangeInSpanElement(tagId, attributes, blockFreeSelection)); }
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 SemanticHtmlGalleryItem(string elementName, _ELEMENT_TAG_ID elementTagId, string label, Bitmap bitmap) : base(label, bitmap, label) { this.elementName = elementName; this.elementTagId = elementTagId; }
public static void ApplyBlockStyle(HtmlEditorControl editor, _ELEMENT_TAG_ID styleTagId, MarkupRange selection, MarkupRange maximumBounds, MarkupRange postOpSelection) { new HtmlBlockFormatHelper(editor).ApplyBlockStyle(styleTagId, selection, maximumBounds, postOpSelection); }
/// <summary> /// Returns the tag name given the identifier (ID). /// </summary> /// <param name="e"></param> /// <returns></returns> public string GetNameForTagId(_ELEMENT_TAG_ID tagId) { IntPtr p; MarkupServices.GetNameForTagID(tagId, out p); return Marshal.PtrToStringBSTR(p); }
void IHtmlEditorCommandSource.ClearFormatting() { if (_initialDocumentLoaded) { MarkupRange selectedMarkupRange = SelectedMarkupRange; if (IsValidContentInsertionPoint(selectedMarkupRange)) { using (IUndoUnit undo = CreateUndoUnit()) { MarkupRange selection = SelectedMarkupRange; using (DamageServices.CreateDamageTracker(selection, true)) { try { // Note: that if the selection is non-empty, the mshtml command REMOVEFORMAT applies to word level. GetMshtmlCommand(IDM.REMOVEFORMAT).Execute(); // REMOVEFORMAT does not remove :h1, h2, h3, h4, h5, h6, ul, ol, li. // We have to remove these ourselves. // First, we change h1, h2, h3, h4, h5, h6, and li to p // Then we remove ul and ol. // Any list items should now be paragraph elements _ELEMENT_TAG_ID[] toBeParagraph = new _ELEMENT_TAG_ID[] { _ELEMENT_TAG_ID.TAGID_LI, _ELEMENT_TAG_ID.TAGID_H1, _ELEMENT_TAG_ID.TAGID_H2, _ELEMENT_TAG_ID.TAGID_H3, _ELEMENT_TAG_ID.TAGID_H4, _ELEMENT_TAG_ID.TAGID_H5, _ELEMENT_TAG_ID.TAGID_H6, }; if (!IsEditFieldSelected) { HtmlStyleHelper.ChangeElementTagIds(MarkupServices, selection, toBeParagraph, _ELEMENT_TAG_ID.TAGID_P); MarkupRange parentBlockRange = MarkupServices.CreateMarkupRange(selection.ParentBlockElement()); foreach (_ELEMENT_TAG_ID tagId in new[] {_ELEMENT_TAG_ID.TAGID_UL, _ELEMENT_TAG_ID.TAGID_OL, _ELEMENT_TAG_ID.TAGID_BLOCKQUOTE}) { parentBlockRange.RemoveElementsByTagId(tagId, false); } // We'll remove the alignment by removing the align attribute at block level HtmlStyleHelper.RemoveAttributes(MarkupServices, parentBlockRange, new[] { "align" }); } //reselect the selected text. if (selection.IsEmpty()) { DisplayServices.TraceMoveToMarkupPointer(CaretPositionDisplayPointer, selection.Start); SynchronizeCaretWithDisplayPointer(CaretPositionDisplayPointer, true); } else selection.ToTextRange().select(); } catch (Exception ex) { Trace.Fail("Exception thrown during ClearFormatting: " + ex); } } undo.Commit(); } } } }
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 HtmlElementFormattingStyle(string name, string element, _ELEMENT_TAG_ID elementTagId) { _name = name; _element = element; _elementTagId = elementTagId; }
public static IHTMLElement WrapRangeInElement(MshtmlMarkupServices services, MarkupRange range, _ELEMENT_TAG_ID tagId, string attributes) { IHTMLElement newElement = services.CreateElement(tagId, attributes); services.InsertElement(newElement, range.Start, range.End); return(newElement); }
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(); } }
private bool IsSelection(_ELEMENT_TAG_ID tagId) { if (_initialDocumentLoaded) { return SelectedMarkupRange.IsTagId(tagId, true); } return false; }
public void RemoveElementsByTagId(_ELEMENT_TAG_ID tagId, bool onlyIfNoAttributes) { if (tagId == _ELEMENT_TAG_ID.TAGID_NULL) return; // Remove the tagId up the parent chain IHTMLElement currentElement = ParentElement(); while (currentElement != null) { if (MarkupServices.GetElementTagId(currentElement) == tagId && (!onlyIfNoAttributes || !HTMLElementHelper.HasMeaningfulAttributes(currentElement))) { try { MarkupServices.RemoveElement(currentElement); } catch (COMException e) { Trace.Fail(String.Format("Failed to remove element ({0}) with error: {1}", currentElement.outerHTML, // {0} e // {1} )); } } currentElement = currentElement.parentElement; } // Remove any other instances IHTMLElement[] elements = GetElements(ElementFilters.CreateTagIdFilter(MarkupServices.GetNameForTagId(tagId)), false); foreach (IHTMLElement e in elements) { if (MarkupServices.GetElementTagId(e) == tagId && (!onlyIfNoAttributes || !HTMLElementHelper.HasMeaningfulAttributes(e))) { try { MarkupServices.RemoveElement(e); } catch (COMException ex) { Trace.Fail(String.Format("Failed to remove element ({0}) with error: {1}", e.outerHTML, // {0} ex // {1} )); } } } }
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 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)); }
public static IHTMLElement WrapRangeInElement(MshtmlMarkupServices services, MarkupRange range, _ELEMENT_TAG_ID tagId) { return WrapRangeInElement(services, range, tagId, string.Empty); }
public static IHTMLElement WrapRangeInElement(MshtmlMarkupServices services, MarkupRange range, _ELEMENT_TAG_ID tagId) { return(WrapRangeInElement(services, range, tagId, string.Empty)); }
public static IHTMLElement WrapRangeInElement(MshtmlMarkupServices services, MarkupRange range, _ELEMENT_TAG_ID tagId, string attributes) { IHTMLElement newElement = services.CreateElement(tagId, attributes); services.InsertElement(newElement, range.Start, range.End); return newElement; }
/// <summary> /// Determines if a range has a particular _ELEMENT_TAG_ID applied. /// </summary> /// <param name="tagId"></param> /// <param name="partially">If true, then IsTagId will return true if any part of it is contained within a tagId element. /// If false, then IsTagId will return true only if the range is entirely contained within a tagId element.</param> /// <returns></returns> public bool IsTagId(_ELEMENT_TAG_ID tagId, bool partially) { // This first block of code will return true if the range is entirely contained within an element with the given tagId. IHTMLElement currentElement = ParentElement(); while (currentElement != null) { if (MarkupServices.GetElementTagId(currentElement) == tagId) return true; currentElement = currentElement.parentElement; } // This second block of code will return true if the range is partially contained within an element with the given tagId. if (partially) { IHTMLElement[] elements = GetElements(ElementFilters.CreateTagIdFilter(MarkupServices.GetNameForTagId(tagId)), false); return elements.Length > 0; } return false; }