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> /// Makes sure that whole (not parts of) lists are included in the source of a paste. /// </summary> /// <param name="range">The original source range. The range may be modified.</param> /// <param name="markupServices">MarkupServices for the range.</param> private void ExpandToIncludeLists(MarkupRange range, MshtmlMarkupServices markupServices) { MarkupPointer pointer = markupServices.CreateMarkupPointer(); IHTMLElementFilter listFilter = ElementFilters.CreateCompoundElementFilter(ElementFilters.LIST_ELEMENTS, ElementFilters.LIST_ITEM_ELEMENTS); IHTMLElement[] listElements = range.GetElements(listFilter, false); foreach (IHTMLElement element in listElements) { IHTMLElement parentList = element; while (parentList != null && !ElementFilters.IsListElement(parentList)) { parentList = parentList.parentElement; } if (parentList != null) { pointer.MoveAdjacentToElement(parentList, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); if (range.Start.IsRightOf(pointer)) { range.Start.MoveToPointer(pointer); } pointer.MoveAdjacentToElement(parentList, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); if (range.End.IsLeftOf(pointer)) { range.End.MoveToPointer(pointer); } } } }
private bool IsSplitStopElement(IHTMLElement e) { return (ElementFilters.IsListItemElement(e) || ElementFilters.IsBlockQuoteElement(e) || ElementFilters.IsTableCellElement(e)); }
private bool ShouldDeleteForBlockFormatting(IHTMLElement e) { if (e == null || e.sourceIndex == -1) { return(false); } Debug.Assert(!ElementFilters.IsListItemElement(e) && !ElementFilters.IsBlockQuoteElement(e) && !ElementFilters.IsTableCellElement(e), ""); return (ElementFilters.IsBlockElement(e) && !ElementFilters.IsListItemElement(e) && !ElementFilters.IsBlockQuoteElement(e)); }
private void ApplyBlockStyle(_ELEMENT_TAG_ID styleTagId, MarkupRange selection, MarkupRange maximumBounds, MarkupRange postOpSelection) { Debug.Assert(selection != maximumBounds, "selection and maximumBounds must be distinct objects"); SelectionPositionPreservationCookie selectionPreservationCookie = null; //update the range cling and gravity so it will stick with the re-arranged block content selection.Start.PushCling(false); selection.Start.PushGravity(_POINTER_GRAVITY.POINTER_GRAVITY_Left); selection.End.PushCling(false); selection.End.PushGravity(_POINTER_GRAVITY.POINTER_GRAVITY_Right); try { if (selection.IsEmpty()) { //nothing is selected, so expand the selection to cover the entire parent block element IHTMLElementFilter stopFilter = ElementFilters.CreateCompoundElementFilter(ElementFilters.BLOCK_ELEMENTS, new IHTMLElementFilter(IsSplitStopElement)); MovePointerLeftUntilRegionBreak(selection.Start, stopFilter, maximumBounds.Start); MovePointerRightUntilRegionBreak(selection.End, stopFilter, maximumBounds.End); } using (IUndoUnit undo = _editor.CreateSelectionUndoUnit(selection)) { selectionPreservationCookie = SelectionPositionPreservationHelper.Save(_markupServices, postOpSelection, selection); if (selection.IsEmptyOfContent()) { ApplyBlockFormatToEmptySelection(selection, styleTagId, maximumBounds); } else { ApplyBlockFormatToContentSelection(selection, styleTagId, maximumBounds); } undo.Commit(); } } finally { selection.Start.PopCling(); selection.Start.PopGravity(); selection.End.PopCling(); selection.End.PopGravity(); } if (!SelectionPositionPreservationHelper.Restore(selectionPreservationCookie, selection, selection.Clone())) { selection.ToTextRange().select(); } }
private void ApplyBlockFormatToEmptySelection(MarkupRange selection, _ELEMENT_TAG_ID styleTagId, MarkupRange maximumBounds) { bool deleteParentBlock = false; //expand the selection to include the parent content block. If the expansion can cover the block element //without exceeding the maximum bounds, then delete the parent element and wrap the selection in the //new block element. If the maximum bounds are exceeded, then just wrap the selection around the bounds. IHTMLElementFilter stopFilter = ElementFilters.CreateCompoundElementFilter(ElementFilters.BLOCK_ELEMENTS, new IHTMLElementFilter(IsSplitStopElement)); MovePointerLeftUntilRegionBreak(selection.Start, stopFilter, maximumBounds.Start); MovePointerRightUntilRegionBreak(selection.End, stopFilter, maximumBounds.End); MarkupRange tmpRange = selection.Clone(); tmpRange.End.MoveToPointer(selection.Start); IHTMLElement startStopParent = tmpRange.End.GetParentElement(stopFilter); if (startStopParent != null) { tmpRange.Start.MoveAdjacentToElement(startStopParent, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); if (tmpRange.IsEmptyOfContent()) //the range from the selection the the block start is empty { tmpRange.Start.MoveToPointer(selection.End); IHTMLElement endStopParent = tmpRange.Start.GetParentElement(stopFilter); if (endStopParent != null && startStopParent.sourceIndex == endStopParent.sourceIndex) { tmpRange.Start.MoveAdjacentToElement(endStopParent, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd); if (tmpRange.IsEmptyOfContent()) //the range from the selection the the block end is empty { tmpRange.MoveToElement(endStopParent, true); if (maximumBounds.InRange(tmpRange) && !(endStopParent is IHTMLTableCell)) { deleteParentBlock = true; //the parent has no useful content outside the selection, so it's safe to delete } } } } } //delete the block parent (if appropriate) and wrap the selection in the new block element. if (deleteParentBlock) { (startStopParent as IHTMLDOMNode).removeNode(false); } IHTMLElement newBlock = WrapRangeInBlockElement(selection, styleTagId); selection.MoveToElement(newBlock, false); }
private IHTMLElement GetResizeTargetElement(string id) { if (_contentSource.ResizeCapabilities != ResizeCapabilities.None) { if (id != null) { IHTMLElement[] elements = ElementRange.GetElements(ElementFilters.CreateIdFilter(id), true); if (elements.Length > 0) { return(elements[0]); } } } return(HTMLElement); }
private void DeleteInsertionTargetBlockIfEmpty(MarkupPointer insertionPoint) { //locate the parent block element (stop at post body element) IHTMLElement parent = insertionPoint.GetParentElement( ElementFilters.CreateCompoundElementFilter(ElementFilters.CreateElementEqualsFilter(HTMLElement), ElementFilters.BLOCK_ELEMENTS)); if (parent != null && parent.sourceIndex != HTMLElement.sourceIndex && //never remove the post body block EditorContext.MarkupServices.CreateMarkupRange(parent, false).IsEmptyOfContent()) { //delete the empty parent block element (parent as IHTMLDOMNode).removeNode(true); } }
/// <summary> /// Searches through the provided document for a start and end comment marker and then returns the fragment as /// a MarkupRange. /// </summary> /// <param name="document">The document to search.</param> /// <param name="startMarker">The comment text that marks the start of the fragment /// (e.g. <!--StartFragment--> ).</param> /// <param name="endMarker">The comment text that marks the end of the fragment /// (e.g. <!--EndFragment--> ).</param> /// <returns>The fragment as a MarkupRange or null if no valid fragment was found.</returns> private MarkupRange FindMarkedFragment(IHTMLDocument2 document, string startMarker, string endMarker) { MarkupPointer startFragment = null; MarkupPointer endFragment = null; MshtmlMarkupServices markupServices = new MshtmlMarkupServices((IMarkupServicesRaw)document); // Look for the markers in the document. foreach (IHTMLElement element in document.all) { if (element is IHTMLCommentElement && ((IHTMLCommentElement)element).text == startMarker) { startFragment = markupServices.CreateMarkupPointer(element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); } else if (element is IHTMLCommentElement && ((IHTMLCommentElement)element).text == endMarker) { endFragment = markupServices.CreateMarkupPointer(element, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); } } if (startFragment == null || endFragment == null || !startFragment.Positioned || !endFragment.Positioned || startFragment.IsRightOf(endFragment)) { Trace.WriteLine("Unable to find fragment or invalid fragment!"); return(null); } // WinLive 251786: IE (and most other browsers) allow HTML like the following: // <p>This is a paragraph[cursor] // <p>This is a paragraph // However, when we use MarkupPointers to walk through this HTML, IE pretends there is a </p> at the end // of each of the above lines. This can cause issues when we copy part of this HTML somewhere else (e.g // everything after the [cursor]) and attempt to walk through both copies (e.g. during paste with keep // source formatting) at the same time. This holds true for some other elements, such as <li>s and <td>s. MarkupContext startContext = startFragment.Right(false); if (startFragment.IsLeftOf(endFragment) && startContext.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope && startContext.Element != null && ElementFilters.IsEndTagOptional(startContext.Element) && !Regex.IsMatch(startContext.Element.outerHTML, String.Format(CultureInfo.InvariantCulture, @"</{0}(\s[^>]*)?>\s*$", startContext.Element.tagName), RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)) { startFragment.Right(true); } return(markupServices.CreateMarkupRange(startFragment, endFragment)); }
/// <summary> /// Utility for properly printing the end tag for an element. /// This utility takes care of including/suppressing end tags for empty nodes properly. /// </summary> /// <param name="writer"></param> /// <param name="element"></param> private static void printElementEnd(HtmlWriter writer, IHTMLElement element) { // No tagName, no end tag. if (string.IsNullOrEmpty(element.tagName)) { return; } if (ElementFilters.RequiresEndTag(element)) { writer.WriteEndElement(true); } else { writer.WriteEndElement(false); } }
private void SelectNextRegion(bool backward) { MarkupRange smartContentRange = EditorContext.MarkupServices.CreateMarkupRange(HTMLElement, false); IHTMLElement[] editFields = smartContentRange.GetElements(ElementFilters.CreateClassFilter(InlineEditField.EDIT_FIELD), true); IHTMLElement element = GetSelectedChildEditField(HTMLElement, EditorContext.Selection.SelectedMarkupRange); if (element == null) { if (editFields.Length > 0) { SelectElement(backward ? editFields[editFields.Length - 1] : editFields[0]); } else { Select(); } return; } // One of the edit fields was selected for (int i = 0; i < editFields.Length; i++) { IHTMLElement editField = editFields[i]; if (element.sourceIndex == editField.sourceIndex) { if (i == 0 && backward || i == editFields.Length - 1 && !backward) { Select(); } else { SelectElement(backward ? editFields[i - 1] : editFields[i + 1]); } return; } } Debug.Fail("How did we get here?"); }
public static void RemoveAttributes(MshtmlMarkupServices markupServices, MarkupRange selection, string[] attributesToRemove) { IHTMLElementFilter[] filters = new IHTMLElementFilter[attributesToRemove.Length]; for (int i = 0; i < attributesToRemove.Length; i++) { filters[i] = ElementFilters.CreateElementAttributeFilter(attributesToRemove[i]); } IHTMLElement[] elements = selection.GetElements(ElementFilters.CreateCompoundElementFilter(filters), false); foreach (IHTMLElement element in elements) { foreach (string attribute in attributesToRemove) { element.removeAttribute(attribute, 0); } } }
private int CountVisibleElements(IHTMLElement node) { if (node == null) { return(0); } int count = 0; IHTMLElementCollection all = (IHTMLElementCollection)node.all; foreach (IHTMLElement child in all) { if (ElementFilters.IsVisibleEmptyElement(child)) { count++; } } return(count); }
public static void ClearBackgroundColor(MshtmlMarkupServices markupServices, MarkupRange selection) { HtmlStyleHelper htmlStyleHelper = new HtmlStyleHelper(markupServices); htmlStyleHelper.SplitInlineTags(selection.Start); htmlStyleHelper.SplitInlineTags(selection.End); IHTMLElement[] elements = selection.GetElements(ElementFilters.CreateTagIdFilter("font"), false); foreach (IHTMLElement element in elements) { element.style.backgroundColor = ""; } // We may now be left with empty font tags, e.g. <font>blah</font>. // After switching between editors this becomes <font size="+0">blah</font>, which // causes blah to be rendered differently. // To avoid that we need to remove any empty-attribute font tags. selection.RemoveElementsByTagId(_ELEMENT_TAG_ID.TAGID_FONT, true); }
/// <summary> /// <font><span>aaa[splitPoint]bbb</span></font> --> <font><span>aaa</span></font><font>[splitPoint]<span>bbb</span></font> /// </summary> private void SplitInlineTags(MarkupPointer splitPoint) { Debug.Assert(splitPoint.Positioned); IHTMLElement currentElement = splitPoint.GetParentElement(ElementFilters.CreateElementPassFilter()); while (currentElement != null) { if (!ElementFilters.IsInlineElement(currentElement)) { return; } IHTMLElement parentElement = currentElement.parentElement; MarkupRange currentElementRange = _markupServices.CreateMarkupRange(currentElement, false); MarkupRange leftRange = _markupServices.CreateMarkupRange(); IHTMLElement leftElement = _markupServices.CreateElement(_markupServices.GetElementTagId(currentElement), null); HTMLElementHelper.CopyAttributes(currentElement, leftElement); leftRange.MoveToPointers(currentElementRange.Start, splitPoint); _markupServices.InsertElement(leftElement, leftRange.Start, leftRange.End); splitPoint.MoveAdjacentToElement(leftElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); MarkupRange rightRange = _markupServices.CreateMarkupRange(); IHTMLElement rightElement = _markupServices.CreateElement(_markupServices.GetElementTagId(currentElement), null); HTMLElementHelper.CopyAttributes(currentElement, rightElement); rightRange.MoveToPointers(splitPoint, currentElementRange.End); #if DEBUG // Verify that the right range does not overlap the left *element* MarkupRange leftElementRange = _markupServices.CreateMarkupRange(leftElement, true); Debug.Assert(leftElementRange.End.IsLeftOfOrEqualTo(rightRange.Start), "Your right range overlaps the left element that you just created!"); #endif _markupServices.InsertElement(rightElement, rightRange.Start, rightRange.End); splitPoint.MoveAdjacentToElement(rightElement, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); _markupServices.RemoveElement(currentElement); currentElement = parentElement; } }
private static string BalanceHtml(string html) { StringBuilder sb = new StringBuilder(html.Length + 10); SimpleHtmlParser parser = new SimpleHtmlParser(html); Element el; while (null != (el = parser.Next())) { if (el is BeginTag) { BeginTag bt = (BeginTag)el; if (!ElementFilters.RequiresEndTag(bt.Name)) { bt.Complete = true; } } sb.Append(el.ToString()); } return(sb.ToString()); }
/// <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)); }
private bool IsSelectableControlElement(IHTMLElement e) { return(ElementFilters.IsImageElement(e) || ContentSourceManager.IsSmartContent(e)); }
public static void ChangeElementTagIds(MshtmlMarkupServices markupServices, MarkupRange selection, _ELEMENT_TAG_ID[] tagBefore, _ELEMENT_TAG_ID tagAfter) { HtmlStyleHelper htmlStyleHelper = new HtmlStyleHelper(markupServices); int parentStartDiff = 0; MarkupRange rangeToChange; bool selectionStartedEmpty = selection.IsEmpty(); if (selectionStartedEmpty) { // Operate on parent block element rangeToChange = markupServices.CreateMarkupRange(selection.ParentBlockElement()); parentStartDiff = selection.Start.MarkupPosition - rangeToChange.Start.MarkupPosition; } else { rangeToChange = selection; // If expanding the selection would not include any new text, then expand it. // <h1>|abc|</h1> --> |<h1>abc</h1>| rangeToChange.MoveOutwardIfNoText(); } IHTMLElementFilter[] filters = new IHTMLElementFilter[tagBefore.Length]; for (int i = 0; i < tagBefore.Length; i++) { filters[i] = ElementFilters.CreateTagIdFilter(markupServices.GetNameForTagId(tagBefore[i])); } IHTMLElement[] elements = rangeToChange.GetElements(ElementFilters.CreateCompoundElementFilter(filters), false); foreach (IHTMLElement element in elements) { MarkupRange elementRange = markupServices.CreateMarkupRange(element); int startPositionDiff = rangeToChange.Start.MarkupPosition - elementRange.Start.MarkupPosition; int endPositionDiff = rangeToChange.End.MarkupPosition - elementRange.End.MarkupPosition; // @RIBBON TODO: Appropriately preserve element attributes when changing tag ids? MarkupRange newElementRange = markupServices.CreateMarkupRange(htmlStyleHelper.WrapRangeInSpanElement(tagAfter, null, elementRange)); markupServices.RemoveElement(element); MarkupPointer startPointer = rangeToChange.Start.Clone(); startPointer.MoveToMarkupPosition(startPointer.Container, newElementRange.Start.MarkupPosition + startPositionDiff); if (startPointer.IsLeftOf(rangeToChange.Start)) { rangeToChange.Start.MoveToPointer(startPointer); } MarkupPointer endPointer = rangeToChange.End.Clone(); endPointer.MoveToMarkupPosition(endPointer.Container, newElementRange.End.MarkupPosition + endPositionDiff); if (endPointer.IsLeftOf(elementRange.End)) { rangeToChange.End.MoveToPointer(endPointer); } } if (selectionStartedEmpty) { selection.Start.MoveToMarkupPosition(selection.Start.Container, rangeToChange.Start.MarkupPosition + parentStartDiff); selection.Collapse(true); } }
private 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); }
/// <summary> /// Returns true if shift+tab focused region changing is supported for the current edit location /// </summary> /// <returns></returns> private bool ShiftTabFocusChangeSupported() { //Shift-tab is only supported if the caret is positioned at the beginning of the post //If the selection is currently inside a non-blockquote block element with no visible content //to the left of it, then focus change is supported. if (!EditorContext.Selection.SelectedMarkupRange.IsEmpty()) { return(false); } MarkupRange range = ElementRange.Clone(); range.Start.MoveAdjacentToElement(HTMLElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); range.End = EditorContext.Selection.SelectedMarkupRange.Start; //if there is any text between the caret and the beginning of the post, //then ShiftTab focus changing is not allowed. string text = range.Text; if (text != null && range.Text.Trim() != String.Empty) { return(false); } MarkupContext context = new MarkupContext(); range.Start.Right(true, context); int blockElementDepth = 0; while (range.Start.IsLeftOfOrEqualTo(range.End)) { if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope || context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_NoScope) { string tagName = context.Element.tagName; if (tagName.Equals("BLOCKQUOTE") || ElementFilters.IsListElement(context.Element) || ElementFilters.IsListItemElement(context.Element)) { return(false); //ShiftTab in a blockquote or list implies "un-blockquote" or "un-list" } else if (ElementFilters.IsBlockElement(context.Element)) { blockElementDepth++; if (blockElementDepth > 1) { return(false); //there are multiple block elements, so this is not the beginning } } else if (ElementFilters.IsVisibleEmptyElement(context.Element)) { return(false); //there is a visible empty element (like an image), so this is not the beginning } } else if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope) { if (ElementFilters.IsVisibleEmptyElement(context.Element)) { return(false); //there is a visible empty element (like an image), so this is not the beginning } } range.Start.Right(true, context); } return(true); }
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 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(); } }