/// <summary> /// WinLive 225587: If the user hits delete when the cursor is just before a table, we want to move the /// content at the cursor into the first table cell. This behavior aligns us with Word 2010. /// </summary> private void TryMoveIntoNextTable(HtmlEditorSelectionOperationEventArgs ea) { if (!SelectedMarkupRange.IsEmpty() || IsEditFieldSelected) { return; } IHTMLElement nextTable = null; bool enteredABlockElement = false; int numBlockElementsBetweenSelectionAndNextTable = 0; IHTMLElement brElement = null; MarkupRange postBodyRange = MarkupServices.CreateMarkupRange(PostBodyElement, false); postBodyRange.Start.MoveToPointer(SelectedMarkupRange.Start); postBodyRange.WalkRange( delegate (MarkupRange currentRange, MarkupContext context, string text) { if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text || context.Element == null) { // There is text between the cursor position and the next table. Stop processing and quit. return false; } if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_NoScope && ElementFilters.IsVisibleEmptyElement(context.Element)) { if (context.Element is IHTMLBRElement && brElement == null) { // Allow up to one <br> element to be between the cursor position and the next table as // long as its in our parent block element, since it won't cause a line break, e.g.: // <div>Hello[cursor]<br></div><table>...</table> // <table><tr><td>Hello[cursor]<br><table>...</table></td></tr></table> brElement = context.Element; return true; } // There is content between the cursor position and the next table. Stop processing and quit. return false; } if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope) { if (context.Element is IHTMLTable) { if (StopTryMoveIntoNextTable(context.Element)) return false; // We found the next table so we can stop walking the range. nextTable = context.Element; return false; } if (ElementFilters.IsBlockElement(context.Element) || ElementFilters.IsListElement(context.Element)) { // Set a flag so that we know that since starting at the cursor position, we've walked into // a block element, e.g.: // <div>Hello[cursor]<div>[currentPosition]</div></div> // <ul><li>Hello[cursor]</li><li>[currentPosition]</li></ul> enteredABlockElement = true; } } if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope) { if (ElementFilters.IsBlockElement(context.Element) || ElementFilters.IsListElement(context.Element)) { if (enteredABlockElement) { // We entered and exited a block element without finding a table, e.g.: // <div>Hello[cursor]<div></div>[currentPosition]</div> // <ul><li>Hello[cursor]</li><li></li>[currentPosition]</ul> // Stop processing and quit. return false; } else { // We count up the number of block elements we've exited so we know what parent to // remove later, e.g.: // <div><div><div>Hello[cursor]</div></div>[currentPosition]</div><table>...</table> numBlockElementsBetweenSelectionAndNextTable++; } } else if (ElementFilters.IsTableCellElement(context.Element)) { // We exited a table cell element without finding a table. Stop processing and quit. // <table><tr><td>Hello[cursor]</td>[currPosition]</tr></table> return false; } } // Keep walking the range. return true; }, false); if (nextTable == null) { return; } // First, let's figure out what element the current selection is in. IHTMLElement parentBlockElement = SelectedMarkupRange.ParentElement( e => ElementFilters.IsListItemElement(e) || ElementFilters.IsTableCellElement(e) || ((ElementFilters.IsBlockElement(e) || ElementFilters.IsListElement(e)) && --numBlockElementsBetweenSelectionAndNextTable <= 0)); // This represents the range of markup that we should move into the table. MarkupRange currentBlockRange = MarkupServices.CreateMarkupRange(parentBlockElement, false); using (IUndoUnit undoUnit = CreateUndoUnit()) { if (brElement != null) { if (!currentBlockRange.InRange(brElement)) { // There's a <br> causing a line break between the cursor and the next table, so we won't try to move anything. return; } else { // The <br> would cause a line break if we moved it into the <table>, e.g.: // <div>Hello<br></div><table><tr><td></td></tr></table> - if we moved "Hello<br>" into the // <td>, the <br> would now cause a line break (although it did not when wrapped in a <div>). HTMLElementHelper.RemoveElement(brElement); } } // In the case of <div>Some text...|<table>...</table>...</div> - we need to be careful not to remove the <div>. if (currentBlockRange.InRange(nextTable)) { currentBlockRange.Start.MoveAdjacentToElement(parentBlockElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); currentBlockRange.End.MoveAdjacentToElement(nextTable, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); if (ElementFilters.IsTableCellElement(parentBlockElement)) { // We use <br>s for line breaks in tables. IHTMLElement[] brElements = currentBlockRange.GetElements(e => e is IHTMLBRElement, true); if (brElements.Length > 0) { // We'll only move the last line, e.g.: // <table><tr><td>Hello<br>World<table>...<table></td></tr></table> - only move "World" into the nested table. currentBlockRange.Start.MoveAdjacentToElement(brElements[brElements.Length - 1], _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); } } parentBlockElement = null; } else { // Fix up the range to only contain inline elements and text, e.g.: // <div><div><div><em>Hello</em></div></div></div><table>...</table> - we'll only move "<em>Hello</em>" into the table. currentBlockRange.SelectInner(); MarkupPointerMoveHelper.MoveUnitBounded(currentBlockRange.Start, MarkupPointerMoveHelper.MoveDirection.LEFT, MarkupPointerAdjacency.BeforeExitBlock, parentBlockElement); MarkupPointerMoveHelper.MoveUnitBounded(currentBlockRange.End, MarkupPointerMoveHelper.MoveDirection.RIGHT, MarkupPointerAdjacency.BeforeExitBlock, parentBlockElement); } // We're ready to move content into the <table> we found, now find the first <td> inside of it. MarkupPointer p = MarkupServices.CreateMarkupPointer(nextTable, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); IHTMLElement firstTd = p.SeekElementRight(e => e is IHTMLTableCell, MarkupServices.CreateMarkupPointer(nextTable, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd)); if (firstTd != null) { if (MarkupServices.CreateMarkupRange(firstTd, false).IsEmptyOfContent()) { // The table behavior leaves an empty space in the table, so make sure we remove it before inserting. firstTd.innerHTML = String.Empty; } // Move the content at the cursor into the <td>. MarkupPointer afterBeginFirstTd = MarkupServices.CreateMarkupPointer(firstTd, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); afterBeginFirstTd.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right; MarkupServices.Move(currentBlockRange.Start, currentBlockRange.End, afterBeginFirstTd); // Remove the original container around the cursor if necessary, e.g.: // <p>Hello[cursor]</p><table><tr><td></td></tr></table> should become => // <table><tr><td>Hello[cursor]</td></tr></table> if (parentBlockElement != null) { if (ElementFilters.IsListItemElement(parentBlockElement)) { // If this is the only list item in the list, then we want to remove the list too, e.g.: // <ul><li>Hello[cursor]</li></ul><table><tr><td></td></tr></table> should become => // <table><tr><td>Hello[cursor]</td></tr></table> IHTMLElement listElement = parentBlockElement.parentElement; if (listElement != null && ElementFilters.IsListElement(listElement) && ((IHTMLElementCollection)listElement.children).length == 1) { HTMLElementHelper.RemoveElement(listElement); } } HTMLElementHelper.RemoveElement(parentBlockElement); } // Move the cursor inside the table. MarkupServices.CreateMarkupRange(afterBeginFirstTd, afterBeginFirstTd).ToTextRange().select(); // Make sure the content gets spellchecked since we just moved it. _damageServices.AddDamage(MarkupServices.CreateMarkupRange(firstTd, false)); ea.Handled = true; undoUnit.Commit(); } } }
protected virtual void OnClear(HtmlEditorSelectionOperationEventArgs ea) { if (HandleClear != null) { foreach (HtmlEditorSelectionOperationEventHandler handler in HandleClear.GetInvocationList()) { handler(ea); if (ea.Handled) break; } } }
private void ClearSelection(IHtmlEditorSelection selection) { using (_damageServices.CreateDeleteDamageTracker(selection.SelectedMarkupRange)) { // allow override by components HtmlEditorSelectionOperationEventArgs ea = new HtmlEditorSelectionOperationEventArgs(selection); OnClear(ea); // if no override then execute if (!ea.Handled) { GetMshtmlCommand(IDM.DELETE).Execute(); // Clear out any state maintained by MSHTML regarding the backcolor. if (selection.SelectedMarkupRange.IsTagId(_ELEMENT_TAG_ID.TAGID_FONT, false)) GetMshtmlCommand(IDM.BACKCOLOR).Execute(null); } FireSelectionChanged(); } }
private void CopySelection(IHtmlEditorSelection selection) { // allow override by components HtmlEditorSelectionOperationEventArgs ea = new HtmlEditorSelectionOperationEventArgs(selection); OnCopy(ea); // if no override then execute if (!ea.Handled) GetMshtmlCommand(IDM.COPY).Execute(); }
private void CutSelection(IHtmlEditorSelection selection) { using (_damageServices.CreateDeleteDamageTracker(selection.SelectedMarkupRange)) { // allow override by components HtmlEditorSelectionOperationEventArgs ea = new HtmlEditorSelectionOperationEventArgs(selection); OnCut(ea); // if no override then execute if (!ea.Handled) GetMshtmlCommand(IDM.CUT).Execute(); } }
private void EditorContext_HandleCut(HtmlEditorSelectionOperationEventArgs ea) { if (Selected && MultipleCellsSelected && !EntireTableSelected) { EditorContext.ExecuteCommand(IDM.COPY); using (IUndoUnit undoUnit = EditorContext.CreateUndoUnit()) { TableEditor.ClearCells(EditorContext); undoUnit.Commit(); } ea.Handled = true; } }