private static void MoveUnitBounded(MarkupPointer p, MoveDirection direction, MoveContextFilter continueFilter, MarkupPointer boundary) { MarkupPointer p1 = p.Clone(); MarkupPointer lastGoodPosition = p.Clone(); MarkupContext context = new MarkupContext(); MoveFilterResult result = MoveFilterResult.CONTINUE; while (CheckMoveBoundary(p1, boundary, direction) && result == MoveFilterResult.CONTINUE) { lastGoodPosition.MoveToPointer(p1); MovePointer(p1, direction, context); result = continueFilter(context); } if (result == MoveFilterResult.CONTINUE) { //we hit the boundary, so position pointer at the boundary p1.MoveToPointer(boundary); } else if (result == MoveFilterResult.STOP_BACK) { p1.MoveToPointer(lastGoodPosition); } p.MoveToPointer(p1); }
/// <summary> /// Creates an instance of the IMarkupPointer object with an initial position /// at the same location as another pointer. /// </summary> /// <param name="initialPosition"></param> /// <returns></returns> public MarkupPointer CreateMarkupPointer(MarkupPointer initialPosition) { MarkupPointer pointer = CreateMarkupPointer(); pointer.MoveToPointer(initialPosition); return(pointer); }
/// <summary> /// Walk through the markup range in reverse, letting the walker visit each position. /// </summary> /// <param name="walker">the delegate walking navigating the the markup range</param> /// <param name="inScopeElementsOnly">if true, enter/exit notifications about out-of-scope elements will be suppressed.</param> /// <returns></returns> public void WalkRangeReverse(MarkupRangeWalker walker, bool inScopeContextsOnly) { MarkupPointer p1 = MarkupServices.CreateMarkupPointer(End); MarkupPointer p2 = MarkupServices.CreateMarkupPointer(End); p1.Cling = false; p2.Cling = false; MarkupContext context = new MarkupContext(); bool continueWalking = true; MarkupRange currentRange = null; while (continueWalking && p2.IsRightOf(Start)) { string text = null; bool isInScope = true; p2.Left(true, context); currentRange = new MarkupRange(p2.Clone(), p1.Clone(), MarkupServices); if (inScopeContextsOnly) { if (context.Element != null) { if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope) { p1.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); isInScope = InRange(p1); } else if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope) { p1.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); isInScope = InRange(p1); } } else if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text) { // It's possible part of the text is out of scope, so only return the in-scope text. if (currentRange.Start.IsLeftOf(Start)) { currentRange.Start.MoveToPointer(Start); } } } if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text) { text = currentRange.Text; } if (!inScopeContextsOnly || isInScope) { continueWalking = walker(currentRange, context, text); } p1.MoveToPointer(p2); } }
public void ExpandToInclude(MarkupRange range) { if (range == null) { return; } if (Positioned) { if (range.Start.IsLeftOf(Start)) { Start.MoveToPointer(range.Start); } if (range.End.IsRightOf(End)) { End.MoveToPointer(range.End); } } else { MoveToRange(range); } }
public void MoveToWordEnd(MarkupPointer p) { lock (_p) { _p.MoveToPointer(p); _p2.MoveToPointer(p); _p.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_NEXTWORDBEGIN); _p2.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_NEXTWORDEND); // If CurrentScope is null, this means we have walked off the end of the // document, in that case we don't want to move the pointer, at is already // at the end of word. if (_p2.IsLeftOfOrEqualTo(_p) && _p.CurrentScope != null) p.MoveToPointer(_p2); //else, the pointer is already at the end of the current word } }
public void MoveToWordStart(MarkupPointer p) { lock (_p) { _p.MoveToPointer(p); _p2.MoveToPointer(p); _p.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_PREVWORDEND); _p2.MoveUnit(_MOVEUNIT_ACTION.MOVEUNIT_PREVWORDBEGIN); if (_p2.IsRightOfOrEqualTo(_p)) p.MoveToPointer(_p2); //else, the pointer is already at the start of the current word } }
private bool OverwriteDestinationBlockIfEmpty(MarkupPointer destinationStart, MarkupRange bounds) { bool blockOverwritten = false; IHTMLElement parentBlockElement = destinationStart.GetParentElement(ElementFilters.BLOCK_ELEMENTS); if (parentBlockElement != null) { MarkupRange parentBlockRange = MarkupServices.CreateMarkupRange(parentBlockElement, false); if (bounds.InRange(parentBlockRange, false)) { if (PrimaryEditableBounds == null || PrimaryEditableBounds.InRange(parentBlockRange)) { if (parentBlockRange.IsEmptyOfContent()) { parentBlockRange.MoveToElement(parentBlockElement, true); DeleteContentNoCling(parentBlockRange.Start, parentBlockRange.End); // Check to see if the delete we just did caused the insertion point to be // to become a no longer positioned, and if it did we move it to the start // of what we deleted if (!destinationStart.Positioned) { //Debug.WriteLine("Invalid pointer after delete, moving the target pointer to the start of the deleted markup."); destinationStart.MoveToPointer(parentBlockRange.Start); } blockOverwritten = true; } } } } return blockOverwritten; }
protected virtual void InsertHtml(MarkupPointer start, MarkupPointer end, string html, string sourceUrl, bool allowBlockBreakout) { MarkupRange range = MarkupServices.CreateMarkupRange(start, end); if (!IsValidContentInsertionPoint(range)) { DisplayMessage.Show(MessageId.InvalidInsertionPoint); return; } Trace.Assert(start.Positioned && end.Positioned, string.Format(CultureInfo.InvariantCulture, "Invalid pointer being used for insert. start:({0}),end:({1})", start.Positioned, end.Positioned)); // begin undo unit IUndoUnit undoUnit = CreateUndoUnit(); start.PushCling(true); end.PushCling(true); using (undoUnit) { // Any changes to the way we remove the content in the destination may need to be changed in // KeepSourceFormatting.PasteSourceOverDestination as well! MarkupPointerMoveHelper.PerformImageBreakout(start); MarkupPointerMoveHelper.PerformImageBreakout(end); //if the start and endpoints are not equal, then we need to paste over the selection //so delete the selected region (which will collapse the pointers to now be equal. if (!start.IsEqualTo(end)) { //delete the selected content if (ContentIsDeletableForInsert(start, end)) { // CT: There is currently a case where we leave empty blocks behind // see bug 628054. This happens when the start and end markup pointers don't completely // contain the selected blocks, like: <p>|line1</p><p>line2|</p>. In this case, calling // deleteNoContentNoClient will leave you with <p>|</p><p>|</p>. The next line collapses // the end pointer back to the start point since that is where selection started. DeleteContentNoCling(start, end); end.MoveToPointer(start); } } if (!string.IsNullOrEmpty(html)) { //Note: we use MarkupServices to insert the content so that IE doesn't try to fix up URLs. //Element.insertAdjacentHTML() is a no-no because it rewrites relaive URLs to include //the fullpath from the local filesytem. //MarkupServices.ParseString() doesn't attempt to fix up URLs, so its safe to use. //We will now stage the new content into a MarkupContainer, and then move it into //the working document. MarkupPointer sc1 = MarkupServices.CreateMarkupPointer(); MarkupPointer sc2 = MarkupServices.CreateMarkupPointer(); // Do the work ahead of time to get an <p></P> ready to be inserted // doing this work after the insert this html was called with sometimes // causes mshtml to not paint things until they are moused over(embeds) // BUG: 624122, 622715 MarkupPointer mpStart = MarkupServices.CreateMarkupPointer(); MarkupPointer mpEnd = MarkupServices.CreateMarkupPointer(); // Make a temp document and load our ending html into it. MarkupServices.ParseString(CONTENT_BODY_PADDING, mpStart, mpEnd); //Create a temporary document from the html and set the start/end pointers to the //start and end of the document. MarkupServices.ParseString(html, sc1, sc2); IHTMLDocument2 doc = sc1.GetDocument(); MarkupRange stagingRange = MarkupServices.CreateMarkupRange(sc1, sc2); stagingRange.MoveToElement(doc.body, false); // We only need to insert the ending new line if there was a div or image added bool allowNewLineInsert = ShouldAllowNewLineInsert(html); Trace.Assert(stagingRange.Positioned && stagingRange.Start.Positioned && stagingRange.End.Positioned && sc1.Positioned && sc2.Positioned, String.Format(CultureInfo.InvariantCulture, "Staging document is not ready. stagingRange:({0}),stagingRange.Start:({1}),stagingRange.End:({2}),sc1:({3}),sc2:({4})", stagingRange.Positioned, stagingRange.Start.Positioned, stagingRange.End.Positioned, sc1.Positioned, sc2.Positioned)); try { // Any changes to the way we remove the content in the destination may need to be changed in // KeepSourceFormatting.PasteSourceOverDestination as well! bool emptyBlockRemoved; if (stagingRange.ContainsElements(ElementFilters.IsBlockOrTableElement)) { // if the destination is an empty block element then just overwrite it emptyBlockRemoved = OverwriteDestinationBlockIfEmpty(start, PrimaryEditableBounds); if (!emptyBlockRemoved && allowBlockBreakout) { // otherwise split the destination block or move outside of the block MarkupHelpers.SplitBlockForInsertionOrBreakout(MarkupServices, PrimaryEditableBounds, start); } end.MoveToPointer(start); } } catch (COMException ex) { Trace.WriteLine(String.Format(CultureInfo.InvariantCulture, "RemoveBlockOrTableElement Failed ({0},{1},{2},{4}): {3}", stagingRange.Start.Positioned, stagingRange.End.Positioned, end.Positioned, ex, start.Positioned)); throw; } InflateEmptyParagraphs(stagingRange); FixUpStickyBrs(stagingRange); if (HTMLDocumentHelper.IsQuirksMode(HTMLDocument)) { ForceTablesToInheritFontColor(stagingRange); } Trace.Assert(stagingRange.Positioned && stagingRange.Start.Positioned && stagingRange.End.Positioned && sc1.Positioned && sc2.Positioned, String.Format(CultureInfo.InvariantCulture, "Staging document corrupt after RemoveBlockOrTableElement. stagingRange:({0}),stagingRange.Start:({1}),stagingRange.End:({2}),sc1:({3}),sc2:({4})", stagingRange.Positioned, stagingRange.Start.Positioned, stagingRange.End.Positioned, sc1.Positioned, sc2.Positioned)); IDisposable damageTracker = null; try { damageTracker = CreateDamageTracking(end, true); } catch (COMException ex) { Trace.WriteLine(String.Format(CultureInfo.InvariantCulture, "CreateDamageTracking Failed ({0}): {1}", end.Positioned, ex)); throw; } Trace.Assert(stagingRange.Positioned && stagingRange.Start.Positioned && stagingRange.End.Positioned && sc1.Positioned && sc2.Positioned, String.Format(CultureInfo.InvariantCulture, "Staging document corrupt after CreateDamageTracking. stagingRange:({0}),stagingRange.Start:({1}),stagingRange.End:({2}),sc1:({3}),sc2:({4})", stagingRange.Positioned, stagingRange.Start.Positioned, stagingRange.End.Positioned, sc1.Positioned, sc2.Positioned)); using (damageTracker) { // CT: Because we don't set gravity, these pointers can end up in indeterminant positions. // For example, when inserting HTML over a selection inside of a block, the start // pointer can end up on the right side of the end pointer. Pushing gravity onto // the pointers before we call this should provide consistent markup pointer behavior. try { start.PushGravity(_POINTER_GRAVITY.POINTER_GRAVITY_Left); end.PushGravity(_POINTER_GRAVITY.POINTER_GRAVITY_Right); Trace.Assert(stagingRange.Positioned && stagingRange.Start.Positioned && stagingRange.End.Positioned && sc1.Positioned && sc2.Positioned, String.Format(CultureInfo.InvariantCulture, "Staging document corrupt after applying gravity. stagingRange:({0}),stagingRange.Start:({1}),stagingRange.End:({2}),sc1:({3}),sc2:({4})", stagingRange.Positioned, stagingRange.Start.Positioned, stagingRange.End.Positioned, sc1.Positioned, sc2.Positioned)); try { MarkupServices.Move(stagingRange.Start, stagingRange.End, end); } catch (COMException ex) { Trace.WriteLine( String.Format(CultureInfo.InvariantCulture, "MarkupServices.Move Failed ({0},{1},{2}): {3}", stagingRange.Start.Positioned, stagingRange.End.Positioned, end.Positioned, ex)); throw; } } finally { end.PopGravity(); start.PopGravity(); } if (allowNewLineInsert && TidyWhitespace) { try { EnsureNewLineAtDocEnd(mpStart, mpEnd); } catch (Exception ex) { Trace.WriteLine("Failed to insert new line at end of document: " + ex); } } } } // note that we have completed our edit undoUnit.Commit(); start.PopCling(); end.PopCling(); } }
/// <summary> /// Safely removes the content within this range without leaving the document badly formed. /// </summary> public void RemoveContent() { //delete the selection by moving a delete range right (from the start). //Each time that a tag that does not entirely exist within this selection //is encountered, the range content will be deleted, the deleteRange will //skip over the element. MarkupRange deleteRange = this.Clone(); Trace.Assert(deleteRange.Start.Positioned, "Trying to remove content from selection that contains pointers that are not positioned."); deleteRange.End.MoveToPointer(deleteRange.Start); MarkupPointer p = MarkupServices.CreateMarkupPointer(); MarkupPointer previousPosition = MarkupServices.CreateMarkupPointer(deleteRange.End); MarkupContext context = new MarkupContext(); deleteRange.End.Right(true, context); while (deleteRange.End.IsLeftOf(End)) { if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope) { p.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); if (p.IsRightOf(End)) { //this element does not exist entirely in this selection, so we need to //ignore it in the delete. //save this position so that the delete range can be repositioned here p.MoveToPointer(deleteRange.End); //move the end left since we overstepped the valid delete range deleteRange.End.MoveToPointer(previousPosition); //delete the content in the deleteRange, and move it back to this position deleteRangeContentAndMoveToPosition(deleteRange, p); } else { //this element exists entirely in this selection, so skip to its end (since //we know it can be deleted) deleteRange.End.MoveToPointer(p); } } else if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope) { p.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); if (p.IsLeftOf(Start)) { //this element does not exist entirely in this selection, so we need to //ignore it in the delete. //save this position so that the delete range can be repositioned here p.MoveToPointer(deleteRange.End); //move the end left since we overstepped the valid delete range deleteRange.End.MoveToPointer(previousPosition); //delete the content in the deleteRange, and move it back to this position deleteRangeContentAndMoveToPosition(deleteRange, p); } else { //this element exists entirely in this selection, so skip to its end (since //we know it can be deleted) deleteRange.End.MoveToPointer(p); } } previousPosition.MoveToPointer(deleteRange.End); deleteRange.End.Right(true, context); } //delete the last part of the range deleteRange.End.MoveToPointer(End); if (!deleteRange.Start.Equals(deleteRange.End)) { MarkupServices.Remove(deleteRange.Start, deleteRange.End); } }
private static void MoveUnitBounded(MarkupPointer p, MoveDirection direction, MoveContextFilter continueFilter, MarkupPointer boundary) { MarkupPointer p1 = p.Clone(); MarkupPointer lastGoodPosition = p.Clone(); MarkupContext context = new MarkupContext(); MoveFilterResult result = MoveFilterResult.CONTINUE; while (CheckMoveBoundary(p1, boundary, direction) && result == MoveFilterResult.CONTINUE) { lastGoodPosition.MoveToPointer(p1); MovePointer(p1, direction, context); result = continueFilter(context); } if (result == MoveFilterResult.CONTINUE) { //we hit the boundary, so position pointer at the boundary p1.MoveToPointer(boundary); } else if (result == MoveFilterResult.STOP_BACK) { p1.MoveToPointer(lastGoodPosition); } p.MoveToPointer(p1); }