/// <summary> /// Adjust the start and end of the range to match the offset/length, in characters. /// if the offset/length adjustment fails to produce the expected value, /// then the adjustment is cancelled and false is returned. /// </summary> public static bool AdjustMarkupRange(ref IHTMLTxtRange stagingTextRange, MarkupRange range, int offset, int length) { string currentText = GetRangeTextFast(range) ?? ""; if (offset == 0 && length == currentText.Length) { return(true); } string expectedText; try { expectedText = currentText.Substring(offset, length); } catch (ArgumentOutOfRangeException) { return(false); } MarkupRange testRange = range.Clone(); AdjustMarkupRangeCore(testRange, offset, length, currentText); if (GetRangeTextFast(testRange) != expectedText) { return(false); } range.MoveToRange(testRange); return(true); }
public static void RelinquishTxtRange(IHTMLTxtRange txtRange, MarkupRange range) { if (cache == null) { return; } try { lock (cache.SyncRoot) { Queue queue = (Queue)cache[GetDocumentKey(range.MarkupServices.MarkupServicesRaw)]; if (queue != null) { lock (queue.SyncRoot) { queue.Enqueue(txtRange); } } } } catch (Exception ex) { Debug.WriteLine("Failure in IHTMLTxtRangePool: " + ex); cache = null; } }
public static String ToFormattedHtml(MshtmlMarkupServices markupServices, MarkupRange bounds) { StringBuilder sb = new StringBuilder(); HtmlWriter xmlWriter = new HtmlWriter(sb); PrintHtml(xmlWriter, markupServices, bounds); return sb.ToString(); }
/// <summary> /// Find the most logic direction to move the cursor so that it matches the where has clicked /// </summary> /// <param name="Selection"></param> /// <returns></returns> public static Direction FindSelectionToLogicalPosition(MarkupRange Selection, IHTMLElement body, bool?forward) { // There is a selection, not just a click if (!Selection.Start.IsEqualTo(Selection.End)) { return(Direction.None); } Direction dir = ImageBreakout(Selection.Start); if (dir != Direction.None) { return(dir); } MarkupContext contextRight = Selection.Start.Right(false); MarkupContext contextLeft = Selection.Start.Left(false); // there is text or some other type of content around the click if (!((contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope || contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope) && (contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope || contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope))) { return(Direction.None); } // The click is not between two block elements, so it should be fine where it is if (!ElementFilters.IsBlockElement(contextLeft.Element) || !ElementFilters.IsBlockElement(contextRight.Element)) { return(Direction.None); } // </blockElement>|</postBody> if (contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope && !IsSmartContent(contextLeft) && contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope && contextRight.Element.id == body.id) { return(Direction.Left); } // <postBody>|<blockElement> if (contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope && !IsSmartContent(contextRight) && contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope && contextLeft.Element.id == body.id) { return(Direction.Right); } // </blockElement>|<blockElement> if (contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope && !IsSmartContent(contextLeft) && contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope && !IsSmartContent(contextRight)) { return(forward == null || forward == true ? Direction.Right : Direction.Left); } return(Direction.None); }
private void Init(IHTMLDocument document, MshtmlMarkupServices markupServices, MarkupRange selectionRange, MarkupRangeFilter filter, DamageFunction damageFunction, bool expandRange) { // save references this.htmlDocument = document; this.markupServices = markupServices; this.selectionRange = selectionRange; this.filter = filter; this.damageFunction = damageFunction; // If the range is already the body, don't expand it or else it will be the whole document if (expandRange) ExpandRangeToWordBoundaries(selectionRange); // initialize pointer to beginning of selection range MarkupPointer wordStart = MarkupServices.CreateMarkupPointer(selectionRange.Start); MarkupPointer wordEnd = MarkupServices.CreateMarkupPointer(selectionRange.Start); //create the range for holding the current word. //Be sure to set its gravity so that it stays around text that get replaced. currentWordRange = MarkupServices.CreateMarkupRange(wordStart, wordEnd); currentWordRange.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left; currentWordRange.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right; currentVirtualPosition = currentWordRange.End.Clone(); }
public static string GetRangeHtmlFast(MarkupRange range) { IHTMLTxtRange stagingTextRange = IHTMLTxtRangePool.AquireTxtRange(range); string returnValue = UseStagingTextRange(ref stagingTextRange, range, rng => rng.htmlText); IHTMLTxtRangePool.RelinquishTxtRange(stagingTextRange, range); return(returnValue); }
/// <summary> /// Insert html at the given pointer. /// </summary> /// <param name="html"></param> /// <param name="insertionPoint"></param> public void InsertHtml(string html, MarkupPointer insertionPoint) { MarkupRange content = CreateMarkupRange(); ParseString(html, content.Start, content.End); Move(content.Start, content.End, insertionPoint); }
/// <summary> /// Replaces an element with a new element while preserving the content inside the old element. /// </summary> /// <param name="oldElement"></param> /// <param name="newElement"></param> public void ReplaceElement(IHTMLElement oldElement, IHTMLElement newElement) { MarkupRange range = CreateMarkupRange(); range.MoveToElement(oldElement, true); InsertElement(newElement, range.Start, range.End); RemoveElement(oldElement); }
/// <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); } }
/// <summary> /// Create a MarkupRange from a TextRange. /// </summary> /// <param name="textRange"></param> /// <returns></returns> public MarkupRange CreateMarkupRange(IHTMLTxtRange textRange) { MarkupPointer Begin = CreateMarkupPointer(); MarkupPointer End = CreateMarkupPointer(); End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right; MovePointersToRange(textRange, Begin, End); MarkupRange markupRange = new MarkupRange(Begin, End, this); return(markupRange); }
public static IHTMLTxtRange AquireTxtRange(MarkupRange range) { try { if (cache != null) { int documentId = GetDocumentKey(range.MarkupServices.MarkupServicesRaw); Queue queue = null; IHTMLTxtRange returnRange = null; lock (cache.SyncRoot) { queue = (Queue)cache[documentId]; if (queue == null) { queue = Queue.Synchronized(new Queue()); cache.Add(documentId, queue); } if (queue.Count > 0) { lock (queue.SyncRoot) { returnRange = (IHTMLTxtRange)queue.Dequeue(); } } else { returnRange = range.ToTextRange(); } } return(returnRange); } } catch (Exception ex) { Debug.WriteLine("Failure in IHTMLTxtRangePool: " + ex); cache = null; } try { return(range.ToTextRange()); } catch (Exception ex) { Debug.WriteLine("Failure in IHTMLTxtRangePool: " + ex); return(null); } }
/// <summary> /// Create a MarkupRange from that surrounds an Element. /// </summary> /// <returns></returns> public MarkupRange CreateMarkupRange(IHTMLElement element, bool outside) { _ELEMENT_ADJACENCY beginAdj = outside ? _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin : _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin; _ELEMENT_ADJACENCY endAdj = outside ? _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd : _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd; MarkupPointer Begin = CreateMarkupPointer(element, beginAdj); MarkupPointer End = CreateMarkupPointer(element, endAdj); End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right; MarkupRange markupRange = new MarkupRange(Begin, End, this); return(markupRange); }
/// <summary> /// Creates a clone that spans the same range as this MarkupRange. /// Note: The clone can be manipulated without changing the position of this range. /// </summary> /// <returns></returns> public MarkupRange Clone() { MarkupRange clone = MarkupServices.CreateMarkupRange(); clone.Start.MoveToPointer(Start); clone.Start.Cling = Start.Cling; clone.Start.Gravity = Start.Gravity; clone.End.MoveToPointer(End); clone.End.Cling = End.Cling; clone.End.Gravity = End.Gravity; return(clone); }
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 static void AdjustMarkupRangeCore(MarkupRange range, int offset, int length, string currentText) { MarkupPointer start = range.Start; MarkupPointer end = range.End; if (offset > 0) { start.MoveToMarkupPosition(start.Container, start.MarkupPosition + offset); } if (length < (offset + currentText.Length)) { end.MoveToMarkupPosition(start.Container, start.MarkupPosition + length); } }
private static void PrintHtml(HtmlWriter writer, MshtmlMarkupServices MarkupServices, MarkupRange bounds) { //create a range to span a single position while walking the doc MarkupRange range = MarkupServices.CreateMarkupRange(); range.Start.MoveToPointer(bounds.Start); range.End.MoveToPointer(bounds.Start); //create a context that can be reused while walking the document. MarkupContext context = new MarkupContext(); //move the range.End to the right and print out each element along the way range.End.Right(true, context); while (context.Context != _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_None && range.Start.IsLeftOf(bounds.End)) { string text = null; if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text) { //if this is a text context, then get the text that is between the start and end points. text = range.HtmlText; //the range.HtmlText operation sometimes returns the outer tags for a text node, //so we need to strip the tags. //FIXME: if the Right/Left operations returned the available text value, this wouldn't be necessary. if (text != null) text = StripSurroundingTags(text); } else if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope) { string htmlText = range.HtmlText; if (context.Element.innerHTML == null && htmlText != null && htmlText.IndexOf(" ") != -1) { //HACK: Under these conditions, there was a was an invisible NBSP char in the //document that is not detectable by walking through the document with MarkupServices. //So, we force the text of the element to be the char to ensure that the //whitespace that was visible in the editor is visible in the final document. text = " "; } } //print the context. printContext(writer, context, text, range); //move the start element to the spot where the end currently is so tht there is //only ever a single difference in position range.Start.MoveToPointer(range.End); //move the end to the next position range.End.Right(true, context); } }
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 static void SplitBlockForInsertionOrBreakout(MshtmlMarkupServices markupServices, MarkupRange bounds, MarkupPointer insertAt) { IHTMLElement currentBlock = insertAt.GetParentElement(ElementFilters.BLOCK_OR_TABLE_CELL_ELEMENTS); if (currentBlock == null) { return; } if (ElementFilters.IsBlockQuoteElement(currentBlock) || ElementFilters.IsTableCellElement(currentBlock)) { return; } MarkupPointer blockStart = markupServices.CreateMarkupPointer(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); MarkupPointer blockEnd = markupServices.CreateMarkupPointer(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); if (bounds != null && (blockStart.IsLeftOf(bounds.Start) || blockEnd.IsRightOf(bounds.End))) { return; } // Don't split if at the beginning or end of the visible content in the block. // Instead just move the insertion point outside the block. MarkupRange testRange = markupServices.CreateMarkupRange(); testRange.Start.MoveToPointer(insertAt); testRange.End.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd); if (testRange.IsEmptyOfContent()) { insertAt.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); return; } testRange.Start.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); testRange.End.MoveToPointer(insertAt); if (testRange.IsEmptyOfContent()) { insertAt.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); return; } MarkupPointer moveTarget = markupServices.CreateMarkupPointer(blockEnd); markupServices.Move(insertAt, blockEnd, moveTarget); insertAt.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); }
public KeepSourceFormatting(MarkupRange sourceRange, MarkupRange destinationRange) { Debug.Assert(sourceRange.Start.Container.GetOwningDoc() == destinationRange.Start.Container.GetOwningDoc(), "Ranges must share an owning document!"); if (sourceRange == null) { throw new ArgumentNullException("sourceRange"); } if (!sourceRange.Positioned) { throw new ArgumentException("sourceRange must be positioned."); } if (sourceRange.Start.IsRightOf(sourceRange.End)) { throw new ArgumentException("sourceRange start must be before range end."); } if (destinationRange == null) { throw new ArgumentNullException("destinationRange"); } if (!destinationRange.Positioned) { throw new ArgumentException("destinationRange must be positioned."); } if (destinationRange.Start.IsRightOf(destinationRange.End)) { throw new ArgumentException("destinationRange start must be before range end."); } this.sourceDocument = sourceRange.Start.Container.Document; this.sourceMarkupServices = new MshtmlMarkupServices((IMarkupServicesRaw)this.sourceDocument); this.sourceRange = sourceRange.Clone(); this.sourceRange.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left; this.sourceRange.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right; this.destinationDocument = destinationRange.Start.Container.Document; this.destinationMarkupServices = new MshtmlMarkupServices((IMarkupServicesRaw)this.destinationDocument); this.destinationRange = destinationRange.Clone(); this.destinationRange.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left; this.destinationRange.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right; }
/// <summary> /// Returns null if ranges do not intersect /// </summary> /// <param name="range"></param> /// <returns></returns> public MarkupRange Intersect(MarkupRange range) { MarkupPointer maxStart = Start.IsRightOf(range.Start) ? Start : range.Start; MarkupPointer minEnd = End.IsLeftOf(range.End) ? End : range.End; if (minEnd.IsLeftOf(maxStart)) { return(null); } MarkupRange intersection = MarkupServices.CreateMarkupRange(); intersection.Start.MoveToPointer(maxStart); intersection.End.MoveToPointer(minEnd); return(intersection); }
//this is similar to the GetTopLevelElements except will also return table cells if correct filter // is set and recurse is equal to true public IHTMLElement[] GetTopLevelBlocksAndCells(IHTMLElementFilter filter, bool recurse) { ArrayList list = new ArrayList(); Hashtable usedElements = new Hashtable(); MarkupPointer p = MarkupServices.CreateMarkupPointer(Start); MarkupContext context = p.Right(false); //move p through the range to locate each of the top level elements while (p.IsLeftOf(End)) { if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope || context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_NoScope) { p.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); if (usedElements[context.Element] == null) { if (p.IsLeftOfOrEqualTo(End) && (filter == null || filter(context.Element))) { list.Add(context.Element); } //special case--inside of a table element, want to get out the cells inside else if (recurse && ElementFilters.TABLE_ELEMENTS(context.Element)) { MarkupRange newRange = MarkupServices.CreateMarkupRange(context.Element); newRange.Start.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); if (newRange.Start.IsLeftOf(Start)) { newRange.Start.MoveToPointer(Start); } if (newRange.End.IsRightOf(End)) { newRange.End.MoveToPointer(End); } //recursively check inside table element for table cells list.AddRange(newRange.GetTopLevelBlocksAndCells(filter, true)); } //cache the fact that we've already tested this element. usedElements[context.Element] = context.Element; } } p.Right(true, context); } return(HTMLElementHelper.ToElementArray(list)); }
public OleUndoUnit(MshtmlMarkupServices markupServices, MarkupRange selection) { _markupServices = markupServices; //serialize the current position of the markup pointers so that they can be restored if //the DOM gets rolled back into the same state via an undo/redo operation. if (selection != null && selection.Positioned) { IMarkupPointer2Raw pointer2StartRaw = selection.Start.PointerRaw as IMarkupPointer2Raw; IMarkupPointer2Raw pointer2EndRaw = selection.End.PointerRaw as IMarkupPointer2Raw; pointer2StartRaw.GetMarkupPosition(out _startPosition); pointer2EndRaw.GetMarkupPosition(out _endPosition); pointer2StartRaw.GetContainer(out _markupContainer); } _description = "OleUndoUnit" + Guid.NewGuid().ToString(); Undo = true; }
public TableSelection(MarkupRange markupRange) { // calculate the begin and end cells IHTMLTableCell beginCell; IHTMLTableCell endCell; ArrayList selectedCells; FindCellRange(markupRange, out selectedCells, out beginCell, out endCell); // see if the two cells have a single containing table IHTMLTable table = GetSelectedTable(beginCell, endCell, markupRange) as IHTMLTable; // if we have a table then calculate the rest of our states if (table != null) { // validate the table selection if (ValidateTableSelection(table, markupRange, out _entireTableSelected)) { _table = table; _beginCell = beginCell; _endCell = endCell; // filter selected cells to only include direct descendents of this table (no // cells from nested tables) _selectedCells = new ArrayList(); foreach (IHTMLElement cell in selectedCells) if (HTMLElementHelper.ElementsAreEqual(TableHelper.GetContainingTableElement(cell) as IHTMLElement, _table as IHTMLElement)) _selectedCells.Add(cell); _hasContiguousSelection = !HTMLElementHelper.ElementsAreEqual(_beginCell as IHTMLElement, _endCell as IHTMLElement); _beginRow = GetContainingRowForCell(beginCell); _endRow = GetContainingRowForCell(endCell); _beginColumn = new HTMLTableColumn(_table, beginCell); _endColumn = new HTMLTableColumn(_table, endCell); } } }
public static IHTMLElement GetSelectedChildEditField(IHTMLElement parent, MarkupRange selection) { if (selection == null || !selection.Positioned) { Trace.Fail("Selection is invalid!"); return null; } IHTMLElement element = selection.ParentElement(); if (element == null || !HTMLElementHelper.IsChildOrSameElement(parent, element)) return null; do { if (InlineEditField.IsEditField(element)) return element; element = element.parentElement; } while (element != null && element.sourceIndex != parent.sourceIndex); return null; }
public static int GetParentContainerBlockWidth(MarkupRange markupRange) { IHTMLElement2 parentBlock = markupRange.Start.GetParentElement(ElementFilters.BLOCK_OR_TABLE_CELL_ELEMENTS) as IHTMLElement2; if (parentBlock != null) { // TODO: we would like to always clientWidth here however for an empty block element this will // be zero. So in this case we use scrollWidth which should be a proxy except in the case where // the parent element has a horizontal scroll bar (in which case we may insert a table which // is worst case too narrow). What we "should" do is insert and remove some bogus content // within the block elemet to force its clientWidth to the right value. int blockWidth = parentBlock.clientWidth; if (blockWidth == 0) blockWidth = parentBlock.scrollWidth; return blockWidth; } else { return 0; } }
/// <summary> /// Find the most logic direction to move to visible location that is where it appears to the user. /// And then moves it to that location. Returns true if it moved the pointer. /// </summary> /// <param name="Selection"></param> /// <returns></returns> public static bool DriveSelectionToLogicalPosition(MarkupRange Selection, IHTMLElement body, bool?forward) { Direction dir = FindSelectionToLogicalPosition(Selection, body, forward); if (dir == Direction.None) { return(false); } if (dir == Direction.Right) { Selection.End.Right(true); Selection.Start.Right(true); } else if (dir == Direction.Left) { Selection.End.Left(true); Selection.Start.Left(true); } return(true); }
/// <summary> /// Create a MarkupRange from a selection object. /// </summary> public MarkupRange CreateMarkupRange(IHTMLSelectionObject selection) { if (selection == null) { return(null); } // see what type of range is selected object range = selection.createRange(); if (range is IHTMLTxtRange) { return(CreateMarkupRange(range as IHTMLTxtRange)); } else if (range is IHTMLControlRange) { // we only support single-selection so a "control-range" can always // be converted into a single-element text range IHTMLControlRange controlRange = range as IHTMLControlRange; if (controlRange.length == 1) { IHTMLElement selectedElement = controlRange.item(0); MarkupRange markupRange = CreateMarkupRange(selectedElement); //return the precisely positioned text range return(markupRange); } else { Debug.Fail("Length of control range not equal to 1 (value was " + controlRange.length.ToString(CultureInfo.InvariantCulture)); return(null); } } else // null or unexpected range type { return(null); } }
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 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; }
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); } } }
public IDisposable CreateDeleteDamageTracker(MarkupRange range) { if (DamageTrackingEnabled) { //Bug fix: The delete key triggers an extra selection change event that causes word damage to be //inappropriately committed, so we use a flag to suppress the selection change event for //this case. suppressSelectionChangeForDelete = true; return new DeleteDamageTracker(this); } else return null; }
public void AddDamage(MarkupRange range, bool includeAdjacentWords) { if (DamageTrackingEnabled) wordRangeDamager.AddDamage(range, includeAdjacentWords); }
public void AddDamage(MarkupRange range) { AddDamage(range, false); }
/// <summary> /// Text ranges are extremely expensive to create, and readily reusable. UseStagingTextRange /// is a higher order function that makes it slightly easier to reuse text ranges, which can /// give much better performance if the lifetime of your stagingTextRange reference spans /// lots of calls. /// </summary> /// <typeparam name="TResult">The type of the result from the function we'll execute.</typeparam> /// <param name="stagingTextRange">A reference to a stagingTextRange that we can use, it can be null at first and we'll create on demand if necessary.</param> /// <param name="range">The markup range to move the stagingTextRange to.</param> /// <param name="func">The function to pass the stagingTextRange to after it's been created/positioned.</param> /// <returns>The value returned from func.</returns> public static TResult UseStagingTextRange <TResult>(ref IHTMLTxtRange stagingTextRange, MarkupRange range, TextRangeFunc <TResult> func) { Debug.Assert(range != null, "Range must not be null!"); Debug.Assert(range.Positioned, "Range must be positioned!"); Debug.Assert(range.Start.IsLeftOfOrEqualTo(range.End), "Range start must be left of or equal to range end!"); if (stagingTextRange == null) { stagingTextRange = range.MarkupServices.CreateTextRange(range.Start, range.End); } else { range.MarkupServices.MoveRangeToPointers(range.Start, range.End, stagingTextRange); } return(func(stagingTextRange)); }
/// <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 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); } } }
private void DumpDamagedRegions(MarkupRange[] damagedRegions) { Trace.WriteLine("---Damaged Regions---"); foreach (MarkupRange damage in damagedRegions) { string text = damage.Text; if (text != null) Trace.WriteLine(" " + text.Replace('\r', ' ').Replace('\n', ' ')); } }
/// <summary> /// Expands this range out to the next parent shared by the start and end points /// if there there are no non-empty text elements between them. /// </summary> /// <returns></returns> public bool MoveOutwardIfNo(RangeFilter rangeFilter) { MarkupRange newRange = MarkupServices.CreateMarkupRange(); IHTMLElement sharedParent = GetSharedParent(Start, End); // If share a common parent, we will take the shared parent's parent so we can see if we want to grab // all the html inside of it, unless the shared parent is the body element, in which case we don't want to // epxand outward anymore if (Start.CurrentScope == sharedParent && End.CurrentScope == sharedParent && !(sharedParent is IHTMLBodyElement)) { sharedParent = sharedParent.parentElement; } //expand to the inside of the shared parent first. If this matches the current placement //of the pointers, then expand to the outside of the parent. This allows the outter selection //to grow incrementally in such a way as to allow the shared parent to be tested between //each iteration of this operation. newRange.Start.MoveAdjacentToElement(sharedParent, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); newRange.End.MoveAdjacentToElement(sharedParent, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd); if (newRange.IsEmpty() || newRange.Start.IsRightOf(Start) || newRange.End.IsLeftOf(End)) { newRange.Start.MoveAdjacentToElement(sharedParent, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); newRange.End.MoveAdjacentToElement(sharedParent, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); } if (!rangeFilter(newRange.Start, Start) && !rangeFilter(End, newRange.End) && !(Start.IsEqualTo(newRange.Start) && End.IsEqualTo(newRange.End))) { Start.MoveToPointer(newRange.Start); End.MoveToPointer(newRange.End); return(true); } else { //span the start and end pointers as siblings by finding the parents of start and end //pointers that are direct children of the sharedParent IHTMLElement child = GetOuterMostChildOfParent(Start, true, sharedParent); if (child != null) { newRange.Start.MoveAdjacentToElement(child, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); } else { newRange.Start = Start; } child = GetOuterMostChildOfParent(End, false, sharedParent); if (child != null) { newRange.End.MoveAdjacentToElement(child, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); } else { newRange.End = End; } if (!rangeFilter(newRange.Start, Start) && !rangeFilter(End, newRange.End) && !(Start.IsEqualTo(newRange.Start) && End.IsEqualTo(newRange.End))) { Start.MoveToPointer(newRange.Start); End.MoveToPointer(newRange.End); return(true); } else { //the range didn't change, so return false. return(false); } } }
public void Reset() { damageQueue.Clear(); _wordHelper = new MarkupServicesWordHelper(_mshtmlEditor.MshtmlControl.MarkupServices); _currentSelectionDamage = _mshtmlEditor.MshtmlControl.MarkupServices.CreateMarkupRange(); _currentSelectionDamage.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left; _currentSelectionDamage.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right; }
/// <summary> /// Move this markup range to the specified location. /// </summary> /// <param name="textRange"></param> public void MoveToRange(MarkupRange range) { Start.MoveToPointer(range.Start); End.MoveToPointer(range.End); }
public bool Intersects(MarkupRange range) { return(!(Start.IsRightOf(range.End) || End.IsLeftOf(range.Start))); }
/// <summary> /// Returns true if the specified range is in a position between, or (if allowed) equal to this range's Start/End points. /// </summary> /// <returns></returns> public bool InRange(MarkupRange range, bool allowEquals) { return(InRange(range.Start, allowEquals) && InRange(range.End, allowEquals)); }
public IDisposable CreateDamageTracker(MarkupRange range, bool includeAdjacentWords) { if (DamageTrackingEnabled) return new DamageTracker(this, range.Start, range.End, includeAdjacentWords); else return null; }
private void _AddDamage(MarkupRange range) { lock (damageQueue) { damageQueue.EnqueueDamage(range); //Debug.WriteLine("Word Damage:" + _currentSelectionDamage.Text); _mshtmlEditor.BeginInvoke(new ThreadStart(FireDamageOccurred)); } }
public DamageTracker(HtmlEditorControlDamageServices damageServices, MarkupPointer start, MarkupPointer end, bool includeAdjacentWords) { _damageServices = damageServices; _damageRange = damageServices.wordRangeDamager.CreateDamageTrackingRange(start, end, includeAdjacentWords); }
/// <summary> /// Deletes (unsafely!) content within a range and repositions the range at a new position. /// </summary> /// <param name="range"></param> /// <param name="newPosition"></param> private void deleteRangeContentAndMoveToPosition(MarkupRange range, MarkupPointer newPosition) { MarkupServices.Remove(range.Start, range.End); range.Start.MoveToPointer(newPosition); range.End.MoveToPointer(newPosition); }
public DamageEvent(MarkupRange[] damageRegions) { _damageRegions = damageRegions; }
internal void Add(OpenLiveWriter.Mshtml.MarkupRange SelectedMarkupRange) { listBoxSelection.Items.Insert(0, new SelectionItem(SelectedMarkupRange)); }
private void ExpandDamageToAdjacentWords(MarkupRange damageRange) { ExpandDamageToPreviousWord(damageRange); ExpandDamageToNextWord(damageRange); }
private bool HasTextBetween(MarkupPointer start, MarkupPointer end) { MarkupRange range = MarkupServices.CreateMarkupRange(start, end); return(!range.IsEmptyOfText(false)); }
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); }
public MarkupRange[] GetDamagedRegions() { lock (damageQueue) { MarkupRange[] damage = damageQueue.DequeueDamage(); //reset the current selection damage MarkupRange newSelectionDamage = _mshtmlEditor.MshtmlControl.MarkupServices.CreateMarkupRange(); newSelectionDamage.MoveToRange(_currentSelectionDamage); _currentSelectionDamage = newSelectionDamage; damaged = false; return damage; } }
private void AdjustCurrentWordRange(MarkupRange range) { if (damaged) { _currentSelectionDamage = _mshtmlEditor.MshtmlControl.MarkupServices.CreateMarkupRange(); damaged = false; } _currentSelectionDamage.MoveToRange(range); //Trace.WriteLine("adjacent to word start: " + IsAdjacentToWordStart(range.Start)); //Trace.WriteLine("adjacent to word end: " + IsAdjacentToWordEnd(range.End)); _wordHelper.MoveToWordStart(_currentSelectionDamage.Start); _wordHelper.MoveToWordEnd(_currentSelectionDamage.End); //Trace.WriteLine("Current Word:" + _currentSelectionDamage.Text); }
/// <summary> /// Only edit tables that are not contained within SmartContent blocks and which /// are marked with the "unselectable" attribute. Since this is an attribute which /// applies only to editing scenarios it is almost certain never to enter the editor /// "from the wild" so it is a reasonable way to determine whether we created the /// table (and thus can guarantee that it conforms to our editing capabilities). The /// only other reasonable choice would be to mark the table up with some other /// pseudo-hidden metadata, which seems even more undesirable. /// </summary> public static bool TableElementIsEditable(IHTMLElement element, MarkupRange elementMarkupRange) { return TableElementContainsWriterEditingMark(element) && !TableElementIsContainedInSmartContent(elementMarkupRange); }
public void EnqueueDamage(MarkupRange range) { lock (damagedRanges) { damagedRanges.Add(range); } }
public static bool DriveSelectionToLogicalPosition(MarkupRange Selection, IHTMLElement body) { return(DriveSelectionToLogicalPosition(Selection, body, null)); }
private static bool TableElementIsContainedInSmartContent(MarkupRange elementRange) { // table elements inside smart content regions are not editable IHTMLElement parentSmartContent = elementRange.Start.GetParentElement(ContentSourceManager.CreateSmartContentElementFilter()); return parentSmartContent != null; }
/// <summary> /// Create a MarkupRange from a set of MarkupPointers. /// </summary> /// <param name="start"></param> /// <param name="end"></param> /// <returns></returns> public MarkupRange CreateMarkupRange(MarkupPointer start, MarkupPointer end) { MarkupRange markupRange = new MarkupRange(start, end, this); return(markupRange); }
private IHTMLElement GetNextElement(MarkupPointer start, MarkupRange boundaries, IHTMLElementFilter filter, bool forward) { start = start.Clone(); MarkupPointer boundary = forward ? boundaries.End : boundaries.Start; MarkupContext moveResult = new MarkupContext(); _MARKUP_CONTEXT_TYPE skipContext = _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope; //advance the pointer if (forward) start.Right(true, moveResult); else start.Left(true, moveResult); while (forward ? start.IsLeftOf(boundary) : start.IsRightOf(boundary)) { if (moveResult.Element != null && moveResult.Context != skipContext && filter(moveResult.Element)) { return moveResult.Element; } //advance the pointer if (forward) start.Right(true, moveResult); else start.Left(true, moveResult); } return null; }
/// <summary> /// Returns true if the specified range is in a position between, or equal to this range's Start/End points. /// </summary> /// <param name="p"></param> /// <returns></returns> public bool InRange(MarkupRange range) { return(InRange(range, true)); }