private static void NormalizeBounds(ref MarkupRange bounds) { bool cloned = false; if (bounds.Start.IsRightOf(bounds.End)) { if (!cloned) { cloned = true; bounds = bounds.Clone(); } bounds.Normalize(); } MarkupContext ctx = bounds.Start.Right(false); while (ctx.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope && ElementFilters.IsBlockElement(ctx.Element)) { if (!cloned) { cloned = true; bounds = bounds.Clone(); } bounds.Start.Right(true); bounds.Start.Right(false, ctx); } }
/// <summary> /// Returns the maximum range that can be safely considered equivalent to this range (without bringing new text into the range). /// </summary> /// <param name="range"></param> /// <returns></returns> private MarkupRange CreateMaxSafeRange(MarkupRange range) { MarkupRange maxRange = range.Clone(); SelectOuter(maxRange); return(maxRange); }
public bool Contains(MarkupRange testRange) { // Select just the inner-most text to normalize the MarkupRange before comparing it. testRange.SelectInner(); int pos = BinarySearch(words.Keys, testRange.Start.PointerRaw, new MarkupPointerComparer()); if (pos >= 0) { if (words.Values[pos].End.IsLeftOf(testRange.End)) { Debug.Fail("testRange partially overlaps with range"); try { Debug.WriteLine("testRange: [" + testRange.Text + "]"); Debug.WriteLine("thisRange: [" + words.Values[pos].Text + "]"); } catch (Exception e) { Debug.WriteLine(e.ToString()); } } return(true); } pos = (~pos) - 1; if (pos < 0) { return(false); } if (words.Values[pos].End.IsRightOf(testRange.Start)) { if (words.Values[pos].End.IsLeftOf(testRange.End)) { #if DEBUG MarkupRange temp = testRange.Clone(); temp.Start.MoveToPointer(words.Values[pos].End); if ((temp.Text ?? "").Trim().Length > 0) { Debug.Fail("testRange partially overlaps with range"); try { Debug.WriteLine("testRange: [" + testRange.Text + "]"); Debug.WriteLine("thisRange: [" + words.Values[pos].Text + "]"); Debug.WriteLine("overlap: [" + temp.Text + "]"); } catch (Exception e) { Debug.WriteLine(e.ToString()); } } #endif } return(true); } return(false); }
/// <summary> /// Fixes up all the headers in the entire markupRange. /// </summary> /// <param name="turnBold">Whether or not the text should be turning bold.</param> private void FixupHeaders(bool turnBold) { IHTMLElement elementStartHeader = markupRange.Start.GetParentElement(ElementFilters.HEADER_ELEMENTS); IHTMLElement elementEndHeader = markupRange.End.GetParentElement(ElementFilters.HEADER_ELEMENTS); MarkupRange currentRange = markupRange.Clone(); if (elementStartHeader != null) { // Takes care of the following cases: // <h1>...|blah|...</h1> // <h1>...|blah...</h1>...|... MarkupRange startRange = markupServices.CreateMarkupRange(elementStartHeader, false); startRange.Start.MoveToPointer(markupRange.Start); if (startRange.End.IsRightOf(markupRange.End)) { startRange.End.MoveToPointer(markupRange.End); } FixupHeaderRange(startRange, turnBold); currentRange.Start.MoveAdjacentToElement(elementStartHeader, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); if (currentRange.End.IsLeftOf(currentRange.Start)) { currentRange.End.MoveToPointer(currentRange.Start); } } if (elementEndHeader != null && !HTMLElementHelper.ElementsAreEqual(elementStartHeader, elementEndHeader)) { // Takes care of the following case: // ...|...<h1>...blah|...</h1> MarkupRange endRange = markupServices.CreateMarkupRange(elementEndHeader, false); endRange.End.MoveToPointer(markupRange.End); FixupHeaderRange(endRange, turnBold); currentRange.End.MoveAdjacentToElement(elementEndHeader, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); if (currentRange.Start.IsRightOf(currentRange.End)) { currentRange.Start.MoveToPointer(currentRange.End); } } if (!markupRange.InRange(currentRange)) { return; } IHTMLElement[] headerElements = currentRange.GetElements(ElementFilters.HEADER_ELEMENTS, true); if (headerElements != null && headerElements.Length > 0) { foreach (IHTMLElement element in headerElements) { MarkupRange headerRange = markupServices.CreateMarkupRange(element, false); FixupHeaderRange(headerRange, turnBold); } } }
internal void AddDamage(MarkupRange range, bool includeAdjacentWords) { MarkupRange wordRange = range.Clone(); _wordHelper.MoveToWordStart(wordRange.Start); _wordHelper.MoveToWordEnd(wordRange.End); if (includeAdjacentWords) { ExpandDamageToAdjacentWords(wordRange); } _AddDamage(wordRange); }
private string GetHtmlText(out MarkupPointer blockBoundary) { MarkupRange blockRange = _blogPostHtmlEditorControl.SelectedMarkupRange.Clone(); MarkupRange textRange = blockRange.Clone(); IHTMLElement ele = blockRange.ParentElement(ElementFilters.IsBlockOrTableCellOrBodyElement); if (ele == null) { blockBoundary = null; return(String.Empty); } blockRange.MoveToElement(ele, false); blockBoundary = blockRange.Start; // Fix Bug 616152 - We want the start and end pointer to match so // we can look back from the start of the insertion point (not the end, // which would include the selection that is going to be overwritten) textRange.Collapse(true); // Fix bug WinLive 59172: Specific characters of mixed characters are broken in mail body after press the 'Enter' key // Caused by using MOVEUNIT_PREVCHAR to navigate into Unicode surrogate (32-bit) characters. We work around this by moving // only at word or block boundaries. string html = null; do { int startPos = textRange.Start.MarkupPosition; textRange.Start.MoveUnitBounded(_MOVEUNIT_ACTION.MOVEUNIT_PREVWORDBEGIN, blockBoundary); if (textRange.Start.MarkupPosition == startPos) { // PREVWORDBEGIN didn't actually move us, due to no word being available. // To avoid an infinite loop, just move the text range to include the whole // block and move on. textRange.Start.MoveToPointer(blockRange.Start); break; } if (textRange.Positioned && textRange.Start.IsLeftOfOrEqualTo(textRange.End)) { html = MarkupHelpers.GetRangeTextFast(textRange); } html = html ?? string.Empty; } while (html.Length < MinLookBack && textRange.Start.IsRightOf(blockRange.Start)); if (html == null && textRange.Positioned && textRange.Start.IsLeftOfOrEqualTo(textRange.End)) { html = MarkupHelpers.GetRangeTextFast(textRange); } return(html ?? string.Empty); }
public TypographicCharacterHandler(MarkupRange currentSelection, InsertHtml insertHtml, IBlogPostImageEditingContext imageEditingContext, IHTMLElement postBodyElement, char c, string htmlText, MarkupPointer blockBoundary) { _currentSelection = currentSelection.Clone(); _currentSelection.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right; _currentSelection.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left; _insertHtml = insertHtml; _imageEditingContext = imageEditingContext; _postBodyElement = postBodyElement; this.c = c; this.htmlText = htmlText; this.blockBoundary = blockBoundary; }
private bool IsRangeInUrl(MarkupRange range) { //must have this range cloned, otherwise in some cases IsInsideURL call // was MOVING the current word range! if "www.foo.com" was in the editor, // when the final "\"" was the current word, this call MOVED the current // word range BACK to www.foo.com, then nextWord would get "\"" and a loop // would occur (bug 411528) range = range.Clone(); IMarkupPointer2Raw p2StartRaw = (IMarkupPointer2Raw)range.Start.PointerRaw; bool insideUrl; p2StartRaw.IsInsideURL(range.End.PointerRaw, out insideUrl); return(insideUrl); }
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 bool ValidateTableSelection(IHTMLTable table, MarkupRange selectionMarkupRange, out bool tableFullySelected) { // assume table is not fully selected tableFullySelected = false; // first check to see that this is a "Writer" editable table if (!TableHelper.TableElementContainsWriterEditingMark(table as IHTMLElement)) { return(false); } // get elemental objects we need to analyze the table IHTMLElement tableElement = table as IHTMLElement; MarkupRange tableMarkupRange = selectionMarkupRange.Clone(); tableMarkupRange.MoveToElement(table as IHTMLElement, true); // analyze selection bool selectionAtTableStart = tableMarkupRange.Start.IsEqualTo(selectionMarkupRange.Start); bool selectionAtTableEnd = tableMarkupRange.End.IsEqualTo(selectionMarkupRange.End); // is the table fully selected? if (selectionAtTableStart && selectionAtTableEnd) { tableFullySelected = true; return(true); } else { MarkupRange selectionMarkupRange2 = selectionMarkupRange.Clone(); // is the selection bounded by the table IHTMLElement beginParentTable = selectionMarkupRange2.Start.SeekElementLeft(ElementFilters.CreateEqualFilter(tableElement)); IHTMLElement endParentTable = selectionMarkupRange2.End.SeekElementRight(ElementFilters.CreateEqualFilter(tableElement)); return(beginParentTable != null && endParentTable != null); } }
private void ExpandDamageToPreviousWord(MarkupRange damageRange) { MarkupRange previousWordRange = damageRange.Clone(); previousWordRange.Start.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_PREVWORDBEGIN); // If we were already at the first word in the document, moving to the previous word would have put the // MarkupPointer outside the body element. if (previousWordRange.Start.GetParentElement(ElementFilters.BODY_ELEMENT) != null) { previousWordRange.Collapse(true); previousWordRange.End.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_NEXTWORDEND); if (!previousWordRange.IsEmpty() && !_editorControl.IgnoreRangeForSpellChecking(previousWordRange)) { damageRange.Start.MoveToPointer(previousWordRange.Start); } } }
public void Add(MarkupRange range) { MarkupRange newRange = range.Clone(); newRange.Start.Cling = false; newRange.End.Cling = false; newRange.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right; newRange.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left; // Select just the inner-most text so that it is easier to compare to other MarkupRanges. newRange.SelectInner(); try { words.Add(newRange.Start.PointerRaw, newRange); } catch (ArgumentException ex) { Trace.Fail(ex.ToString()); } }
private void SplitBlockForApplyingBlockStyles(MarkupPointer splitPoint, MarkupRange maximumBounds) { //find the split stop parent IHTMLElement splitStop = splitPoint.GetParentElement(new IHTMLElementFilter(IsSplitStopElement)); if (splitStop != null) { MarkupPointer stopLocation = _markupServices.CreateMarkupPointer(splitStop, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); if (maximumBounds.InRange(stopLocation)) { stopLocation.MoveAdjacentToElement(splitStop, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd); if (maximumBounds.InRange(stopLocation)) { maximumBounds = maximumBounds.Clone(); maximumBounds.MoveToElement(splitStop, false); } } } MarkupHelpers.SplitBlockForInsertionOrBreakout(_markupServices, maximumBounds, splitPoint); }
public void HighlightSpelling(MarkupRange range) { // check spelling MshtmlWordRange wordRange; if (range == null) //check the whole document. { wordRange = new MshtmlWordRange(_htmlDocument, false, _filter, _damageFunction); } else { //range is invalid for some reason--damage committed while switching views, getting it later on the timer if (!range.Positioned) { return; } else if (range.Text == null || String.IsNullOrEmpty(range.Text.Trim())) { //empty range--on a delete for instance, just clear _spellingHighlighter.ClearRange(range.Start, range.End); _ignoredOnce.ClearRange(range); return; } else { MarkupRange origRange = range.Clone(); //here are the words to check wordRange = new MshtmlWordRange(_htmlDocument, range, _filter, _damageFunction); //check for emptiness at start and end, clear those _spellingHighlighter.ClearRange(origRange.Start, range.Start); _spellingHighlighter.ClearRange(range.End, origRange.End); _ignoredOnce.ClearRange(range); } } _spellingHighlighter.CheckSpelling(wordRange); }
public FixupSegment(MarkupRange rangeToFixup, TextStyle sourceTextStyle) { if (rangeToFixup == null) { throw new ArgumentNullException("rangeToFixup"); } if (!rangeToFixup.Positioned) { throw new ArgumentException("rangeToFixup must be positioned!"); } if (sourceTextStyle == null) { throw new ArgumentNullException("sourceTextStyle"); } SourceTextStyle = sourceTextStyle; RangeToFixup = rangeToFixup.Clone(); // We want the RangeToFixup to stick with the text its wrapped around. RangeToFixup.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right; RangeToFixup.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left; }
public BoldApplier(MshtmlMarkupServices markupServices, MarkupRange markupRange, IMshtmlCommand boldCommand) { this.markupServices = markupServices; this.markupRange = markupRange.Clone(); this.boldCommand = boldCommand; }
/// <summary> /// Fixes up a range that is contained in a single header element. /// </summary> /// <param name="range">A range that is contained in a single header element.</param> /// <param name="turnBold">Whether or not the text should be turning bold.</param> private void FixupHeaderRange(MarkupRange range, bool turnBold) { IHTMLElement parentHeaderElement = range.ParentBlockElement(); if (parentHeaderElement == null || !ElementFilters.IsHeaderElement(parentHeaderElement)) { Debug.Fail("Expected entire range to be inside a single header element."); return; } MarkupRange expandedRange = range.Clone(); // Make sure we expand the selection to include any <font> tags that may be wrapping us. MarkupPointerMoveHelper.MoveUnitBounded(expandedRange.Start, MarkupPointerMoveHelper.MoveDirection.LEFT, MarkupPointerAdjacency.BeforeVisible, parentHeaderElement); MarkupPointerMoveHelper.MoveUnitBounded(expandedRange.End, MarkupPointerMoveHelper.MoveDirection.RIGHT, MarkupPointerAdjacency.BeforeVisible, parentHeaderElement); // Walks in-scope elements and clears out any elements or styles that might affect the bold formatting. var elementsToRemove = new List <IHTMLElement>(); expandedRange.WalkRange( delegate(MarkupRange currentexpandedRange, MarkupContext context, string text) { IHTMLElement currentElement = context.Element; if (currentElement != null && context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope) { if (IsStrongOrBold(currentElement)) { elementsToRemove.Add(currentElement); } else if (IsFontableElement(currentElement) && HTMLElementHelper.IsBold((IHTMLElement2)currentElement) != turnBold) { currentElement.style.fontWeight = String.Empty; } } return(true); }, true); elementsToRemove.ForEach(e => markupServices.RemoveElement(e)); // Walks the range to find any segments of text that need to be fixed up. var rangesToWrap = new List <MarkupRange>(); range.WalkRange( delegate(MarkupRange currentRange, MarkupContext context, string text) { if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text) { TextStyles currentTextStyles = new TextStyles(currentRange.Start); if (currentTextStyles.Bold != turnBold) { rangesToWrap.Add(currentRange.Clone()); } } return(true); }, true); rangesToWrap.ForEach(r => WrapRangeInFontIfNecessary(r, turnBold)); }
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 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(); } }
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); }
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(); } }
public SelectionPreserver(MarkupRange selectedMarkupRange) { _preservedMarkupRange = selectedMarkupRange.Clone(); _preservedMarkupRange.Start.Cling = true; _preservedMarkupRange.End.Cling = true; }