michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/dom/Selection.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsAString.h" michael@0: #include "nsAlgorithm.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsDebug.h" michael@0: #include "nsEditProperty.h" michael@0: #include "nsEditor.h" michael@0: #include "nsEditorUtils.h" michael@0: #include "nsError.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsHTMLEditUtils.h" michael@0: #include "nsHTMLEditor.h" michael@0: #include "nsIAtom.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMNode.h" michael@0: #include "nsIDOMRange.h" michael@0: #include "nsIEditor.h" michael@0: #include "nsIFrame.h" michael@0: #include "nsIHTMLEditor.h" michael@0: #include "nsINode.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsISupportsUtils.h" michael@0: #include "nsITableCellLayout.h" // For efficient access to table cell michael@0: #include "nsITableEditor.h" michael@0: #include "nsLiteralString.h" michael@0: #include "nsQueryFrame.h" michael@0: #include "nsString.h" michael@0: #include "nsTArray.h" michael@0: #include "nsTableCellFrame.h" michael@0: #include "nsTableOuterFrame.h" michael@0: #include "nscore.h" michael@0: #include michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: /*************************************************************************** michael@0: * stack based helper class for restoring selection after table edit michael@0: */ michael@0: class MOZ_STACK_CLASS nsSetSelectionAfterTableEdit michael@0: { michael@0: private: michael@0: nsCOMPtr mEd; michael@0: nsCOMPtr mTable; michael@0: int32_t mCol, mRow, mDirection, mSelected; michael@0: public: michael@0: nsSetSelectionAfterTableEdit(nsITableEditor *aEd, nsIDOMElement* aTable, michael@0: int32_t aRow, int32_t aCol, int32_t aDirection, michael@0: bool aSelected) : michael@0: mEd(do_QueryInterface(aEd)) michael@0: { michael@0: mTable = aTable; michael@0: mRow = aRow; michael@0: mCol = aCol; michael@0: mDirection = aDirection; michael@0: mSelected = aSelected; michael@0: } michael@0: michael@0: ~nsSetSelectionAfterTableEdit() michael@0: { michael@0: if (mEd) michael@0: mEd->SetSelectionAfterTableEdit(mTable, mRow, mCol, mDirection, mSelected); michael@0: } michael@0: // This is needed to abort the caret reset in the destructor michael@0: // when one method yields control to another michael@0: void CancelSetCaret() {mEd = nullptr; mTable = nullptr;} michael@0: }; michael@0: michael@0: // Stack-class to turn on/off selection batching for table selection michael@0: class MOZ_STACK_CLASS nsSelectionBatcherForTable michael@0: { michael@0: private: michael@0: nsCOMPtr mSelection; michael@0: public: michael@0: nsSelectionBatcherForTable(nsISelection *aSelection) michael@0: { michael@0: nsCOMPtr sel(aSelection); michael@0: mSelection = do_QueryInterface(sel); michael@0: if (mSelection) mSelection->StartBatchChanges(); michael@0: } michael@0: virtual ~nsSelectionBatcherForTable() michael@0: { michael@0: if (mSelection) mSelection->EndBatchChanges(); michael@0: } michael@0: }; michael@0: michael@0: // Table Editing helper utilities (not exposed in IDL) michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::InsertCell(nsIDOMElement *aCell, int32_t aRowSpan, int32_t aColSpan, michael@0: bool aAfter, bool aIsHeader, nsIDOMElement **aNewCell) michael@0: { michael@0: NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); michael@0: if (aNewCell) *aNewCell = nullptr; michael@0: michael@0: // And the parent and offsets needed to do an insert michael@0: nsCOMPtr cellParent; michael@0: nsresult res = aCell->GetParentNode(getter_AddRefs(cellParent)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(cellParent, NS_ERROR_NULL_POINTER); michael@0: michael@0: int32_t cellOffset = GetChildOffset(aCell, cellParent); michael@0: michael@0: nsCOMPtr newCell; michael@0: if (aIsHeader) michael@0: res = CreateElementWithDefaults(NS_LITERAL_STRING("th"), getter_AddRefs(newCell)); michael@0: else michael@0: res = CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell)); michael@0: michael@0: if(NS_FAILED(res)) return res; michael@0: if(!newCell) return NS_ERROR_FAILURE; michael@0: michael@0: //Optional: return new cell created michael@0: if (aNewCell) michael@0: { michael@0: *aNewCell = newCell.get(); michael@0: NS_ADDREF(*aNewCell); michael@0: } michael@0: michael@0: if( aRowSpan > 1) michael@0: { michael@0: // Note: Do NOT use editor transaction for this michael@0: nsAutoString newRowSpan; michael@0: newRowSpan.AppendInt(aRowSpan, 10); michael@0: newCell->SetAttribute(NS_LITERAL_STRING("rowspan"), newRowSpan); michael@0: } michael@0: if( aColSpan > 1) michael@0: { michael@0: // Note: Do NOT use editor transaction for this michael@0: nsAutoString newColSpan; michael@0: newColSpan.AppendInt(aColSpan, 10); michael@0: newCell->SetAttribute(NS_LITERAL_STRING("colspan"), newColSpan); michael@0: } michael@0: if(aAfter) cellOffset++; michael@0: michael@0: //Don't let Rules System change the selection michael@0: nsAutoTxnsConserveSelection dontChangeSelection(this); michael@0: return InsertNode(newCell, cellParent, cellOffset); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsHTMLEditor::SetColSpan(nsIDOMElement *aCell, int32_t aColSpan) michael@0: { michael@0: NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); michael@0: nsAutoString newSpan; michael@0: newSpan.AppendInt(aColSpan, 10); michael@0: return SetAttribute(aCell, NS_LITERAL_STRING("colspan"), newSpan); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsHTMLEditor::SetRowSpan(nsIDOMElement *aCell, int32_t aRowSpan) michael@0: { michael@0: NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); michael@0: nsAutoString newSpan; michael@0: newSpan.AppendInt(aRowSpan, 10); michael@0: return SetAttribute(aCell, NS_LITERAL_STRING("rowspan"), newSpan); michael@0: } michael@0: michael@0: /****************************************************************/ michael@0: michael@0: // Table Editing interface methods michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::InsertTableCell(int32_t aNumber, bool aAfter) michael@0: { michael@0: nsCOMPtr table; michael@0: nsCOMPtr curCell; michael@0: nsCOMPtr cellParent; michael@0: int32_t cellOffset, startRowIndex, startColIndex; michael@0: nsresult res = GetCellContext(nullptr, michael@0: getter_AddRefs(table), michael@0: getter_AddRefs(curCell), michael@0: getter_AddRefs(cellParent), &cellOffset, michael@0: &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: // Don't fail if no cell found michael@0: NS_ENSURE_TRUE(curCell, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: michael@0: // Get more data for current cell in row we are inserting at (we need COLSPAN) michael@0: int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: res = GetCellDataAt(table, startRowIndex, startColIndex, michael@0: getter_AddRefs(curCell), michael@0: &curStartRowIndex, &curStartColIndex, &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE); michael@0: int32_t newCellIndex = aAfter ? (startColIndex+colSpan) : startColIndex; michael@0: //We control selection resetting after the insert... michael@0: nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, newCellIndex, ePreviousColumn, false); michael@0: //...so suppress Rules System selection munging michael@0: nsAutoTxnsConserveSelection dontChangeSelection(this); michael@0: michael@0: int32_t i; michael@0: for (i = 0; i < aNumber; i++) michael@0: { michael@0: nsCOMPtr newCell; michael@0: res = CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell)); michael@0: if (NS_SUCCEEDED(res) && newCell) michael@0: { michael@0: if (aAfter) cellOffset++; michael@0: res = InsertNode(newCell, cellParent, cellOffset); michael@0: if(NS_FAILED(res)) break; michael@0: } michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetFirstRow(nsIDOMElement* aTableElement, nsIDOMNode** aRowNode) michael@0: { michael@0: NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER); michael@0: michael@0: *aRowNode = nullptr; michael@0: michael@0: NS_ENSURE_TRUE(aTableElement, NS_ERROR_NULL_POINTER); michael@0: michael@0: nsCOMPtr tableElement; michael@0: nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aTableElement, getter_AddRefs(tableElement)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(tableElement, NS_ERROR_NULL_POINTER); michael@0: michael@0: nsCOMPtr tableChild; michael@0: res = tableElement->GetFirstChild(getter_AddRefs(tableChild)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: while (tableChild) michael@0: { michael@0: nsCOMPtr content = do_QueryInterface(tableChild); michael@0: if (content) michael@0: { michael@0: nsIAtom *atom = content->Tag(); michael@0: michael@0: if (atom == nsEditProperty::tr) michael@0: { michael@0: // Found a row directly under michael@0: *aRowNode = tableChild; michael@0: NS_ADDREF(*aRowNode); michael@0: return NS_OK; michael@0: } michael@0: // Look for row in one of the row container elements michael@0: if (atom == nsEditProperty::tbody || michael@0: atom == nsEditProperty::thead || michael@0: atom == nsEditProperty::tfoot) michael@0: { michael@0: nsCOMPtr rowNode; michael@0: res = tableChild->GetFirstChild(getter_AddRefs(rowNode)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // We can encounter textnodes here -- must find a row michael@0: while (rowNode && !nsHTMLEditUtils::IsTableRow(rowNode)) michael@0: { michael@0: nsCOMPtr nextNode; michael@0: res = rowNode->GetNextSibling(getter_AddRefs(nextNode)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: rowNode = nextNode; michael@0: } michael@0: if(rowNode) michael@0: { michael@0: *aRowNode = rowNode.get(); michael@0: NS_ADDREF(*aRowNode); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: // Here if table child was a CAPTION or COLGROUP michael@0: // or child of a row parent wasn't a row (bad HTML?), michael@0: // or first child was a textnode michael@0: // Look in next table child michael@0: nsCOMPtr nextChild; michael@0: res = tableChild->GetNextSibling(getter_AddRefs(nextChild)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: tableChild = nextChild; michael@0: }; michael@0: // If here, row was not found michael@0: return NS_EDITOR_ELEMENT_NOT_FOUND; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetNextRow(nsIDOMNode* aCurrentRowNode, nsIDOMNode **aRowNode) michael@0: { michael@0: NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER); michael@0: michael@0: *aRowNode = nullptr; michael@0: michael@0: NS_ENSURE_TRUE(aCurrentRowNode, NS_ERROR_NULL_POINTER); michael@0: michael@0: if (!nsHTMLEditUtils::IsTableRow(aCurrentRowNode)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr nextRow; michael@0: nsresult res = aCurrentRowNode->GetNextSibling(getter_AddRefs(nextRow)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: nsCOMPtr nextNode; michael@0: michael@0: // Skip over any textnodes here michael@0: while (nextRow && !nsHTMLEditUtils::IsTableRow(nextRow)) michael@0: { michael@0: res = nextRow->GetNextSibling(getter_AddRefs(nextNode)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: nextRow = nextNode; michael@0: } michael@0: if(nextRow) michael@0: { michael@0: *aRowNode = nextRow.get(); michael@0: NS_ADDREF(*aRowNode); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // No row found, search for rows in other table sections michael@0: nsCOMPtr rowParent; michael@0: res = aCurrentRowNode->GetParentNode(getter_AddRefs(rowParent)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(rowParent, NS_ERROR_NULL_POINTER); michael@0: michael@0: nsCOMPtr parentSibling; michael@0: res = rowParent->GetNextSibling(getter_AddRefs(parentSibling)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: while (parentSibling) michael@0: { michael@0: res = parentSibling->GetFirstChild(getter_AddRefs(nextRow)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // We can encounter textnodes here -- must find a row michael@0: while (nextRow && !nsHTMLEditUtils::IsTableRow(nextRow)) michael@0: { michael@0: res = nextRow->GetNextSibling(getter_AddRefs(nextNode)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: nextRow = nextNode; michael@0: } michael@0: if(nextRow) michael@0: { michael@0: *aRowNode = nextRow.get(); michael@0: NS_ADDREF(*aRowNode); michael@0: return NS_OK; michael@0: } michael@0: #ifdef DEBUG_cmanske michael@0: printf("GetNextRow: firstChild of row's parent's sibling is not a TR!\n"); michael@0: #endif michael@0: // We arrive here only if a table section has no children michael@0: // or first child of section is not a row (bad HTML or more "_moz_text" nodes!) michael@0: // So look for another section sibling michael@0: res = parentSibling->GetNextSibling(getter_AddRefs(nextNode)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: parentSibling = nextNode; michael@0: } michael@0: // If here, row was not found michael@0: return NS_EDITOR_ELEMENT_NOT_FOUND; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetLastCellInRow(nsIDOMNode* aRowNode, nsIDOMNode** aCellNode) michael@0: { michael@0: NS_ENSURE_TRUE(aCellNode, NS_ERROR_NULL_POINTER); michael@0: michael@0: *aCellNode = nullptr; michael@0: michael@0: NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER); michael@0: michael@0: nsCOMPtr rowChild; michael@0: nsresult res = aRowNode->GetLastChild(getter_AddRefs(rowChild)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: while (rowChild && !nsHTMLEditUtils::IsTableCell(rowChild)) michael@0: { michael@0: // Skip over textnodes michael@0: nsCOMPtr previousChild; michael@0: res = rowChild->GetPreviousSibling(getter_AddRefs(previousChild)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: rowChild = previousChild; michael@0: }; michael@0: if (rowChild) michael@0: { michael@0: *aCellNode = rowChild.get(); michael@0: NS_ADDREF(*aCellNode); michael@0: return NS_OK; michael@0: } michael@0: // If here, cell was not found michael@0: return NS_EDITOR_ELEMENT_NOT_FOUND; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::InsertTableColumn(int32_t aNumber, bool aAfter) michael@0: { michael@0: nsCOMPtr selection; michael@0: nsCOMPtr table; michael@0: nsCOMPtr curCell; michael@0: int32_t startRowIndex, startColIndex; michael@0: nsresult res = GetCellContext(getter_AddRefs(selection), michael@0: getter_AddRefs(table), michael@0: getter_AddRefs(curCell), michael@0: nullptr, nullptr, michael@0: &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: // Don't fail if no cell found michael@0: NS_ENSURE_TRUE(curCell, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: michael@0: // Get more data for current cell (we need ROWSPAN) michael@0: int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: res = GetCellDataAt(table, startRowIndex, startColIndex, michael@0: getter_AddRefs(curCell), michael@0: &curStartRowIndex, &curStartColIndex, michael@0: &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE); michael@0: michael@0: nsAutoEditBatch beginBatching(this); michael@0: // Prevent auto insertion of BR in new cell until we're done michael@0: nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); michael@0: michael@0: // Use column after current cell if requested michael@0: if (aAfter) michael@0: { michael@0: startColIndex += actualColSpan; michael@0: //Detect when user is adding after a COLSPAN=0 case michael@0: // Assume they want to stop the "0" behavior and michael@0: // really add a new column. Thus we set the michael@0: // colspan to its true value michael@0: if (colSpan == 0) michael@0: SetColSpan(curCell, actualColSpan); michael@0: } michael@0: michael@0: int32_t rowCount, colCount, rowIndex; michael@0: res = GetTableSize(table, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: //We reset caret in destructor... michael@0: nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow, false); michael@0: //.. so suppress Rules System selection munging michael@0: nsAutoTxnsConserveSelection dontChangeSelection(this); michael@0: michael@0: // If we are inserting after all existing columns michael@0: // Make sure table is "well formed" michael@0: // before appending new column michael@0: if (startColIndex >= colCount) michael@0: NormalizeTable(table); michael@0: michael@0: nsCOMPtr rowNode; michael@0: for ( rowIndex = 0; rowIndex < rowCount; rowIndex++) michael@0: { michael@0: #ifdef DEBUG_cmanske michael@0: if (rowIndex == rowCount-1) michael@0: printf(" ***InsertTableColumn: Inserting cell at last row: %d\n", rowIndex); michael@0: #endif michael@0: michael@0: if (startColIndex < colCount) michael@0: { michael@0: // We are inserting before an existing column michael@0: res = GetCellDataAt(table, rowIndex, startColIndex, michael@0: getter_AddRefs(curCell), michael@0: &curStartRowIndex, &curStartColIndex, michael@0: &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Don't fail entire process if we fail to find a cell michael@0: // (may fail just in particular rows with < adequate cells per row) michael@0: if (curCell) michael@0: { michael@0: if (curStartColIndex < startColIndex) michael@0: { michael@0: // We have a cell spanning this location michael@0: // Simply increase its colspan to keep table rectangular michael@0: // Note: we do nothing if colsSpan=0, michael@0: // since it should automatically span the new column michael@0: if (colSpan > 0) michael@0: SetColSpan(curCell, colSpan+aNumber); michael@0: } else { michael@0: // Simply set selection to the current cell michael@0: // so we can let InsertTableCell() do the work michael@0: // Insert a new cell before current one michael@0: selection->Collapse(curCell, 0); michael@0: res = InsertTableCell(aNumber, false); michael@0: } michael@0: } michael@0: } else { michael@0: // Get current row and append new cells after last cell in row michael@0: if(rowIndex == 0) michael@0: res = GetFirstRow(table.get(), getter_AddRefs(rowNode)); michael@0: else michael@0: { michael@0: nsCOMPtr nextRow; michael@0: res = GetNextRow(rowNode.get(), getter_AddRefs(nextRow)); michael@0: rowNode = nextRow; michael@0: } michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: if (rowNode) michael@0: { michael@0: nsCOMPtr lastCell; michael@0: res = GetLastCellInRow(rowNode, getter_AddRefs(lastCell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(lastCell, NS_ERROR_FAILURE); michael@0: michael@0: curCell = do_QueryInterface(lastCell); michael@0: if (curCell) michael@0: { michael@0: // Simply add same number of cells to each row michael@0: // Although tempted to check cell indexes for curCell, michael@0: // the effects of COLSPAN>1 in some cells makes this futile! michael@0: // We must use NormalizeTable first to assure michael@0: // that there are cells in each cellmap location michael@0: selection->Collapse(curCell, 0); michael@0: res = InsertTableCell(aNumber, true); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::InsertTableRow(int32_t aNumber, bool aAfter) michael@0: { michael@0: nsCOMPtr selection; michael@0: nsCOMPtr table; michael@0: nsCOMPtr curCell; michael@0: michael@0: int32_t startRowIndex, startColIndex; michael@0: nsresult res = GetCellContext(nullptr, michael@0: getter_AddRefs(table), michael@0: getter_AddRefs(curCell), michael@0: nullptr, nullptr, michael@0: &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: // Don't fail if no cell found michael@0: NS_ENSURE_TRUE(curCell, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: michael@0: // Get more data for current cell in row we are inserting at (we need COLSPAN) michael@0: int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: res = GetCellDataAt(table, startRowIndex, startColIndex, michael@0: getter_AddRefs(curCell), michael@0: &curStartRowIndex, &curStartColIndex, michael@0: &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE); michael@0: michael@0: int32_t rowCount, colCount; michael@0: res = GetTableSize(table, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: nsAutoEditBatch beginBatching(this); michael@0: // Prevent auto insertion of BR in new cell until we're done michael@0: nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); michael@0: michael@0: if (aAfter) michael@0: { michael@0: // Use row after current cell michael@0: startRowIndex += actualRowSpan; michael@0: michael@0: //Detect when user is adding after a ROWSPAN=0 case michael@0: // Assume they want to stop the "0" behavior and michael@0: // really add a new row. Thus we set the michael@0: // rowspan to its true value michael@0: if (rowSpan == 0) michael@0: SetRowSpan(curCell, actualRowSpan); michael@0: } michael@0: michael@0: //We control selection resetting after the insert... michael@0: nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); michael@0: //...so suppress Rules System selection munging michael@0: nsAutoTxnsConserveSelection dontChangeSelection(this); michael@0: michael@0: nsCOMPtr cellForRowParent; michael@0: int32_t cellsInRow = 0; michael@0: if (startRowIndex < rowCount) michael@0: { michael@0: // We are inserting above an existing row michael@0: // Get each cell in the insert row to adjust for COLSPAN effects while we michael@0: // count how many cells are needed michael@0: int32_t colIndex = 0; michael@0: // This returns NS_TABLELAYOUT_CELL_NOT_FOUND when we run past end of row, michael@0: // which passes the NS_SUCCEEDED macro michael@0: while ( NS_OK == GetCellDataAt(table, startRowIndex, colIndex, michael@0: getter_AddRefs(curCell), michael@0: &curStartRowIndex, &curStartColIndex, michael@0: &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, michael@0: &isSelected) ) michael@0: { michael@0: if (curCell) michael@0: { michael@0: if (curStartRowIndex < startRowIndex) michael@0: { michael@0: // We have a cell spanning this location michael@0: // Simply increase its rowspan michael@0: //Note that if rowSpan == 0, we do nothing, michael@0: // since that cell should automatically extend into the new row michael@0: if (rowSpan > 0) michael@0: SetRowSpan(curCell, rowSpan+aNumber); michael@0: } else { michael@0: // We have a cell in the insert row michael@0: michael@0: // Count the number of cells we need to add to the new row michael@0: cellsInRow += actualColSpan; michael@0: michael@0: // Save cell we will use below michael@0: if (!cellForRowParent) michael@0: cellForRowParent = curCell; michael@0: } michael@0: // Next cell in row michael@0: colIndex += actualColSpan; michael@0: } michael@0: else michael@0: colIndex++; michael@0: } michael@0: } else { michael@0: // We are adding a new row after all others michael@0: // If it weren't for colspan=0 effect, michael@0: // we could simply use colCount for number of new cells... michael@0: cellsInRow = colCount; michael@0: michael@0: // ...but we must compensate for all cells with rowSpan = 0 in the last row michael@0: int32_t lastRow = rowCount-1; michael@0: int32_t tempColIndex = 0; michael@0: while ( NS_OK == GetCellDataAt(table, lastRow, tempColIndex, michael@0: getter_AddRefs(curCell), michael@0: &curStartRowIndex, &curStartColIndex, michael@0: &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, michael@0: &isSelected) ) michael@0: { michael@0: if (rowSpan == 0) michael@0: cellsInRow -= actualColSpan; michael@0: michael@0: tempColIndex += actualColSpan; michael@0: michael@0: // Save cell from the last row that we will use below michael@0: if (!cellForRowParent && curStartRowIndex == lastRow) michael@0: cellForRowParent = curCell; michael@0: } michael@0: } michael@0: michael@0: if (cellsInRow > 0) michael@0: { michael@0: // The row parent and offset where we will insert new row michael@0: nsCOMPtr parentOfRow; michael@0: int32_t newRowOffset; michael@0: michael@0: NS_NAMED_LITERAL_STRING(trStr, "tr"); michael@0: if (cellForRowParent) michael@0: { michael@0: nsCOMPtr parentRow; michael@0: res = GetElementOrParentByTagName(trStr, cellForRowParent, getter_AddRefs(parentRow)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(parentRow, NS_ERROR_NULL_POINTER); michael@0: michael@0: parentRow->GetParentNode(getter_AddRefs(parentOfRow)); michael@0: NS_ENSURE_TRUE(parentOfRow, NS_ERROR_NULL_POINTER); michael@0: michael@0: newRowOffset = GetChildOffset(parentRow, parentOfRow); michael@0: michael@0: // Adjust for when adding past the end michael@0: if (aAfter && startRowIndex >= rowCount) michael@0: newRowOffset++; michael@0: } michael@0: else michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: for (int32_t row = 0; row < aNumber; row++) michael@0: { michael@0: // Create a new row michael@0: nsCOMPtr newRow; michael@0: res = CreateElementWithDefaults(trStr, getter_AddRefs(newRow)); michael@0: if (NS_SUCCEEDED(res)) michael@0: { michael@0: NS_ENSURE_TRUE(newRow, NS_ERROR_FAILURE); michael@0: michael@0: for (int32_t i = 0; i < cellsInRow; i++) michael@0: { michael@0: nsCOMPtr newCell; michael@0: res = CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(newCell, NS_ERROR_FAILURE); michael@0: michael@0: // Don't use transaction system yet! (not until entire row is inserted) michael@0: nsCOMPtrresultNode; michael@0: res = newRow->AppendChild(newCell, getter_AddRefs(resultNode)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: // Use transaction system to insert the entire row+cells michael@0: // (Note that rows are inserted at same childoffset each time) michael@0: res = InsertNode(newRow, parentOfRow, newRowOffset); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: } michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: // Editor helper only michael@0: // XXX Code changed for bug 217717 and now we don't need aSelection param michael@0: // TODO: Remove aSelection param michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::DeleteTable2(nsIDOMElement *aTable, nsISelection *aSelection) michael@0: { michael@0: NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); michael@0: michael@0: // Select the table michael@0: nsresult res = ClearSelection(); michael@0: if (NS_SUCCEEDED(res)) michael@0: res = AppendNodeToSelectionAsRange(aTable); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: return DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::DeleteTable() michael@0: { michael@0: nsCOMPtr selection; michael@0: nsCOMPtr table; michael@0: nsresult res = GetCellContext(getter_AddRefs(selection), michael@0: getter_AddRefs(table), michael@0: nullptr, nullptr, nullptr, nullptr, nullptr); michael@0: michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: nsAutoEditBatch beginBatching(this); michael@0: return DeleteTable2(table, selection); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::DeleteTableCell(int32_t aNumber) michael@0: { michael@0: nsCOMPtr selection; michael@0: nsCOMPtr table; michael@0: nsCOMPtr cell; michael@0: int32_t startRowIndex, startColIndex; michael@0: michael@0: michael@0: nsresult res = GetCellContext(getter_AddRefs(selection), michael@0: getter_AddRefs(table), michael@0: getter_AddRefs(cell), michael@0: nullptr, nullptr, michael@0: &startRowIndex, &startColIndex); michael@0: michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: // Don't fail if we didn't find a table or cell michael@0: NS_ENSURE_TRUE(table && cell, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: michael@0: nsAutoEditBatch beginBatching(this); michael@0: // Prevent rules testing until we're done michael@0: nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); michael@0: michael@0: nsCOMPtr firstCell; michael@0: nsCOMPtr range; michael@0: res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: int32_t rangeCount; michael@0: res = selection->GetRangeCount(&rangeCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: if (firstCell && rangeCount > 1) michael@0: { michael@0: // When > 1 selected cell, michael@0: // ignore aNumber and use selected cells michael@0: cell = firstCell; michael@0: michael@0: int32_t rowCount, colCount; michael@0: res = GetTableSize(table, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Get indexes -- may be different than original cell michael@0: res = GetCellIndexes(cell, &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // The setCaret object will call SetSelectionAfterTableEdit in its destructor michael@0: nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); michael@0: nsAutoTxnsConserveSelection dontChangeSelection(this); michael@0: michael@0: bool checkToDeleteRow = true; michael@0: bool checkToDeleteColumn = true; michael@0: while (cell) michael@0: { michael@0: bool deleteRow = false; michael@0: bool deleteCol = false; michael@0: michael@0: if (checkToDeleteRow) michael@0: { michael@0: // Optimize to delete an entire row michael@0: // Clear so we don't repeat AllCellsInRowSelected within the same row michael@0: checkToDeleteRow = false; michael@0: michael@0: deleteRow = AllCellsInRowSelected(table, startRowIndex, colCount); michael@0: if (deleteRow) michael@0: { michael@0: // First, find the next cell in a different row michael@0: // to continue after we delete this row michael@0: int32_t nextRow = startRowIndex; michael@0: while (nextRow == startRowIndex) michael@0: { michael@0: res = GetNextSelectedCell(nullptr, getter_AddRefs(cell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if (!cell) break; michael@0: res = GetCellIndexes(cell, &nextRow, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: // Delete entire row michael@0: res = DeleteRow(table, startRowIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: if (cell) michael@0: { michael@0: // For the next cell: Subtract 1 for row we deleted michael@0: startRowIndex = nextRow - 1; michael@0: // Set true since we know we will look at a new row next michael@0: checkToDeleteRow = true; michael@0: } michael@0: } michael@0: } michael@0: if (!deleteRow) michael@0: { michael@0: if (checkToDeleteColumn) michael@0: { michael@0: // Optimize to delete an entire column michael@0: // Clear this so we don't repeat AllCellsInColSelected within the same Col michael@0: checkToDeleteColumn = false; michael@0: michael@0: deleteCol = AllCellsInColumnSelected(table, startColIndex, colCount); michael@0: if (deleteCol) michael@0: { michael@0: // First, find the next cell in a different column michael@0: // to continue after we delete this column michael@0: int32_t nextCol = startColIndex; michael@0: while (nextCol == startColIndex) michael@0: { michael@0: res = GetNextSelectedCell(nullptr, getter_AddRefs(cell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if (!cell) break; michael@0: res = GetCellIndexes(cell, &startRowIndex, &nextCol); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: // Delete entire Col michael@0: res = DeleteColumn(table, startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if (cell) michael@0: { michael@0: // For the next cell, subtract 1 for col. deleted michael@0: startColIndex = nextCol - 1; michael@0: // Set true since we know we will look at a new column next michael@0: checkToDeleteColumn = true; michael@0: } michael@0: } michael@0: } michael@0: if (!deleteCol) michael@0: { michael@0: // First get the next cell to delete michael@0: nsCOMPtr nextCell; michael@0: res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(nextCell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Then delete the cell michael@0: res = DeleteNode(cell); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // The next cell to delete michael@0: cell = nextCell; michael@0: if (cell) michael@0: { michael@0: res = GetCellIndexes(cell, &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: else for (int32_t i = 0; i < aNumber; i++) michael@0: { michael@0: res = GetCellContext(getter_AddRefs(selection), michael@0: getter_AddRefs(table), michael@0: getter_AddRefs(cell), michael@0: nullptr, nullptr, michael@0: &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: // Don't fail if no cell found michael@0: NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: michael@0: if (1 == GetNumberOfCellsInRow(table, startRowIndex)) michael@0: { michael@0: nsCOMPtr parentRow; michael@0: res = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cell, getter_AddRefs(parentRow)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(parentRow, NS_ERROR_NULL_POINTER); michael@0: michael@0: // We should delete the row instead, michael@0: // but first check if its the only row left michael@0: // so we can delete the entire table michael@0: int32_t rowCount, colCount; michael@0: res = GetTableSize(table, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: if (rowCount == 1) michael@0: return DeleteTable2(table, selection); michael@0: michael@0: // We need to call DeleteTableRow to handle cells with rowspan michael@0: res = DeleteTableRow(1); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: else michael@0: { michael@0: // More than 1 cell in the row michael@0: michael@0: // The setCaret object will call SetSelectionAfterTableEdit in its destructor michael@0: nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); michael@0: nsAutoTxnsConserveSelection dontChangeSelection(this); michael@0: michael@0: res = DeleteNode(cell); michael@0: // If we fail, don't try to delete any more cells??? michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::DeleteTableCellContents() michael@0: { michael@0: nsCOMPtr selection; michael@0: nsCOMPtr table; michael@0: nsCOMPtr cell; michael@0: int32_t startRowIndex, startColIndex; michael@0: nsresult res; michael@0: res = GetCellContext(getter_AddRefs(selection), michael@0: getter_AddRefs(table), michael@0: getter_AddRefs(cell), michael@0: nullptr, nullptr, michael@0: &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: // Don't fail if no cell found michael@0: NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: michael@0: michael@0: nsAutoEditBatch beginBatching(this); michael@0: // Prevent rules testing until we're done michael@0: nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); michael@0: //Don't let Rules System change the selection michael@0: nsAutoTxnsConserveSelection dontChangeSelection(this); michael@0: michael@0: michael@0: nsCOMPtr firstCell; michael@0: nsCOMPtr range; michael@0: res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: michael@0: if (firstCell) michael@0: { michael@0: cell = firstCell; michael@0: res = GetCellIndexes(cell, &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: michael@0: nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); michael@0: michael@0: while (cell) michael@0: { michael@0: DeleteCellContents(cell); michael@0: if (firstCell) michael@0: { michael@0: // We doing a selected cells, so do all of them michael@0: res = GetNextSelectedCell(nullptr, getter_AddRefs(cell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: else michael@0: cell = nullptr; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::DeleteCellContents(nsIDOMElement *aCell) michael@0: { michael@0: NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); michael@0: michael@0: // Prevent rules testing until we're done michael@0: nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); michael@0: michael@0: nsCOMPtr child; michael@0: bool hasChild; michael@0: aCell->HasChildNodes(&hasChild); michael@0: michael@0: while (hasChild) michael@0: { michael@0: aCell->GetLastChild(getter_AddRefs(child)); michael@0: nsresult res = DeleteNode(child); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: aCell->HasChildNodes(&hasChild); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::DeleteTableColumn(int32_t aNumber) michael@0: { michael@0: nsCOMPtr selection; michael@0: nsCOMPtr table; michael@0: nsCOMPtr cell; michael@0: int32_t startRowIndex, startColIndex, rowCount, colCount; michael@0: nsresult res = GetCellContext(getter_AddRefs(selection), michael@0: getter_AddRefs(table), michael@0: getter_AddRefs(cell), michael@0: nullptr, nullptr, michael@0: &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: // Don't fail if no cell found michael@0: NS_ENSURE_TRUE(table && cell, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: michael@0: res = GetTableSize(table, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Shortcut the case of deleting all columns in table michael@0: if(startColIndex == 0 && aNumber >= colCount) michael@0: return DeleteTable2(table, selection); michael@0: michael@0: // Check for counts too high michael@0: aNumber = std::min(aNumber,(colCount-startColIndex)); michael@0: michael@0: nsAutoEditBatch beginBatching(this); michael@0: // Prevent rules testing until we're done michael@0: nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); michael@0: michael@0: // Test if deletion is controlled by selected cells michael@0: nsCOMPtr firstCell; michael@0: nsCOMPtr range; michael@0: res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: int32_t rangeCount; michael@0: res = selection->GetRangeCount(&rangeCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: if (firstCell && rangeCount > 1) michael@0: { michael@0: // Fetch indexes again - may be different for selected cells michael@0: res = GetCellIndexes(firstCell, &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: //We control selection resetting after the insert... michael@0: nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow, false); michael@0: michael@0: if (firstCell && rangeCount > 1) michael@0: { michael@0: // Use selected cells to determine what rows to delete michael@0: cell = firstCell; michael@0: michael@0: while (cell) michael@0: { michael@0: if (cell != firstCell) michael@0: { michael@0: res = GetCellIndexes(cell, &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: // Find the next cell in a different column michael@0: // to continue after we delete this column michael@0: int32_t nextCol = startColIndex; michael@0: while (nextCol == startColIndex) michael@0: { michael@0: res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if (!cell) break; michael@0: res = GetCellIndexes(cell, &startRowIndex, &nextCol); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: res = DeleteColumn(table, startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: } michael@0: else for (int32_t i = 0; i < aNumber; i++) michael@0: { michael@0: res = DeleteColumn(table, startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::DeleteColumn(nsIDOMElement *aTable, int32_t aColIndex) michael@0: { michael@0: NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); michael@0: michael@0: nsCOMPtr cell; michael@0: nsCOMPtr cellInDeleteCol; michael@0: int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: int32_t rowIndex = 0; michael@0: nsresult res = NS_OK; michael@0: michael@0: do { michael@0: res = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell), michael@0: &startRowIndex, &startColIndex, &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: if (cell) michael@0: { michael@0: // Find cells that don't start in column we are deleting michael@0: if (startColIndex < aColIndex || colSpan > 1 || colSpan == 0) michael@0: { michael@0: // We have a cell spanning this location michael@0: // Decrease its colspan to keep table rectangular, michael@0: // but if colSpan=0, it will adjust automatically michael@0: if (colSpan > 0) michael@0: { michael@0: NS_ASSERTION((colSpan > 1),"Bad COLSPAN in DeleteTableColumn"); michael@0: SetColSpan(cell, colSpan-1); michael@0: } michael@0: if (startColIndex == aColIndex) michael@0: { michael@0: // Cell is in column to be deleted, but must have colspan > 1, michael@0: // so delete contents of cell instead of cell itself michael@0: // (We must have reset colspan above) michael@0: DeleteCellContents(cell); michael@0: } michael@0: // To next cell in column michael@0: rowIndex += actualRowSpan; michael@0: } michael@0: else michael@0: { michael@0: // Delete the cell michael@0: if (1 == GetNumberOfCellsInRow(aTable, rowIndex)) michael@0: { michael@0: // Only 1 cell in row - delete the row michael@0: nsCOMPtr parentRow; michael@0: res = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cell, getter_AddRefs(parentRow)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if(!parentRow) return NS_ERROR_NULL_POINTER; michael@0: michael@0: // But first check if its the only row left michael@0: // so we can delete the entire table michael@0: // (This should never happen but it's the safe thing to do) michael@0: int32_t rowCount, colCount; michael@0: res = GetTableSize(aTable, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: if (rowCount == 1) michael@0: { michael@0: nsCOMPtr selection; michael@0: res = GetSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); michael@0: return DeleteTable2(aTable, selection); michael@0: } michael@0: michael@0: // Delete the row by placing caret in cell we were to delete michael@0: // We need to call DeleteTableRow to handle cells with rowspan michael@0: res = DeleteRow(aTable, startRowIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Note that we don't incremenet rowIndex michael@0: // since a row was deleted and "next" michael@0: // row now has current rowIndex michael@0: } michael@0: else michael@0: { michael@0: // A more "normal" deletion michael@0: res = DeleteNode(cell); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: //Skip over any rows spanned by this cell michael@0: rowIndex += actualRowSpan; michael@0: } michael@0: } michael@0: } michael@0: } while (cell); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::DeleteTableRow(int32_t aNumber) michael@0: { michael@0: nsCOMPtr selection; michael@0: nsCOMPtr table; michael@0: nsCOMPtr cell; michael@0: int32_t startRowIndex, startColIndex; michael@0: int32_t rowCount, colCount; michael@0: nsresult res = GetCellContext(getter_AddRefs(selection), michael@0: getter_AddRefs(table), michael@0: getter_AddRefs(cell), michael@0: nullptr, nullptr, michael@0: &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: // Don't fail if no cell found michael@0: NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: michael@0: res = GetTableSize(table, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Shortcut the case of deleting all rows in table michael@0: if(startRowIndex == 0 && aNumber >= rowCount) michael@0: return DeleteTable2(table, selection); michael@0: michael@0: nsAutoEditBatch beginBatching(this); michael@0: // Prevent rules testing until we're done michael@0: nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); michael@0: michael@0: nsCOMPtr firstCell; michael@0: nsCOMPtr range; michael@0: res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: int32_t rangeCount; michael@0: res = selection->GetRangeCount(&rangeCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: if (firstCell && rangeCount > 1) michael@0: { michael@0: // Fetch indexes again - may be different for selected cells michael@0: res = GetCellIndexes(firstCell, &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: michael@0: //We control selection resetting after the insert... michael@0: nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow, false); michael@0: // Don't change selection during deletions michael@0: nsAutoTxnsConserveSelection dontChangeSelection(this); michael@0: michael@0: if (firstCell && rangeCount > 1) michael@0: { michael@0: // Use selected cells to determine what rows to delete michael@0: cell = firstCell; michael@0: michael@0: while (cell) michael@0: { michael@0: if (cell != firstCell) michael@0: { michael@0: res = GetCellIndexes(cell, &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: // Find the next cell in a different row michael@0: // to continue after we delete this row michael@0: int32_t nextRow = startRowIndex; michael@0: while (nextRow == startRowIndex) michael@0: { michael@0: res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if (!cell) break; michael@0: res = GetCellIndexes(cell, &nextRow, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: // Delete entire row michael@0: res = DeleteRow(table, startRowIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: } michael@0: else michael@0: { michael@0: // Check for counts too high michael@0: aNumber = std::min(aNumber,(rowCount-startRowIndex)); michael@0: michael@0: for (int32_t i = 0; i < aNumber; i++) michael@0: { michael@0: res = DeleteRow(table, startRowIndex); michael@0: // If failed in current row, try the next michael@0: if (NS_FAILED(res)) michael@0: startRowIndex++; michael@0: michael@0: // Check if there's a cell in the "next" row michael@0: res = GetCellAt(table, startRowIndex, startColIndex, getter_AddRefs(cell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if(!cell) michael@0: break; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Helper that doesn't batch or change the selection michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::DeleteRow(nsIDOMElement *aTable, int32_t aRowIndex) michael@0: { michael@0: NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); michael@0: michael@0: nsCOMPtr cell; michael@0: nsCOMPtr cellInDeleteRow; michael@0: int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: int32_t colIndex = 0; michael@0: nsresult res = NS_OK; michael@0: michael@0: // Prevent rules testing until we're done michael@0: nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); michael@0: michael@0: // The list of cells we will change rowspan in michael@0: // and the new rowspan values for each michael@0: nsTArray > spanCellList; michael@0: nsTArray newSpanList; michael@0: michael@0: int32_t rowCount, colCount; michael@0: res = GetTableSize(aTable, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Scan through cells in row to do rowspan adjustments michael@0: // Note that after we delete row, startRowIndex will point to the michael@0: // cells in the next row to be deleted michael@0: do { michael@0: if (aRowIndex >= rowCount || colIndex >= colCount) michael@0: break; michael@0: michael@0: res = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), michael@0: &startRowIndex, &startColIndex, &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: michael@0: // We don't fail if we don't find a cell, so this must be real bad michael@0: if(NS_FAILED(res)) return res; michael@0: michael@0: // Compensate for cells that don't start or extend below the row we are deleting michael@0: if (cell) michael@0: { michael@0: if (startRowIndex < aRowIndex) michael@0: { michael@0: // Cell starts in row above us michael@0: // Decrease its rowspan to keep table rectangular michael@0: // but we don't need to do this if rowspan=0, michael@0: // since it will automatically adjust michael@0: if (rowSpan > 0) michael@0: { michael@0: // Build list of cells to change rowspan michael@0: // We can't do it now since it upsets cell map, michael@0: // so we will do it after deleting the row michael@0: spanCellList.AppendElement(cell); michael@0: newSpanList.AppendElement(std::max((aRowIndex - startRowIndex), actualRowSpan-1)); michael@0: } michael@0: } michael@0: else michael@0: { michael@0: if (rowSpan > 1) michael@0: { michael@0: //Cell spans below row to delete, michael@0: // so we must insert new cells to keep rows below even michael@0: // Note that we test "rowSpan" so we don't do this if rowSpan = 0 (automatic readjustment) michael@0: res = SplitCellIntoRows(aTable, startRowIndex, startColIndex, michael@0: aRowIndex - startRowIndex + 1, // The row above the row to insert new cell into michael@0: actualRowSpan - 1, nullptr); // Span remaining below michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: if (!cellInDeleteRow) michael@0: cellInDeleteRow = cell; // Reference cell to find row to delete michael@0: } michael@0: // Skip over other columns spanned by this cell michael@0: colIndex += actualColSpan; michael@0: } michael@0: } while (cell); michael@0: michael@0: // Things are messed up if we didn't find a cell in the row! michael@0: NS_ENSURE_TRUE(cellInDeleteRow, NS_ERROR_FAILURE); michael@0: michael@0: // Delete the entire row michael@0: nsCOMPtr parentRow; michael@0: res = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cellInDeleteRow, getter_AddRefs(parentRow)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: if (parentRow) michael@0: { michael@0: res = DeleteNode(parentRow); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: michael@0: // Now we can set new rowspans for cells stored above michael@0: for (uint32_t i = 0, n = spanCellList.Length(); i < n; i++) michael@0: { michael@0: nsIDOMElement *cellPtr = spanCellList[i]; michael@0: if (cellPtr) michael@0: { michael@0: res = SetRowSpan(cellPtr, newSpanList[i]); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::SelectTable() michael@0: { michael@0: nsCOMPtr table; michael@0: nsresult res = NS_ERROR_FAILURE; michael@0: res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr, getter_AddRefs(table)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: // Don't fail if we didn't find a table michael@0: NS_ENSURE_TRUE(table, NS_OK); michael@0: michael@0: res = ClearSelection(); michael@0: if (NS_SUCCEEDED(res)) michael@0: res = AppendNodeToSelectionAsRange(table); michael@0: michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::SelectTableCell() michael@0: { michael@0: nsCOMPtr cell; michael@0: nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: michael@0: res = ClearSelection(); michael@0: if (NS_SUCCEEDED(res)) michael@0: res = AppendNodeToSelectionAsRange(cell); michael@0: michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::SelectBlockOfCells(nsIDOMElement *aStartCell, nsIDOMElement *aEndCell) michael@0: { michael@0: NS_ENSURE_TRUE(aStartCell && aEndCell, NS_ERROR_NULL_POINTER); michael@0: michael@0: nsCOMPtr selection; michael@0: nsresult res = GetSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); michael@0: michael@0: NS_NAMED_LITERAL_STRING(tableStr, "table"); michael@0: nsCOMPtr table; michael@0: res = GetElementOrParentByTagName(tableStr, aStartCell, getter_AddRefs(table)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr endTable; michael@0: res = GetElementOrParentByTagName(tableStr, aEndCell, getter_AddRefs(endTable)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(endTable, NS_ERROR_FAILURE); michael@0: michael@0: // We can only select a block if within the same table, michael@0: // so do nothing if not within one table michael@0: if (table != endTable) return NS_OK; michael@0: michael@0: int32_t startRowIndex, startColIndex, endRowIndex, endColIndex; michael@0: michael@0: // Get starting and ending cells' location in the cellmap michael@0: res = GetCellIndexes(aStartCell, &startRowIndex, &startColIndex); michael@0: if(NS_FAILED(res)) return res; michael@0: michael@0: res = GetCellIndexes(aEndCell, &endRowIndex, &endColIndex); michael@0: if(NS_FAILED(res)) return res; michael@0: michael@0: // Suppress nsISelectionListener notification michael@0: // until all selection changes are finished michael@0: nsSelectionBatcherForTable selectionBatcher(selection); michael@0: michael@0: // Examine all cell nodes in current selection and michael@0: // remove those outside the new block cell region michael@0: int32_t minColumn = std::min(startColIndex, endColIndex); michael@0: int32_t minRow = std::min(startRowIndex, endRowIndex); michael@0: int32_t maxColumn = std::max(startColIndex, endColIndex); michael@0: int32_t maxRow = std::max(startRowIndex, endRowIndex); michael@0: michael@0: nsCOMPtr cell; michael@0: int32_t currentRowIndex, currentColIndex; michael@0: nsCOMPtr range; michael@0: res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(cell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if (res == NS_EDITOR_ELEMENT_NOT_FOUND) return NS_OK; michael@0: michael@0: while (cell) michael@0: { michael@0: res = GetCellIndexes(cell, ¤tRowIndex, ¤tColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: if (currentRowIndex < maxRow || currentRowIndex > maxRow || michael@0: currentColIndex < maxColumn || currentColIndex > maxColumn) michael@0: { michael@0: selection->RemoveRange(range); michael@0: // Since we've removed the range, decrement pointer to next range michael@0: mSelectedCellIndex--; michael@0: } michael@0: res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: michael@0: int32_t rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: for (int32_t row = minRow; row <= maxRow; row++) michael@0: { michael@0: for(int32_t col = minColumn; col <= maxColumn; col += std::max(actualColSpan, 1)) michael@0: { michael@0: res = GetCellDataAt(table, row, col, getter_AddRefs(cell), michael@0: ¤tRowIndex, ¤tColIndex, michael@0: &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: if (NS_FAILED(res)) break; michael@0: // Skip cells that already selected or are spanned from previous locations michael@0: if (!isSelected && cell && row == currentRowIndex && col == currentColIndex) michael@0: { michael@0: res = AppendNodeToSelectionAsRange(cell); michael@0: if (NS_FAILED(res)) break; michael@0: } michael@0: } michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::SelectAllTableCells() michael@0: { michael@0: nsCOMPtr cell; michael@0: nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Don't fail if we didn't find a cell michael@0: NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: michael@0: nsCOMPtr startCell = cell; michael@0: michael@0: // Get parent table michael@0: nsCOMPtr table; michael@0: res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), cell, getter_AddRefs(table)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if(!table) return NS_ERROR_NULL_POINTER; michael@0: michael@0: int32_t rowCount, colCount; michael@0: res = GetTableSize(table, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: nsCOMPtr selection; michael@0: res = GetSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); michael@0: michael@0: // Suppress nsISelectionListener notification michael@0: // until all selection changes are finished michael@0: nsSelectionBatcherForTable selectionBatcher(selection); michael@0: michael@0: // It is now safe to clear the selection michael@0: // BE SURE TO RESET IT BEFORE LEAVING! michael@0: res = ClearSelection(); michael@0: michael@0: // Select all cells in the same column as current cell michael@0: bool cellSelected = false; michael@0: int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex; michael@0: bool isSelected; michael@0: for(int32_t row = 0; row < rowCount; row++) michael@0: { michael@0: for(int32_t col = 0; col < colCount; col += std::max(actualColSpan, 1)) michael@0: { michael@0: res = GetCellDataAt(table, row, col, getter_AddRefs(cell), michael@0: ¤tRowIndex, ¤tColIndex, michael@0: &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: if (NS_FAILED(res)) break; michael@0: // Skip cells that are spanned from previous rows or columns michael@0: if (cell && row == currentRowIndex && col == currentColIndex) michael@0: { michael@0: res = AppendNodeToSelectionAsRange(cell); michael@0: if (NS_FAILED(res)) break; michael@0: cellSelected = true; michael@0: } michael@0: } michael@0: } michael@0: // Safety code to select starting cell if nothing else was selected michael@0: if (!cellSelected) michael@0: { michael@0: return AppendNodeToSelectionAsRange(startCell); michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::SelectTableRow() michael@0: { michael@0: nsCOMPtr cell; michael@0: nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Don't fail if we didn't find a cell michael@0: NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: nsCOMPtr startCell = cell; michael@0: michael@0: // Get table and location of cell: michael@0: nsCOMPtr selection; michael@0: nsCOMPtr table; michael@0: int32_t startRowIndex, startColIndex; michael@0: michael@0: res = GetCellContext(getter_AddRefs(selection), michael@0: getter_AddRefs(table), michael@0: getter_AddRefs(cell), michael@0: nullptr, nullptr, michael@0: &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); michael@0: michael@0: int32_t rowCount, colCount; michael@0: res = GetTableSize(table, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: //Note: At this point, we could get first and last cells in row, michael@0: // then call SelectBlockOfCells, but that would take just michael@0: // a little less code, so the following is more efficient michael@0: michael@0: // Suppress nsISelectionListener notification michael@0: // until all selection changes are finished michael@0: nsSelectionBatcherForTable selectionBatcher(selection); michael@0: michael@0: // It is now safe to clear the selection michael@0: // BE SURE TO RESET IT BEFORE LEAVING! michael@0: res = ClearSelection(); michael@0: michael@0: // Select all cells in the same row as current cell michael@0: bool cellSelected = false; michael@0: int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex; michael@0: bool isSelected; michael@0: for(int32_t col = 0; col < colCount; col += std::max(actualColSpan, 1)) michael@0: { michael@0: res = GetCellDataAt(table, startRowIndex, col, getter_AddRefs(cell), michael@0: ¤tRowIndex, ¤tColIndex, &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: if (NS_FAILED(res)) break; michael@0: // Skip cells that are spanned from previous rows or columns michael@0: if (cell && currentRowIndex == startRowIndex && currentColIndex == col) michael@0: { michael@0: res = AppendNodeToSelectionAsRange(cell); michael@0: if (NS_FAILED(res)) break; michael@0: cellSelected = true; michael@0: } michael@0: } michael@0: // Safety code to select starting cell if nothing else was selected michael@0: if (!cellSelected) michael@0: { michael@0: return AppendNodeToSelectionAsRange(startCell); michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::SelectTableColumn() michael@0: { michael@0: nsCOMPtr cell; michael@0: nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Don't fail if we didn't find a cell michael@0: NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: michael@0: nsCOMPtr startCell = cell; michael@0: michael@0: // Get location of cell: michael@0: nsCOMPtr selection; michael@0: nsCOMPtr table; michael@0: int32_t startRowIndex, startColIndex; michael@0: michael@0: res = GetCellContext(getter_AddRefs(selection), michael@0: getter_AddRefs(table), michael@0: getter_AddRefs(cell), michael@0: nullptr, nullptr, michael@0: &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); michael@0: michael@0: int32_t rowCount, colCount; michael@0: res = GetTableSize(table, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Suppress nsISelectionListener notification michael@0: // until all selection changes are finished michael@0: nsSelectionBatcherForTable selectionBatcher(selection); michael@0: michael@0: // It is now safe to clear the selection michael@0: // BE SURE TO RESET IT BEFORE LEAVING! michael@0: res = ClearSelection(); michael@0: michael@0: // Select all cells in the same column as current cell michael@0: bool cellSelected = false; michael@0: int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex; michael@0: bool isSelected; michael@0: for(int32_t row = 0; row < rowCount; row += std::max(actualRowSpan, 1)) michael@0: { michael@0: res = GetCellDataAt(table, row, startColIndex, getter_AddRefs(cell), michael@0: ¤tRowIndex, ¤tColIndex, &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: if (NS_FAILED(res)) break; michael@0: // Skip cells that are spanned from previous rows or columns michael@0: if (cell && currentRowIndex == row && currentColIndex == startColIndex) michael@0: { michael@0: res = AppendNodeToSelectionAsRange(cell); michael@0: if (NS_FAILED(res)) break; michael@0: cellSelected = true; michael@0: } michael@0: } michael@0: // Safety code to select starting cell if nothing else was selected michael@0: if (!cellSelected) michael@0: { michael@0: return AppendNodeToSelectionAsRange(startCell); michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::SplitTableCell() michael@0: { michael@0: nsCOMPtr table; michael@0: nsCOMPtr cell; michael@0: int32_t startRowIndex, startColIndex, actualRowSpan, actualColSpan; michael@0: // Get cell, table, etc. at selection anchor node michael@0: nsresult res = GetCellContext(nullptr, michael@0: getter_AddRefs(table), michael@0: getter_AddRefs(cell), michael@0: nullptr, nullptr, michael@0: &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if(!table || !cell) return NS_EDITOR_ELEMENT_NOT_FOUND; michael@0: michael@0: // We need rowspan and colspan data michael@0: res = GetCellSpansAt(table, startRowIndex, startColIndex, actualRowSpan, actualColSpan); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Must have some span to split michael@0: if (actualRowSpan <= 1 && actualColSpan <= 1) michael@0: return NS_OK; michael@0: michael@0: nsAutoEditBatch beginBatching(this); michael@0: // Prevent auto insertion of BR in new cell until we're done michael@0: nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); michael@0: michael@0: // We reset selection michael@0: nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); michael@0: //...so suppress Rules System selection munging michael@0: nsAutoTxnsConserveSelection dontChangeSelection(this); michael@0: michael@0: nsCOMPtr newCell; michael@0: int32_t rowIndex = startRowIndex; michael@0: int32_t rowSpanBelow, colSpanAfter; michael@0: michael@0: // Split up cell row-wise first into rowspan=1 above, and the rest below, michael@0: // whittling away at the cell below until no more extra span michael@0: for (rowSpanBelow = actualRowSpan-1; rowSpanBelow >= 0; rowSpanBelow--) michael@0: { michael@0: // We really split row-wise only if we had rowspan > 1 michael@0: if (rowSpanBelow > 0) michael@0: { michael@0: res = SplitCellIntoRows(table, rowIndex, startColIndex, 1, rowSpanBelow, getter_AddRefs(newCell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: CopyCellBackgroundColor(newCell, cell); michael@0: } michael@0: int32_t colIndex = startColIndex; michael@0: // Now split the cell with rowspan = 1 into cells if it has colSpan > 1 michael@0: for (colSpanAfter = actualColSpan-1; colSpanAfter > 0; colSpanAfter--) michael@0: { michael@0: res = SplitCellIntoColumns(table, rowIndex, colIndex, 1, colSpanAfter, getter_AddRefs(newCell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: CopyCellBackgroundColor(newCell, cell); michael@0: colIndex++; michael@0: } michael@0: // Point to the new cell and repeat michael@0: rowIndex++; michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: nsresult michael@0: nsHTMLEditor::CopyCellBackgroundColor(nsIDOMElement *destCell, nsIDOMElement *sourceCell) michael@0: { michael@0: NS_ENSURE_TRUE(destCell && sourceCell, NS_ERROR_NULL_POINTER); michael@0: michael@0: // Copy backgournd color to new cell michael@0: NS_NAMED_LITERAL_STRING(bgcolor, "bgcolor"); michael@0: nsAutoString color; michael@0: bool isSet; michael@0: nsresult res = GetAttributeValue(sourceCell, bgcolor, color, &isSet); michael@0: michael@0: if (NS_SUCCEEDED(res) && isSet) michael@0: res = SetAttribute(destCell, bgcolor, color); michael@0: michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::SplitCellIntoColumns(nsIDOMElement *aTable, int32_t aRowIndex, int32_t aColIndex, michael@0: int32_t aColSpanLeft, int32_t aColSpanRight, michael@0: nsIDOMElement **aNewCell) michael@0: { michael@0: NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); michael@0: if (aNewCell) *aNewCell = nullptr; michael@0: michael@0: nsCOMPtr cell; michael@0: int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: nsresult res = GetCellDataAt(aTable, aRowIndex, aColIndex, getter_AddRefs(cell), michael@0: &startRowIndex, &startColIndex, michael@0: &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(cell, NS_ERROR_NULL_POINTER); michael@0: michael@0: // We can't split! michael@0: if (actualColSpan <= 1 || (aColSpanLeft + aColSpanRight) > actualColSpan) michael@0: return NS_OK; michael@0: michael@0: // Reduce colspan of cell to split michael@0: res = SetColSpan(cell, aColSpanLeft); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Insert new cell after using the remaining span michael@0: // and always get the new cell so we can copy the background color; michael@0: nsCOMPtr newCell; michael@0: res = InsertCell(cell, actualRowSpan, aColSpanRight, true, false, getter_AddRefs(newCell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if (newCell) michael@0: { michael@0: if (aNewCell) michael@0: { michael@0: *aNewCell = newCell.get(); michael@0: NS_ADDREF(*aNewCell); michael@0: } michael@0: res = CopyCellBackgroundColor(newCell, cell); michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::SplitCellIntoRows(nsIDOMElement *aTable, int32_t aRowIndex, int32_t aColIndex, michael@0: int32_t aRowSpanAbove, int32_t aRowSpanBelow, michael@0: nsIDOMElement **aNewCell) michael@0: { michael@0: NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); michael@0: if (aNewCell) *aNewCell = nullptr; michael@0: michael@0: nsCOMPtr cell; michael@0: int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: nsresult res = GetCellDataAt(aTable, aRowIndex, aColIndex, getter_AddRefs(cell), michael@0: &startRowIndex, &startColIndex, michael@0: &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(cell, NS_ERROR_NULL_POINTER); michael@0: michael@0: // We can't split! michael@0: if (actualRowSpan <= 1 || (aRowSpanAbove + aRowSpanBelow) > actualRowSpan) michael@0: return NS_OK; michael@0: michael@0: int32_t rowCount, colCount; michael@0: res = GetTableSize(aTable, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: nsCOMPtr cell2; michael@0: nsCOMPtr lastCellFound; michael@0: int32_t startRowIndex2, startColIndex2, rowSpan2, colSpan2, actualRowSpan2, actualColSpan2; michael@0: bool isSelected2; michael@0: int32_t colIndex = 0; michael@0: bool insertAfter = (startColIndex > 0); michael@0: // This is the row we will insert new cell into michael@0: int32_t rowBelowIndex = startRowIndex+aRowSpanAbove; michael@0: michael@0: // Find a cell to insert before or after michael@0: do michael@0: { michael@0: // Search for a cell to insert before michael@0: res = GetCellDataAt(aTable, rowBelowIndex, michael@0: colIndex, getter_AddRefs(cell2), michael@0: &startRowIndex2, &startColIndex2, &rowSpan2, &colSpan2, michael@0: &actualRowSpan2, &actualColSpan2, &isSelected2); michael@0: // If we fail here, it could be because row has bad rowspan values, michael@0: // such as all cells having rowspan > 1 (Call FixRowSpan first!) michael@0: if (NS_FAILED(res) || !cell) return NS_ERROR_FAILURE; michael@0: michael@0: // Skip over cells spanned from above (like the one we are splitting!) michael@0: if (cell2 && startRowIndex2 == rowBelowIndex) michael@0: { michael@0: if (insertAfter) michael@0: { michael@0: // New cell isn't first in row, michael@0: // so stop after we find the cell just before new cell's column michael@0: if ((startColIndex2 + actualColSpan2) == startColIndex) michael@0: break; michael@0: michael@0: // If cell found is AFTER desired new cell colum, michael@0: // we have multiple cells with rowspan > 1 that michael@0: // prevented us from finding a cell to insert after... michael@0: if (startColIndex2 > startColIndex) michael@0: { michael@0: // ... so instead insert before the cell we found michael@0: insertAfter = false; michael@0: break; michael@0: } michael@0: } michael@0: else michael@0: { michael@0: break; // Inserting before, so stop at first cell in row we want to insert into michael@0: } michael@0: lastCellFound = cell2; michael@0: } michael@0: // Skip to next available cellmap location michael@0: colIndex += std::max(actualColSpan2, 1); michael@0: michael@0: // Done when past end of total number of columns michael@0: if (colIndex > colCount) michael@0: break; michael@0: michael@0: } while(true); michael@0: michael@0: if (!cell2 && lastCellFound) michael@0: { michael@0: // Edge case where we didn't find a cell to insert after michael@0: // or before because column(s) before desired column michael@0: // and all columns after it are spanned from above. michael@0: // We can insert after the last cell we found michael@0: cell2 = lastCellFound; michael@0: insertAfter = true; // Should always be true, but let's be sure michael@0: } michael@0: michael@0: // Reduce rowspan of cell to split michael@0: res = SetRowSpan(cell, aRowSpanAbove); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: michael@0: // Insert new cell after using the remaining span michael@0: // and always get the new cell so we can copy the background color; michael@0: nsCOMPtr newCell; michael@0: res = InsertCell(cell2, aRowSpanBelow, actualColSpan, insertAfter, false, getter_AddRefs(newCell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if (newCell) michael@0: { michael@0: if (aNewCell) michael@0: { michael@0: *aNewCell = newCell.get(); michael@0: NS_ADDREF(*aNewCell); michael@0: } michael@0: res = CopyCellBackgroundColor(newCell, cell2); michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::SwitchTableCellHeaderType(nsIDOMElement *aSourceCell, nsIDOMElement **aNewCell) michael@0: { michael@0: NS_ENSURE_TRUE(aSourceCell, NS_ERROR_NULL_POINTER); michael@0: michael@0: nsAutoEditBatch beginBatching(this); michael@0: // Prevent auto insertion of BR in new cell created by ReplaceContainer michael@0: nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); michael@0: michael@0: nsCOMPtr newNode; michael@0: michael@0: // Save current selection to restore when done michael@0: // This is needed so ReplaceContainer can monitor selection michael@0: // when replacing nodes michael@0: nsRefPtr selection = GetSelection(); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); michael@0: nsAutoSelectionReset selectionResetter(selection, this); michael@0: michael@0: // Set to the opposite of current type michael@0: nsCOMPtr atom = nsEditor::GetTag(aSourceCell); michael@0: nsString newCellType( (atom == nsEditProperty::td) ? NS_LITERAL_STRING("th") : NS_LITERAL_STRING("td")); michael@0: michael@0: // This creates new node, moves children, copies attributes (true) michael@0: // and manages the selection! michael@0: nsresult res = ReplaceContainer(aSourceCell, address_of(newNode), michael@0: newCellType, nullptr, nullptr, true); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(newNode, NS_ERROR_FAILURE); michael@0: michael@0: // Return the new cell michael@0: if (aNewCell) michael@0: { michael@0: nsCOMPtr newElement = do_QueryInterface(newNode); michael@0: *aNewCell = newElement.get(); michael@0: NS_ADDREF(*aNewCell); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::JoinTableCells(bool aMergeNonContiguousContents) michael@0: { michael@0: nsCOMPtr table; michael@0: nsCOMPtr targetCell; michael@0: int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: nsCOMPtr cell2; michael@0: int32_t startRowIndex2, startColIndex2, rowSpan2, colSpan2, actualRowSpan2, actualColSpan2; michael@0: bool isSelected2; michael@0: michael@0: // Get cell, table, etc. at selection anchor node michael@0: nsresult res = GetCellContext(nullptr, michael@0: getter_AddRefs(table), michael@0: getter_AddRefs(targetCell), michael@0: nullptr, nullptr, michael@0: &startRowIndex, &startColIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if(!table || !targetCell) return NS_EDITOR_ELEMENT_NOT_FOUND; michael@0: michael@0: nsAutoEditBatch beginBatching(this); michael@0: //Don't let Rules System change the selection michael@0: nsAutoTxnsConserveSelection dontChangeSelection(this); michael@0: michael@0: // Note: We dont' use nsSetSelectionAfterTableEdit here so the selection michael@0: // is retained after joining. This leaves the target cell selected michael@0: // as well as the "non-contiguous" cells, so user can see what happened. michael@0: michael@0: nsCOMPtr firstCell; michael@0: int32_t firstRowIndex, firstColIndex; michael@0: res = GetFirstSelectedCellInTable(&firstRowIndex, &firstColIndex, getter_AddRefs(firstCell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: bool joinSelectedCells = false; michael@0: if (firstCell) michael@0: { michael@0: nsCOMPtr secondCell; michael@0: res = GetNextSelectedCell(nullptr, getter_AddRefs(secondCell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // If only one cell is selected, join with cell to the right michael@0: joinSelectedCells = (secondCell != nullptr); michael@0: } michael@0: michael@0: if (joinSelectedCells) michael@0: { michael@0: // We have selected cells: Join just contiguous cells michael@0: // and just merge contents if not contiguous michael@0: michael@0: int32_t rowCount, colCount; michael@0: res = GetTableSize(table, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Get spans for cell we will merge into michael@0: int32_t firstRowSpan, firstColSpan; michael@0: res = GetCellSpansAt( table, firstRowIndex, firstColIndex, firstRowSpan, firstColSpan); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // This defines the last indexes along the "edges" michael@0: // of the contiguous block of cells, telling us michael@0: // that we can join adjacent cells to the block michael@0: // Start with same as the first values, michael@0: // then expand as we find adjacent selected cells michael@0: int32_t lastRowIndex = firstRowIndex; michael@0: int32_t lastColIndex = firstColIndex; michael@0: int32_t rowIndex, colIndex; michael@0: michael@0: // First pass: Determine boundaries of contiguous rectangular block michael@0: // that we will join into one cell, michael@0: // favoring adjacent cells in the same row michael@0: for (rowIndex = firstRowIndex; rowIndex <= lastRowIndex; rowIndex++) michael@0: { michael@0: int32_t currentRowCount = rowCount; michael@0: // Be sure each row doesn't have rowspan errors michael@0: res = FixBadRowSpan(table, rowIndex, rowCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: // Adjust rowcount by number of rows we removed michael@0: lastRowIndex -= (currentRowCount-rowCount); michael@0: michael@0: bool cellFoundInRow = false; michael@0: bool lastRowIsSet = false; michael@0: int32_t lastColInRow = 0; michael@0: int32_t firstColInRow = firstColIndex; michael@0: for (colIndex = firstColIndex; colIndex < colCount; colIndex += std::max(actualColSpan2, 1)) michael@0: { michael@0: res = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell2), michael@0: &startRowIndex2, &startColIndex2, michael@0: &rowSpan2, &colSpan2, michael@0: &actualRowSpan2, &actualColSpan2, &isSelected2); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: if (isSelected2) michael@0: { michael@0: if (!cellFoundInRow) michael@0: // We've just found the first selected cell in this row michael@0: firstColInRow = colIndex; michael@0: michael@0: if (rowIndex > firstRowIndex && firstColInRow != firstColIndex) michael@0: { michael@0: // We're in at least the second row, michael@0: // but left boundary is "ragged" (not the same as 1st row's start) michael@0: //Let's just end block on previous row michael@0: // and keep previous lastColIndex michael@0: //TODO: We could try to find the Maximum firstColInRow michael@0: // so our block can still extend down more rows? michael@0: lastRowIndex = std::max(0,rowIndex - 1); michael@0: lastRowIsSet = true; michael@0: break; michael@0: } michael@0: // Save max selected column in this row, including extra colspan michael@0: lastColInRow = colIndex + (actualColSpan2-1); michael@0: cellFoundInRow = true; michael@0: } michael@0: else if (cellFoundInRow) michael@0: { michael@0: // No cell or not selected, but at least one cell in row was found michael@0: michael@0: if (rowIndex > (firstRowIndex+1) && colIndex <= lastColIndex) michael@0: { michael@0: // Cell is in a column less than current right border in michael@0: // the third or higher selected row, so stop block at the previous row michael@0: lastRowIndex = std::max(0,rowIndex - 1); michael@0: lastRowIsSet = true; michael@0: } michael@0: // We're done with this row michael@0: break; michael@0: } michael@0: } // End of column loop michael@0: michael@0: // Done with this row michael@0: if (cellFoundInRow) michael@0: { michael@0: if (rowIndex == firstRowIndex) michael@0: { michael@0: // First row always initializes the right boundary michael@0: lastColIndex = lastColInRow; michael@0: } michael@0: michael@0: // If we didn't determine last row above... michael@0: if (!lastRowIsSet) michael@0: { michael@0: if (colIndex < lastColIndex) michael@0: { michael@0: // (don't think we ever get here?) michael@0: // Cell is in a column less than current right boundary, michael@0: // so stop block at the previous row michael@0: lastRowIndex = std::max(0,rowIndex - 1); michael@0: } michael@0: else michael@0: { michael@0: // Go on to examine next row michael@0: lastRowIndex = rowIndex+1; michael@0: } michael@0: } michael@0: // Use the minimum col we found so far for right boundary michael@0: lastColIndex = std::min(lastColIndex, lastColInRow); michael@0: } michael@0: else michael@0: { michael@0: // No selected cells in this row -- stop at row above michael@0: // and leave last column at its previous value michael@0: lastRowIndex = std::max(0,rowIndex - 1); michael@0: } michael@0: } michael@0: michael@0: // The list of cells we will delete after joining michael@0: nsTArray > deleteList; michael@0: michael@0: // 2nd pass: Do the joining and merging michael@0: for (rowIndex = 0; rowIndex < rowCount; rowIndex++) michael@0: { michael@0: for (colIndex = 0; colIndex < colCount; colIndex += std::max(actualColSpan2, 1)) michael@0: { michael@0: res = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell2), michael@0: &startRowIndex2, &startColIndex2, michael@0: &rowSpan2, &colSpan2, michael@0: &actualRowSpan2, &actualColSpan2, &isSelected2); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // If this is 0, we are past last cell in row, so exit the loop michael@0: if (actualColSpan2 == 0) michael@0: break; michael@0: michael@0: // Merge only selected cells (skip cell we're merging into, of course) michael@0: if (isSelected2 && cell2 != firstCell) michael@0: { michael@0: if (rowIndex >= firstRowIndex && rowIndex <= lastRowIndex && michael@0: colIndex >= firstColIndex && colIndex <= lastColIndex) michael@0: { michael@0: // We are within the join region michael@0: // Problem: It is very tricky to delete cells as we merge, michael@0: // since that will upset the cellmap michael@0: // Instead, build a list of cells to delete and do it later michael@0: NS_ASSERTION(startRowIndex2 == rowIndex, "JoinTableCells: StartRowIndex is in row above"); michael@0: michael@0: if (actualColSpan2 > 1) michael@0: { michael@0: //Check if cell "hangs" off the boundary because of colspan > 1 michael@0: // Use split methods to chop off excess michael@0: int32_t extraColSpan = (startColIndex2 + actualColSpan2) - (lastColIndex+1); michael@0: if ( extraColSpan > 0) michael@0: { michael@0: res = SplitCellIntoColumns(table, startRowIndex2, startColIndex2, michael@0: actualColSpan2-extraColSpan, extraColSpan, nullptr); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: } michael@0: michael@0: res = MergeCells(firstCell, cell2, false); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Add cell to list to delete michael@0: deleteList.AppendElement(cell2.get()); michael@0: } michael@0: else if (aMergeNonContiguousContents) michael@0: { michael@0: // Cell is outside join region -- just merge the contents michael@0: res = MergeCells(firstCell, cell2, false); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // All cell contents are merged. Delete the empty cells we accumulated michael@0: // Prevent rules testing until we're done michael@0: nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); michael@0: michael@0: for (uint32_t i = 0, n = deleteList.Length(); i < n; i++) michael@0: { michael@0: nsIDOMElement *elementPtr = deleteList[i]; michael@0: if (elementPtr) michael@0: { michael@0: nsCOMPtr node = do_QueryInterface(elementPtr); michael@0: res = DeleteNode(node); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: } michael@0: // Cleanup selection: remove ranges where cells were deleted michael@0: nsCOMPtr selection; michael@0: res = GetSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); michael@0: michael@0: int32_t rangeCount; michael@0: res = selection->GetRangeCount(&rangeCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: nsCOMPtr range; michael@0: int32_t i; michael@0: for (i = 0; i < rangeCount; i++) michael@0: { michael@0: res = selection->GetRangeAt(i, getter_AddRefs(range)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr deletedCell; michael@0: res = GetCellFromRange(range, getter_AddRefs(deletedCell)); michael@0: if (!deletedCell) michael@0: { michael@0: selection->RemoveRange(range); michael@0: rangeCount--; michael@0: i--; michael@0: } michael@0: } michael@0: michael@0: // Set spans for the cell everthing merged into michael@0: res = SetRowSpan(firstCell, lastRowIndex-firstRowIndex+1); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: res = SetColSpan(firstCell, lastColIndex-firstColIndex+1); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: michael@0: // Fixup disturbances in table layout michael@0: NormalizeTable(table); michael@0: } michael@0: else michael@0: { michael@0: // Joining with cell to the right -- get rowspan and colspan data of target cell michael@0: res = GetCellDataAt(table, startRowIndex, startColIndex, getter_AddRefs(targetCell), michael@0: &startRowIndex, &startColIndex, &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(targetCell, NS_ERROR_NULL_POINTER); michael@0: michael@0: // Get data for cell to the right michael@0: res = GetCellDataAt(table, startRowIndex, startColIndex+actualColSpan, getter_AddRefs(cell2), michael@0: &startRowIndex2, &startColIndex2, &rowSpan2, &colSpan2, michael@0: &actualRowSpan2, &actualColSpan2, &isSelected2); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if(!cell2) return NS_OK; // Don't fail if there's no cell michael@0: michael@0: // sanity check michael@0: NS_ASSERTION((startRowIndex >= startRowIndex2),"JoinCells: startRowIndex < startRowIndex2"); michael@0: michael@0: // Figure out span of merged cell starting from target's starting row michael@0: // to handle case of merged cell starting in a row above michael@0: int32_t spanAboveMergedCell = startRowIndex - startRowIndex2; michael@0: int32_t effectiveRowSpan2 = actualRowSpan2 - spanAboveMergedCell; michael@0: michael@0: if (effectiveRowSpan2 > actualRowSpan) michael@0: { michael@0: // Cell to the right spans into row below target michael@0: // Split off portion below target cell's bottom-most row michael@0: res = SplitCellIntoRows(table, startRowIndex2, startColIndex2, michael@0: spanAboveMergedCell+actualRowSpan, michael@0: effectiveRowSpan2-actualRowSpan, nullptr); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: michael@0: // Move contents from cell to the right michael@0: // Delete the cell now only if it starts in the same row michael@0: // and has enough row "height" michael@0: res = MergeCells(targetCell, cell2, michael@0: (startRowIndex2 == startRowIndex) && michael@0: (effectiveRowSpan2 >= actualRowSpan)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: if (effectiveRowSpan2 < actualRowSpan) michael@0: { michael@0: // Merged cell is "shorter" michael@0: // (there are cells(s) below it that are row-spanned by target cell) michael@0: // We could try splitting those cells, but that's REAL messy, michael@0: // so the safest thing to do is NOT really join the cells michael@0: return NS_OK; michael@0: } michael@0: michael@0: if( spanAboveMergedCell > 0 ) michael@0: { michael@0: // Cell we merged started in a row above the target cell michael@0: // Reduce rowspan to give room where target cell will extend its colspan michael@0: res = SetRowSpan(cell2, spanAboveMergedCell); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: michael@0: // Reset target cell's colspan to encompass cell to the right michael@0: res = SetColSpan(targetCell, actualColSpan+actualColSpan2); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::MergeCells(nsCOMPtr aTargetCell, michael@0: nsCOMPtr aCellToMerge, michael@0: bool aDeleteCellToMerge) michael@0: { michael@0: nsCOMPtr targetCell = do_QueryInterface(aTargetCell); michael@0: nsCOMPtr cellToMerge = do_QueryInterface(aCellToMerge); michael@0: NS_ENSURE_TRUE(targetCell && cellToMerge, NS_ERROR_NULL_POINTER); michael@0: michael@0: // Prevent rules testing until we're done michael@0: nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); michael@0: michael@0: // Don't need to merge if cell is empty michael@0: if (!IsEmptyCell(cellToMerge)) { michael@0: // Get index of last child in target cell michael@0: // If we fail or don't have children, michael@0: // we insert at index 0 michael@0: int32_t insertIndex = 0; michael@0: michael@0: // Start inserting just after last child michael@0: uint32_t len = targetCell->GetChildCount(); michael@0: if (len == 1 && IsEmptyCell(targetCell)) { michael@0: // Delete the empty node michael@0: nsIContent* cellChild = targetCell->GetFirstChild(); michael@0: nsresult res = DeleteNode(cellChild->AsDOMNode()); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: insertIndex = 0; michael@0: } else { michael@0: insertIndex = (int32_t)len; michael@0: } michael@0: michael@0: // Move the contents michael@0: while (cellToMerge->HasChildren()) { michael@0: nsCOMPtr cellChild = cellToMerge->GetLastChild()->AsDOMNode(); michael@0: nsresult res = DeleteNode(cellChild); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: res = InsertNode(cellChild, aTargetCell, insertIndex); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: } michael@0: michael@0: // Delete cells whose contents were moved michael@0: if (aDeleteCellToMerge) michael@0: return DeleteNode(aCellToMerge); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::FixBadRowSpan(nsIDOMElement *aTable, int32_t aRowIndex, int32_t& aNewRowCount) michael@0: { michael@0: NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); michael@0: michael@0: int32_t rowCount, colCount; michael@0: nsresult res = GetTableSize(aTable, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: nsCOMPtrcell; michael@0: int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: michael@0: int32_t minRowSpan = -1; michael@0: int32_t colIndex; michael@0: michael@0: for( colIndex = 0; colIndex < colCount; colIndex += std::max(actualColSpan, 1)) michael@0: { michael@0: res = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), michael@0: &startRowIndex, &startColIndex, &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: // NOTE: This is a *real* failure. michael@0: // GetCellDataAt passes if cell is missing from cellmap michael@0: if(NS_FAILED(res)) return res; michael@0: if (!cell) break; michael@0: if(rowSpan > 0 && michael@0: startRowIndex == aRowIndex && michael@0: (rowSpan < minRowSpan || minRowSpan == -1)) michael@0: { michael@0: minRowSpan = rowSpan; michael@0: } michael@0: NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in FixBadRowSpan"); michael@0: } michael@0: if(minRowSpan > 1) michael@0: { michael@0: // The amount to reduce everyone's rowspan michael@0: // so at least one cell has rowspan = 1 michael@0: int32_t rowsReduced = minRowSpan - 1; michael@0: for(colIndex = 0; colIndex < colCount; colIndex += std::max(actualColSpan, 1)) michael@0: { michael@0: res = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), michael@0: &startRowIndex, &startColIndex, &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: if(NS_FAILED(res)) return res; michael@0: // Fixup rowspans only for cells starting in current row michael@0: if(cell && rowSpan > 0 && michael@0: startRowIndex == aRowIndex && michael@0: startColIndex == colIndex ) michael@0: { michael@0: res = SetRowSpan(cell, rowSpan-rowsReduced); michael@0: if(NS_FAILED(res)) return res; michael@0: } michael@0: NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in FixBadRowSpan"); michael@0: } michael@0: } michael@0: return GetTableSize(aTable, &aNewRowCount, &colCount); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::FixBadColSpan(nsIDOMElement *aTable, int32_t aColIndex, int32_t& aNewColCount) michael@0: { michael@0: NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); michael@0: michael@0: int32_t rowCount, colCount; michael@0: nsresult res = GetTableSize(aTable, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: nsCOMPtr cell; michael@0: int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: michael@0: int32_t minColSpan = -1; michael@0: int32_t rowIndex; michael@0: michael@0: for( rowIndex = 0; rowIndex < rowCount; rowIndex += std::max(actualRowSpan, 1)) michael@0: { michael@0: res = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell), michael@0: &startRowIndex, &startColIndex, &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: // NOTE: This is a *real* failure. michael@0: // GetCellDataAt passes if cell is missing from cellmap michael@0: if(NS_FAILED(res)) return res; michael@0: if (!cell) break; michael@0: if(colSpan > 0 && michael@0: startColIndex == aColIndex && michael@0: (colSpan < minColSpan || minColSpan == -1)) michael@0: { michael@0: minColSpan = colSpan; michael@0: } michael@0: NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in FixBadColSpan"); michael@0: } michael@0: if(minColSpan > 1) michael@0: { michael@0: // The amount to reduce everyone's colspan michael@0: // so at least one cell has colspan = 1 michael@0: int32_t colsReduced = minColSpan - 1; michael@0: for(rowIndex = 0; rowIndex < rowCount; rowIndex += std::max(actualRowSpan, 1)) michael@0: { michael@0: res = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell), michael@0: &startRowIndex, &startColIndex, &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: if(NS_FAILED(res)) return res; michael@0: // Fixup colspans only for cells starting in current column michael@0: if(cell && colSpan > 0 && michael@0: startColIndex == aColIndex && michael@0: startRowIndex == rowIndex ) michael@0: { michael@0: res = SetColSpan(cell, colSpan-colsReduced); michael@0: if(NS_FAILED(res)) return res; michael@0: } michael@0: NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in FixBadColSpan"); michael@0: } michael@0: } michael@0: return GetTableSize(aTable, &rowCount, &aNewColCount); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::NormalizeTable(nsIDOMElement *aTable) michael@0: { michael@0: nsRefPtr selection = GetSelection(); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr table; michael@0: nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), michael@0: aTable, getter_AddRefs(table)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: // Don't fail if we didn't find a table michael@0: NS_ENSURE_TRUE(table, NS_OK); michael@0: michael@0: int32_t rowCount, colCount, rowIndex, colIndex; michael@0: res = GetTableSize(table, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Save current selection michael@0: nsAutoSelectionReset selectionResetter(selection, this); michael@0: michael@0: nsAutoEditBatch beginBatching(this); michael@0: // Prevent auto insertion of BR in new cell until we're done michael@0: nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); michael@0: michael@0: nsCOMPtr cell; michael@0: int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: michael@0: // Scan all cells in each row to detect bad rowspan values michael@0: for(rowIndex = 0; rowIndex < rowCount; rowIndex++) michael@0: { michael@0: res = FixBadRowSpan(table, rowIndex, rowCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: // and same for colspans michael@0: for(colIndex = 0; colIndex < colCount; colIndex++) michael@0: { michael@0: res = FixBadColSpan(table, colIndex, colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: michael@0: // Fill in missing cellmap locations with empty cells michael@0: for(rowIndex = 0; rowIndex < rowCount; rowIndex++) michael@0: { michael@0: nsCOMPtr previousCellInRow; michael@0: michael@0: for(colIndex = 0; colIndex < colCount; colIndex++) michael@0: { michael@0: res = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell), michael@0: &startRowIndex, &startColIndex, &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: // NOTE: This is a *real* failure. michael@0: // GetCellDataAt passes if cell is missing from cellmap michael@0: if(NS_FAILED(res)) return res; michael@0: if (!cell) michael@0: { michael@0: //We are missing a cell at a cellmap location michael@0: #ifdef DEBUG michael@0: printf("NormalizeTable found missing cell at row=%d, col=%d\n", rowIndex, colIndex); michael@0: #endif michael@0: // Add a cell after the previous Cell in the current row michael@0: if(previousCellInRow) michael@0: { michael@0: // Insert a new cell after (true), and return the new cell to us michael@0: res = InsertCell(previousCellInRow, 1, 1, true, false, getter_AddRefs(cell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Set this so we use returned new "cell" to set previousCellInRow below michael@0: if(cell) michael@0: startRowIndex = rowIndex; michael@0: } else { michael@0: // We don't have any cells in this row -- We are really messed up! michael@0: #ifdef DEBUG michael@0: printf("NormalizeTable found no cells in row=%d, col=%d\n", rowIndex, colIndex); michael@0: #endif michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: // Save the last cell found in the same row we are scanning michael@0: if(startRowIndex == rowIndex) michael@0: { michael@0: previousCellInRow = cell; michael@0: } michael@0: } michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetCellIndexes(nsIDOMElement *aCell, michael@0: int32_t *aRowIndex, int32_t *aColIndex) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aRowIndex); michael@0: *aColIndex=0; // initialize out params michael@0: NS_ENSURE_ARG_POINTER(aColIndex); michael@0: *aRowIndex=0; michael@0: nsresult res=NS_ERROR_NOT_INITIALIZED; michael@0: if (!aCell) michael@0: { michael@0: // Get the selected cell or the cell enclosing the selection anchor michael@0: nsCOMPtr cell; michael@0: res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); michael@0: if (NS_SUCCEEDED(res) && cell) michael@0: aCell = cell; michael@0: else michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED); michael@0: nsCOMPtr ps = GetPresShell(); michael@0: NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: nsCOMPtr nodeAsContent( do_QueryInterface(aCell) ); michael@0: NS_ENSURE_TRUE(nodeAsContent, NS_ERROR_FAILURE); michael@0: // frames are not ref counted, so don't use an nsCOMPtr michael@0: nsIFrame *layoutObject = nodeAsContent->GetPrimaryFrame(); michael@0: NS_ENSURE_TRUE(layoutObject, NS_ERROR_FAILURE); michael@0: michael@0: nsITableCellLayout *cellLayoutObject = do_QueryFrame(layoutObject); michael@0: NS_ENSURE_TRUE(cellLayoutObject, NS_ERROR_FAILURE); michael@0: return cellLayoutObject->GetCellIndexes(*aRowIndex, *aColIndex); michael@0: } michael@0: michael@0: nsTableOuterFrame* michael@0: nsHTMLEditor::GetTableFrame(nsIDOMElement* aTable) michael@0: { michael@0: NS_ENSURE_TRUE(aTable, nullptr); michael@0: michael@0: nsCOMPtr nodeAsContent( do_QueryInterface(aTable) ); michael@0: NS_ENSURE_TRUE(nodeAsContent, nullptr); michael@0: return do_QueryFrame(nodeAsContent->GetPrimaryFrame()); michael@0: } michael@0: michael@0: //Return actual number of cells (a cell with colspan > 1 counts as just 1) michael@0: int32_t nsHTMLEditor::GetNumberOfCellsInRow(nsIDOMElement* aTable, int32_t rowIndex) michael@0: { michael@0: int32_t cellCount = 0; michael@0: nsCOMPtr cell; michael@0: int32_t colIndex = 0; michael@0: nsresult res; michael@0: do { michael@0: int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: res = GetCellDataAt(aTable, rowIndex, colIndex, getter_AddRefs(cell), michael@0: &startRowIndex, &startColIndex, &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: NS_ENSURE_SUCCESS(res, 0); michael@0: if (cell) michael@0: { michael@0: // Only count cells that start in row we are working with michael@0: if (startRowIndex == rowIndex) michael@0: cellCount++; michael@0: michael@0: //Next possible location for a cell michael@0: colIndex += actualColSpan; michael@0: } michael@0: else michael@0: colIndex++; michael@0: michael@0: } while (cell); michael@0: michael@0: return cellCount; michael@0: } michael@0: michael@0: /* Not scriptable: For convenience in C++ michael@0: Use GetTableRowCount and GetTableColumnCount from JavaScript michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetTableSize(nsIDOMElement *aTable, michael@0: int32_t* aRowCount, int32_t* aColCount) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aRowCount); michael@0: NS_ENSURE_ARG_POINTER(aColCount); michael@0: nsresult res; michael@0: *aRowCount = 0; michael@0: *aColCount = 0; michael@0: nsCOMPtr table; michael@0: // Get the selected talbe or the table enclosing the selection anchor michael@0: res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aTable, getter_AddRefs(table)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); michael@0: michael@0: nsTableOuterFrame* tableFrame = GetTableFrame(table.get()); michael@0: NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE); michael@0: michael@0: *aRowCount = tableFrame->GetRowCount(); michael@0: *aColCount = tableFrame->GetColCount(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetCellDataAt(nsIDOMElement* aTable, int32_t aRowIndex, michael@0: int32_t aColIndex, nsIDOMElement **aCell, michael@0: int32_t* aStartRowIndex, int32_t* aStartColIndex, michael@0: int32_t* aRowSpan, int32_t* aColSpan, michael@0: int32_t* aActualRowSpan, int32_t* aActualColSpan, michael@0: bool* aIsSelected) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aStartRowIndex); michael@0: NS_ENSURE_ARG_POINTER(aStartColIndex); michael@0: NS_ENSURE_ARG_POINTER(aRowSpan); michael@0: NS_ENSURE_ARG_POINTER(aColSpan); michael@0: NS_ENSURE_ARG_POINTER(aActualRowSpan); michael@0: NS_ENSURE_ARG_POINTER(aActualColSpan); michael@0: NS_ENSURE_ARG_POINTER(aIsSelected); michael@0: NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); michael@0: michael@0: nsresult res=NS_ERROR_FAILURE; michael@0: *aStartRowIndex = 0; michael@0: *aStartColIndex = 0; michael@0: *aRowSpan = 0; michael@0: *aColSpan = 0; michael@0: *aActualRowSpan = 0; michael@0: *aActualColSpan = 0; michael@0: *aIsSelected = false; michael@0: michael@0: *aCell = nullptr; michael@0: michael@0: if (!aTable) michael@0: { michael@0: // Get the selected table or the table enclosing the selection anchor michael@0: nsCOMPtr table; michael@0: res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr, getter_AddRefs(table)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if (table) michael@0: aTable = table; michael@0: else michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsTableOuterFrame* tableFrame = GetTableFrame(aTable); michael@0: NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE); michael@0: michael@0: nsTableCellFrame* cellFrame = michael@0: tableFrame->GetCellFrameAt(aRowIndex, aColIndex); michael@0: if (!cellFrame) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: *aIsSelected = cellFrame->IsSelected(); michael@0: cellFrame->GetRowIndex(*aStartRowIndex); michael@0: cellFrame->GetColIndex(*aStartColIndex); michael@0: *aRowSpan = cellFrame->GetRowSpan(); michael@0: *aColSpan = cellFrame->GetColSpan(); michael@0: *aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex); michael@0: *aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex); michael@0: nsCOMPtr domCell = do_QueryInterface(cellFrame->GetContent()); michael@0: domCell.forget(aCell); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // When all you want is the cell michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetCellAt(nsIDOMElement* aTable, int32_t aRowIndex, int32_t aColIndex, nsIDOMElement **aCell) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCell); michael@0: *aCell = nullptr; michael@0: michael@0: if (!aTable) michael@0: { michael@0: // Get the selected table or the table enclosing the selection anchor michael@0: nsCOMPtr table; michael@0: nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr, getter_AddRefs(table)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); michael@0: aTable = table; michael@0: } michael@0: michael@0: nsTableOuterFrame* tableFrame = GetTableFrame(aTable); michael@0: if (!tableFrame) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr domCell = michael@0: do_QueryInterface(tableFrame->GetCellAt(aRowIndex, aColIndex)); michael@0: domCell.forget(aCell); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // When all you want are the rowspan and colspan (not exposed in nsITableEditor) michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetCellSpansAt(nsIDOMElement* aTable, int32_t aRowIndex, int32_t aColIndex, michael@0: int32_t& aActualRowSpan, int32_t& aActualColSpan) michael@0: { michael@0: nsTableOuterFrame* tableFrame = GetTableFrame(aTable); michael@0: if (!tableFrame) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex); michael@0: aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetCellContext(nsISelection **aSelection, michael@0: nsIDOMElement **aTable, michael@0: nsIDOMElement **aCell, michael@0: nsIDOMNode **aCellParent, int32_t *aCellOffset, michael@0: int32_t *aRowIndex, int32_t *aColIndex) michael@0: { michael@0: // Initialize return pointers michael@0: if (aSelection) *aSelection = nullptr; michael@0: if (aTable) *aTable = nullptr; michael@0: if (aCell) *aCell = nullptr; michael@0: if (aCellParent) *aCellParent = nullptr; michael@0: if (aCellOffset) *aCellOffset = 0; michael@0: if (aRowIndex) *aRowIndex = 0; michael@0: if (aColIndex) *aColIndex = 0; michael@0: michael@0: nsCOMPtr selection; michael@0: nsresult res = GetSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); michael@0: michael@0: if (aSelection) michael@0: { michael@0: *aSelection = selection.get(); michael@0: NS_ADDREF(*aSelection); michael@0: } michael@0: nsCOMPtr table; michael@0: nsCOMPtr cell; michael@0: michael@0: // Caller may supply the cell... michael@0: if (aCell && *aCell) michael@0: cell = *aCell; michael@0: michael@0: // ...but if not supplied, michael@0: // get cell if it's the child of selection anchor node, michael@0: // or get the enclosing by a cell michael@0: if (!cell) michael@0: { michael@0: // Find a selected or enclosing table element michael@0: nsCOMPtr cellOrTableElement; michael@0: int32_t selectedCount; michael@0: nsAutoString tagName; michael@0: res = GetSelectedOrParentTableElement(tagName, &selectedCount, michael@0: getter_AddRefs(cellOrTableElement)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if (tagName.EqualsLiteral("table")) michael@0: { michael@0: // We have a selected table, not a cell michael@0: if (aTable) michael@0: { michael@0: *aTable = cellOrTableElement.get(); michael@0: NS_ADDREF(*aTable); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: if (!tagName.EqualsLiteral("td")) michael@0: return NS_EDITOR_ELEMENT_NOT_FOUND; michael@0: michael@0: // We found a cell michael@0: cell = cellOrTableElement; michael@0: } michael@0: if (aCell) michael@0: { michael@0: *aCell = cell.get(); michael@0: NS_ADDREF(*aCell); michael@0: } michael@0: michael@0: // Get containing table michael@0: res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), cell, getter_AddRefs(table)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: // Cell must be in a table, so fail if not found michael@0: NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); michael@0: if (aTable) michael@0: { michael@0: *aTable = table.get(); michael@0: NS_ADDREF(*aTable); michael@0: } michael@0: michael@0: // Get the rest of the related data only if requested michael@0: if (aRowIndex || aColIndex) michael@0: { michael@0: int32_t rowIndex, colIndex; michael@0: // Get current cell location so we can put caret back there when done michael@0: res = GetCellIndexes(cell, &rowIndex, &colIndex); michael@0: if(NS_FAILED(res)) return res; michael@0: if (aRowIndex) *aRowIndex = rowIndex; michael@0: if (aColIndex) *aColIndex = colIndex; michael@0: } michael@0: if (aCellParent) michael@0: { michael@0: nsCOMPtr cellParent; michael@0: // Get the immediate parent of the cell michael@0: res = cell->GetParentNode(getter_AddRefs(cellParent)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: // Cell has to have a parent, so fail if not found michael@0: NS_ENSURE_TRUE(cellParent, NS_ERROR_FAILURE); michael@0: michael@0: *aCellParent = cellParent.get(); michael@0: NS_ADDREF(*aCellParent); michael@0: michael@0: if (aCellOffset) { michael@0: *aCellOffset = GetChildOffset(cell, cellParent); michael@0: } michael@0: } michael@0: michael@0: return res; michael@0: } michael@0: michael@0: nsresult michael@0: nsHTMLEditor::GetCellFromRange(nsIDOMRange *aRange, nsIDOMElement **aCell) michael@0: { michael@0: // Note: this might return a node that is outside of the range. michael@0: // Use carefully. michael@0: NS_ENSURE_TRUE(aRange && aCell, NS_ERROR_NULL_POINTER); michael@0: michael@0: *aCell = nullptr; michael@0: michael@0: nsCOMPtr startParent; michael@0: nsresult res = aRange->GetStartContainer(getter_AddRefs(startParent)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE); michael@0: michael@0: int32_t startOffset; michael@0: res = aRange->GetStartOffset(&startOffset); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: nsCOMPtr childNode = GetChildAt(startParent, startOffset); michael@0: // This means selection is probably at a text node (or end of doc?) michael@0: if (!childNode) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsCOMPtr endParent; michael@0: res = aRange->GetEndContainer(getter_AddRefs(endParent)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE); michael@0: michael@0: int32_t endOffset; michael@0: res = aRange->GetEndOffset(&endOffset); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // If a cell is deleted, the range is collapse michael@0: // (startOffset == endOffset) michael@0: // so tell caller the cell wasn't found michael@0: if (startParent == endParent && michael@0: endOffset == startOffset+1 && michael@0: nsHTMLEditUtils::IsTableCell(childNode)) michael@0: { michael@0: // Should we also test if frame is selected? (Use GetCellDataAt()) michael@0: // (Let's not for now -- more efficient) michael@0: nsCOMPtr cellElement = do_QueryInterface(childNode); michael@0: *aCell = cellElement.get(); michael@0: NS_ADDREF(*aCell); michael@0: return NS_OK; michael@0: } michael@0: return NS_EDITOR_ELEMENT_NOT_FOUND; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetFirstSelectedCell(nsIDOMRange **aRange, nsIDOMElement **aCell) michael@0: { michael@0: NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); michael@0: *aCell = nullptr; michael@0: if (aRange) *aRange = nullptr; michael@0: michael@0: nsCOMPtr selection; michael@0: nsresult res = GetSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr range; michael@0: res = selection->GetRangeAt(0, getter_AddRefs(range)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); michael@0: michael@0: mSelectedCellIndex = 0; michael@0: michael@0: res = GetCellFromRange(range, aCell); michael@0: // Failure here probably means selection is in a text node, michael@0: // so there's no selected cell michael@0: if (NS_FAILED(res)) { michael@0: return NS_EDITOR_ELEMENT_NOT_FOUND; michael@0: } michael@0: // No cell means range was collapsed (cell was deleted) michael@0: if (!*aCell) { michael@0: return NS_EDITOR_ELEMENT_NOT_FOUND; michael@0: } michael@0: michael@0: if (aRange) michael@0: { michael@0: *aRange = range.get(); michael@0: NS_ADDREF(*aRange); michael@0: } michael@0: michael@0: // Setup for next cell michael@0: mSelectedCellIndex = 1; michael@0: michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetNextSelectedCell(nsIDOMRange **aRange, nsIDOMElement **aCell) michael@0: { michael@0: NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); michael@0: *aCell = nullptr; michael@0: if (aRange) *aRange = nullptr; michael@0: michael@0: nsCOMPtr selection; michael@0: nsresult res = GetSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); michael@0: michael@0: int32_t rangeCount; michael@0: res = selection->GetRangeCount(&rangeCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Don't even try if index exceeds range count michael@0: if (mSelectedCellIndex >= rangeCount) michael@0: return NS_EDITOR_ELEMENT_NOT_FOUND; michael@0: michael@0: // Scan through ranges to find next valid selected cell michael@0: nsCOMPtr range; michael@0: for (; mSelectedCellIndex < rangeCount; mSelectedCellIndex++) michael@0: { michael@0: res = selection->GetRangeAt(mSelectedCellIndex, getter_AddRefs(range)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); michael@0: michael@0: res = GetCellFromRange(range, aCell); michael@0: // Failure here means the range doesn't contain a cell michael@0: NS_ENSURE_SUCCESS(res, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: michael@0: // We found a selected cell michael@0: if (*aCell) break; michael@0: #ifdef DEBUG_cmanske michael@0: else michael@0: printf("GetNextSelectedCell: Collapsed range found\n"); michael@0: #endif michael@0: michael@0: // If we didn't find a cell, continue to next range in selection michael@0: } michael@0: // No cell means all remaining ranges were collapsed (cells were deleted) michael@0: NS_ENSURE_TRUE(*aCell, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: michael@0: if (aRange) michael@0: { michael@0: *aRange = range.get(); michael@0: NS_ADDREF(*aRange); michael@0: } michael@0: michael@0: // Setup for next cell michael@0: mSelectedCellIndex++; michael@0: michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetFirstSelectedCellInTable(int32_t *aRowIndex, int32_t *aColIndex, nsIDOMElement **aCell) michael@0: { michael@0: NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); michael@0: *aCell = nullptr; michael@0: if (aRowIndex) michael@0: *aRowIndex = 0; michael@0: if (aColIndex) michael@0: *aColIndex = 0; michael@0: michael@0: nsCOMPtr cell; michael@0: nsresult res = GetFirstSelectedCell(nullptr, getter_AddRefs(cell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); michael@0: michael@0: *aCell = cell.get(); michael@0: NS_ADDREF(*aCell); michael@0: michael@0: // Also return the row and/or column if requested michael@0: if (aRowIndex || aColIndex) michael@0: { michael@0: int32_t startRowIndex, startColIndex; michael@0: res = GetCellIndexes(cell, &startRowIndex, &startColIndex); michael@0: if(NS_FAILED(res)) return res; michael@0: michael@0: if (aRowIndex) michael@0: *aRowIndex = startRowIndex; michael@0: michael@0: if (aColIndex) michael@0: *aColIndex = startColIndex; michael@0: } michael@0: michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::SetSelectionAfterTableEdit(nsIDOMElement* aTable, int32_t aRow, int32_t aCol, michael@0: int32_t aDirection, bool aSelected) michael@0: { michael@0: NS_ENSURE_TRUE(aTable, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: nsCOMPtrselection; michael@0: nsresult res = GetSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: if (!selection) michael@0: { michael@0: #ifdef DEBUG_cmanske michael@0: printf("Selection not found after table manipulation!\n"); michael@0: #endif michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsCOMPtr cell; michael@0: bool done = false; michael@0: do { michael@0: res = GetCellAt(aTable, aRow, aCol, getter_AddRefs(cell)); michael@0: if (NS_SUCCEEDED(res)) michael@0: { michael@0: if (cell) michael@0: { michael@0: if (aSelected) michael@0: { michael@0: // Reselect the cell michael@0: return SelectElement(cell); michael@0: } michael@0: else michael@0: { michael@0: // Set the caret to deepest first child michael@0: // but don't go into nested tables michael@0: // TODO: Should we really be placing the caret at the END michael@0: // of the cell content? michael@0: return CollapseSelectionToDeepestNonTableFirstChild(selection, cell); michael@0: } michael@0: } else { michael@0: // Setup index to find another cell in the michael@0: // direction requested, but move in michael@0: // other direction if already at beginning of row or column michael@0: switch (aDirection) michael@0: { michael@0: case ePreviousColumn: michael@0: if (aCol == 0) michael@0: { michael@0: if (aRow > 0) michael@0: aRow--; michael@0: else michael@0: done = true; michael@0: } michael@0: else michael@0: aCol--; michael@0: break; michael@0: case ePreviousRow: michael@0: if (aRow == 0) michael@0: { michael@0: if (aCol > 0) michael@0: aCol--; michael@0: else michael@0: done = true; michael@0: } michael@0: else michael@0: aRow--; michael@0: break; michael@0: default: michael@0: done = true; michael@0: } michael@0: } michael@0: } michael@0: else michael@0: break; michael@0: } while (!done); michael@0: michael@0: // We didn't find a cell michael@0: // Set selection to just before the table michael@0: nsCOMPtr tableParent; michael@0: res = aTable->GetParentNode(getter_AddRefs(tableParent)); michael@0: if(NS_SUCCEEDED(res) && tableParent) michael@0: { michael@0: int32_t tableOffset = GetChildOffset(aTable, tableParent); michael@0: return selection->Collapse(tableParent, tableOffset); michael@0: } michael@0: // Last resort: Set selection to start of doc michael@0: // (it's very bad to not have a valid selection!) michael@0: return SetSelectionAtDocumentStart(selection); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetSelectedOrParentTableElement(nsAString& aTagName, michael@0: int32_t *aSelectedCount, michael@0: nsIDOMElement** aTableElement) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aTableElement); michael@0: NS_ENSURE_ARG_POINTER(aSelectedCount); michael@0: *aTableElement = nullptr; michael@0: aTagName.Truncate(); michael@0: *aSelectedCount = 0; michael@0: michael@0: nsCOMPtr selection; michael@0: nsresult res = GetSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); michael@0: michael@0: // Try to get the first selected cell michael@0: nsCOMPtr tableOrCellElement; michael@0: res = GetFirstSelectedCell(nullptr, getter_AddRefs(tableOrCellElement)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: NS_NAMED_LITERAL_STRING(tdName, "td"); michael@0: michael@0: if (tableOrCellElement) michael@0: { michael@0: // Each cell is in its own selection range, michael@0: // so count signals multiple-cell selection michael@0: res = selection->GetRangeCount(aSelectedCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: aTagName = tdName; michael@0: } michael@0: else michael@0: { michael@0: nsCOMPtr anchorNode; michael@0: res = selection->GetAnchorNode(getter_AddRefs(anchorNode)); michael@0: if(NS_FAILED(res)) return res; michael@0: NS_ENSURE_TRUE(anchorNode, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr selectedNode; michael@0: michael@0: // Get child of anchor node, if exists michael@0: bool hasChildren; michael@0: anchorNode->HasChildNodes(&hasChildren); michael@0: michael@0: if (hasChildren) michael@0: { michael@0: int32_t anchorOffset; michael@0: res = selection->GetAnchorOffset(&anchorOffset); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: selectedNode = GetChildAt(anchorNode, anchorOffset); michael@0: if (!selectedNode) michael@0: { michael@0: selectedNode = anchorNode; michael@0: // If anchor doesn't have a child, we can't be selecting a table element, michael@0: // so don't do the following: michael@0: } michael@0: else michael@0: { michael@0: nsCOMPtr atom = nsEditor::GetTag(selectedNode); michael@0: michael@0: if (atom == nsEditProperty::td) michael@0: { michael@0: tableOrCellElement = do_QueryInterface(selectedNode); michael@0: aTagName = tdName; michael@0: // Each cell is in its own selection range, michael@0: // so count signals multiple-cell selection michael@0: res = selection->GetRangeCount(aSelectedCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: else if (atom == nsEditProperty::table) michael@0: { michael@0: tableOrCellElement = do_QueryInterface(selectedNode); michael@0: aTagName.AssignLiteral("table"); michael@0: *aSelectedCount = 1; michael@0: } michael@0: else if (atom == nsEditProperty::tr) michael@0: { michael@0: tableOrCellElement = do_QueryInterface(selectedNode); michael@0: aTagName.AssignLiteral("tr"); michael@0: *aSelectedCount = 1; michael@0: } michael@0: } michael@0: } michael@0: if (!tableOrCellElement) michael@0: { michael@0: // Didn't find a table element -- find a cell parent michael@0: res = GetElementOrParentByTagName(tdName, anchorNode, getter_AddRefs(tableOrCellElement)); michael@0: if(NS_FAILED(res)) return res; michael@0: if (tableOrCellElement) michael@0: aTagName = tdName; michael@0: } michael@0: } michael@0: if (tableOrCellElement) michael@0: { michael@0: *aTableElement = tableOrCellElement.get(); michael@0: NS_ADDREF(*aTableElement); michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHTMLEditor::GetSelectedCellsType(nsIDOMElement *aElement, uint32_t *aSelectionType) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aSelectionType); michael@0: *aSelectionType = 0; michael@0: michael@0: // Be sure we have a table element michael@0: // (if aElement is null, this uses selection's anchor node) michael@0: nsCOMPtr table; michael@0: michael@0: nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aElement, getter_AddRefs(table)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: int32_t rowCount, colCount; michael@0: res = GetTableSize(table, &rowCount, &colCount); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // Traverse all selected cells michael@0: nsCOMPtr selectedCell; michael@0: res = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if (res == NS_EDITOR_ELEMENT_NOT_FOUND) return NS_OK; michael@0: michael@0: // We have at least one selected cell, so set return value michael@0: *aSelectionType = nsISelectionPrivate::TABLESELECTION_CELL; michael@0: michael@0: // Store indexes of each row/col to avoid duplication of searches michael@0: nsTArray indexArray; michael@0: michael@0: bool allCellsInRowAreSelected = false; michael@0: bool allCellsInColAreSelected = false; michael@0: while (NS_SUCCEEDED(res) && selectedCell) michael@0: { michael@0: // Get the cell's location in the cellmap michael@0: int32_t startRowIndex, startColIndex; michael@0: res = GetCellIndexes(selectedCell, &startRowIndex, &startColIndex); michael@0: if(NS_FAILED(res)) return res; michael@0: michael@0: if (!indexArray.Contains(startColIndex)) michael@0: { michael@0: indexArray.AppendElement(startColIndex); michael@0: allCellsInRowAreSelected = AllCellsInRowSelected(table, startRowIndex, colCount); michael@0: // We're done as soon as we fail for any row michael@0: if (!allCellsInRowAreSelected) break; michael@0: } michael@0: res = GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell)); michael@0: } michael@0: michael@0: if (allCellsInRowAreSelected) michael@0: { michael@0: *aSelectionType = nsISelectionPrivate::TABLESELECTION_ROW; michael@0: return NS_OK; michael@0: } michael@0: // Test for columns michael@0: michael@0: // Empty the indexArray michael@0: indexArray.Clear(); michael@0: michael@0: // Start at first cell again michael@0: res = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell)); michael@0: while (NS_SUCCEEDED(res) && selectedCell) michael@0: { michael@0: // Get the cell's location in the cellmap michael@0: int32_t startRowIndex, startColIndex; michael@0: res = GetCellIndexes(selectedCell, &startRowIndex, &startColIndex); michael@0: if(NS_FAILED(res)) return res; michael@0: michael@0: if (!indexArray.Contains(startRowIndex)) michael@0: { michael@0: indexArray.AppendElement(startColIndex); michael@0: allCellsInColAreSelected = AllCellsInColumnSelected(table, startColIndex, rowCount); michael@0: // We're done as soon as we fail for any column michael@0: if (!allCellsInRowAreSelected) break; michael@0: } michael@0: res = GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell)); michael@0: } michael@0: if (allCellsInColAreSelected) michael@0: *aSelectionType = nsISelectionPrivate::TABLESELECTION_COLUMN; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditor::AllCellsInRowSelected(nsIDOMElement *aTable, int32_t aRowIndex, int32_t aNumberOfColumns) michael@0: { michael@0: NS_ENSURE_TRUE(aTable, false); michael@0: michael@0: int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: michael@0: for( int32_t col = 0; col < aNumberOfColumns; col += std::max(actualColSpan, 1)) michael@0: { michael@0: nsCOMPtr cell; michael@0: nsresult res = GetCellDataAt(aTable, aRowIndex, col, getter_AddRefs(cell), michael@0: &curStartRowIndex, &curStartColIndex, michael@0: &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: michael@0: NS_ENSURE_SUCCESS(res, false); michael@0: // If no cell, we may have a "ragged" right edge, michael@0: // so return TRUE only if we already found a cell in the row michael@0: NS_ENSURE_TRUE(cell, (col > 0) ? true : false); michael@0: michael@0: // Return as soon as a non-selected cell is found michael@0: NS_ENSURE_TRUE(isSelected, false); michael@0: michael@0: NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in AllCellsInRowSelected"); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditor::AllCellsInColumnSelected(nsIDOMElement *aTable, int32_t aColIndex, int32_t aNumberOfRows) michael@0: { michael@0: NS_ENSURE_TRUE(aTable, false); michael@0: michael@0: int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; michael@0: bool isSelected; michael@0: michael@0: for( int32_t row = 0; row < aNumberOfRows; row += std::max(actualRowSpan, 1)) michael@0: { michael@0: nsCOMPtr cell; michael@0: nsresult res = GetCellDataAt(aTable, row, aColIndex, getter_AddRefs(cell), michael@0: &curStartRowIndex, &curStartColIndex, michael@0: &rowSpan, &colSpan, michael@0: &actualRowSpan, &actualColSpan, &isSelected); michael@0: michael@0: NS_ENSURE_SUCCESS(res, false); michael@0: // If no cell, we must have a "ragged" right edge on the last column michael@0: // so return TRUE only if we already found a cell in the row michael@0: NS_ENSURE_TRUE(cell, (row > 0) ? true : false); michael@0: michael@0: // Return as soon as a non-selected cell is found michael@0: NS_ENSURE_TRUE(isSelected, false); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsHTMLEditor::IsEmptyCell(dom::Element* aCell) michael@0: { michael@0: MOZ_ASSERT(aCell); michael@0: michael@0: // Check if target only contains empty text node or
michael@0: nsCOMPtr cellChild = aCell->GetFirstChild(); michael@0: if (!cellChild) { michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr nextChild = cellChild->GetNextSibling(); michael@0: if (nextChild) { michael@0: return false; michael@0: } michael@0: michael@0: // We insert a single break into a cell by default michael@0: // to have some place to locate a cursor -- it is dispensable michael@0: if (cellChild->IsElement() && cellChild->AsElement()->IsHTML(nsGkAtoms::br)) { michael@0: return true; michael@0: } michael@0: michael@0: bool isEmpty; michael@0: // Or check if no real content michael@0: nsresult rv = IsEmptyNode(cellChild, &isEmpty, false, false); michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: return isEmpty; michael@0: }