Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include <stdio.h> |
michael@0 | 7 | |
michael@0 | 8 | #include "mozilla/Assertions.h" |
michael@0 | 9 | #include "mozilla/dom/Selection.h" |
michael@0 | 10 | #include "mozilla/dom/Element.h" |
michael@0 | 11 | #include "nsAString.h" |
michael@0 | 12 | #include "nsAlgorithm.h" |
michael@0 | 13 | #include "nsCOMPtr.h" |
michael@0 | 14 | #include "nsDebug.h" |
michael@0 | 15 | #include "nsEditProperty.h" |
michael@0 | 16 | #include "nsEditor.h" |
michael@0 | 17 | #include "nsEditorUtils.h" |
michael@0 | 18 | #include "nsError.h" |
michael@0 | 19 | #include "nsGkAtoms.h" |
michael@0 | 20 | #include "nsHTMLEditUtils.h" |
michael@0 | 21 | #include "nsHTMLEditor.h" |
michael@0 | 22 | #include "nsIAtom.h" |
michael@0 | 23 | #include "nsIContent.h" |
michael@0 | 24 | #include "nsIDOMElement.h" |
michael@0 | 25 | #include "nsIDOMNode.h" |
michael@0 | 26 | #include "nsIDOMRange.h" |
michael@0 | 27 | #include "nsIEditor.h" |
michael@0 | 28 | #include "nsIFrame.h" |
michael@0 | 29 | #include "nsIHTMLEditor.h" |
michael@0 | 30 | #include "nsINode.h" |
michael@0 | 31 | #include "nsIPresShell.h" |
michael@0 | 32 | #include "nsISupportsUtils.h" |
michael@0 | 33 | #include "nsITableCellLayout.h" // For efficient access to table cell |
michael@0 | 34 | #include "nsITableEditor.h" |
michael@0 | 35 | #include "nsLiteralString.h" |
michael@0 | 36 | #include "nsQueryFrame.h" |
michael@0 | 37 | #include "nsString.h" |
michael@0 | 38 | #include "nsTArray.h" |
michael@0 | 39 | #include "nsTableCellFrame.h" |
michael@0 | 40 | #include "nsTableOuterFrame.h" |
michael@0 | 41 | #include "nscore.h" |
michael@0 | 42 | #include <algorithm> |
michael@0 | 43 | |
michael@0 | 44 | using namespace mozilla; |
michael@0 | 45 | using namespace mozilla::dom; |
michael@0 | 46 | |
michael@0 | 47 | /*************************************************************************** |
michael@0 | 48 | * stack based helper class for restoring selection after table edit |
michael@0 | 49 | */ |
michael@0 | 50 | class MOZ_STACK_CLASS nsSetSelectionAfterTableEdit |
michael@0 | 51 | { |
michael@0 | 52 | private: |
michael@0 | 53 | nsCOMPtr<nsITableEditor> mEd; |
michael@0 | 54 | nsCOMPtr<nsIDOMElement> mTable; |
michael@0 | 55 | int32_t mCol, mRow, mDirection, mSelected; |
michael@0 | 56 | public: |
michael@0 | 57 | nsSetSelectionAfterTableEdit(nsITableEditor *aEd, nsIDOMElement* aTable, |
michael@0 | 58 | int32_t aRow, int32_t aCol, int32_t aDirection, |
michael@0 | 59 | bool aSelected) : |
michael@0 | 60 | mEd(do_QueryInterface(aEd)) |
michael@0 | 61 | { |
michael@0 | 62 | mTable = aTable; |
michael@0 | 63 | mRow = aRow; |
michael@0 | 64 | mCol = aCol; |
michael@0 | 65 | mDirection = aDirection; |
michael@0 | 66 | mSelected = aSelected; |
michael@0 | 67 | } |
michael@0 | 68 | |
michael@0 | 69 | ~nsSetSelectionAfterTableEdit() |
michael@0 | 70 | { |
michael@0 | 71 | if (mEd) |
michael@0 | 72 | mEd->SetSelectionAfterTableEdit(mTable, mRow, mCol, mDirection, mSelected); |
michael@0 | 73 | } |
michael@0 | 74 | // This is needed to abort the caret reset in the destructor |
michael@0 | 75 | // when one method yields control to another |
michael@0 | 76 | void CancelSetCaret() {mEd = nullptr; mTable = nullptr;} |
michael@0 | 77 | }; |
michael@0 | 78 | |
michael@0 | 79 | // Stack-class to turn on/off selection batching for table selection |
michael@0 | 80 | class MOZ_STACK_CLASS nsSelectionBatcherForTable |
michael@0 | 81 | { |
michael@0 | 82 | private: |
michael@0 | 83 | nsCOMPtr<nsISelectionPrivate> mSelection; |
michael@0 | 84 | public: |
michael@0 | 85 | nsSelectionBatcherForTable(nsISelection *aSelection) |
michael@0 | 86 | { |
michael@0 | 87 | nsCOMPtr<nsISelection> sel(aSelection); |
michael@0 | 88 | mSelection = do_QueryInterface(sel); |
michael@0 | 89 | if (mSelection) mSelection->StartBatchChanges(); |
michael@0 | 90 | } |
michael@0 | 91 | virtual ~nsSelectionBatcherForTable() |
michael@0 | 92 | { |
michael@0 | 93 | if (mSelection) mSelection->EndBatchChanges(); |
michael@0 | 94 | } |
michael@0 | 95 | }; |
michael@0 | 96 | |
michael@0 | 97 | // Table Editing helper utilities (not exposed in IDL) |
michael@0 | 98 | |
michael@0 | 99 | NS_IMETHODIMP |
michael@0 | 100 | nsHTMLEditor::InsertCell(nsIDOMElement *aCell, int32_t aRowSpan, int32_t aColSpan, |
michael@0 | 101 | bool aAfter, bool aIsHeader, nsIDOMElement **aNewCell) |
michael@0 | 102 | { |
michael@0 | 103 | NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); |
michael@0 | 104 | if (aNewCell) *aNewCell = nullptr; |
michael@0 | 105 | |
michael@0 | 106 | // And the parent and offsets needed to do an insert |
michael@0 | 107 | nsCOMPtr<nsIDOMNode> cellParent; |
michael@0 | 108 | nsresult res = aCell->GetParentNode(getter_AddRefs(cellParent)); |
michael@0 | 109 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 110 | NS_ENSURE_TRUE(cellParent, NS_ERROR_NULL_POINTER); |
michael@0 | 111 | |
michael@0 | 112 | int32_t cellOffset = GetChildOffset(aCell, cellParent); |
michael@0 | 113 | |
michael@0 | 114 | nsCOMPtr<nsIDOMElement> newCell; |
michael@0 | 115 | if (aIsHeader) |
michael@0 | 116 | res = CreateElementWithDefaults(NS_LITERAL_STRING("th"), getter_AddRefs(newCell)); |
michael@0 | 117 | else |
michael@0 | 118 | res = CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell)); |
michael@0 | 119 | |
michael@0 | 120 | if(NS_FAILED(res)) return res; |
michael@0 | 121 | if(!newCell) return NS_ERROR_FAILURE; |
michael@0 | 122 | |
michael@0 | 123 | //Optional: return new cell created |
michael@0 | 124 | if (aNewCell) |
michael@0 | 125 | { |
michael@0 | 126 | *aNewCell = newCell.get(); |
michael@0 | 127 | NS_ADDREF(*aNewCell); |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | if( aRowSpan > 1) |
michael@0 | 131 | { |
michael@0 | 132 | // Note: Do NOT use editor transaction for this |
michael@0 | 133 | nsAutoString newRowSpan; |
michael@0 | 134 | newRowSpan.AppendInt(aRowSpan, 10); |
michael@0 | 135 | newCell->SetAttribute(NS_LITERAL_STRING("rowspan"), newRowSpan); |
michael@0 | 136 | } |
michael@0 | 137 | if( aColSpan > 1) |
michael@0 | 138 | { |
michael@0 | 139 | // Note: Do NOT use editor transaction for this |
michael@0 | 140 | nsAutoString newColSpan; |
michael@0 | 141 | newColSpan.AppendInt(aColSpan, 10); |
michael@0 | 142 | newCell->SetAttribute(NS_LITERAL_STRING("colspan"), newColSpan); |
michael@0 | 143 | } |
michael@0 | 144 | if(aAfter) cellOffset++; |
michael@0 | 145 | |
michael@0 | 146 | //Don't let Rules System change the selection |
michael@0 | 147 | nsAutoTxnsConserveSelection dontChangeSelection(this); |
michael@0 | 148 | return InsertNode(newCell, cellParent, cellOffset); |
michael@0 | 149 | } |
michael@0 | 150 | |
michael@0 | 151 | NS_IMETHODIMP nsHTMLEditor::SetColSpan(nsIDOMElement *aCell, int32_t aColSpan) |
michael@0 | 152 | { |
michael@0 | 153 | NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); |
michael@0 | 154 | nsAutoString newSpan; |
michael@0 | 155 | newSpan.AppendInt(aColSpan, 10); |
michael@0 | 156 | return SetAttribute(aCell, NS_LITERAL_STRING("colspan"), newSpan); |
michael@0 | 157 | } |
michael@0 | 158 | |
michael@0 | 159 | NS_IMETHODIMP nsHTMLEditor::SetRowSpan(nsIDOMElement *aCell, int32_t aRowSpan) |
michael@0 | 160 | { |
michael@0 | 161 | NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); |
michael@0 | 162 | nsAutoString newSpan; |
michael@0 | 163 | newSpan.AppendInt(aRowSpan, 10); |
michael@0 | 164 | return SetAttribute(aCell, NS_LITERAL_STRING("rowspan"), newSpan); |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | /****************************************************************/ |
michael@0 | 168 | |
michael@0 | 169 | // Table Editing interface methods |
michael@0 | 170 | |
michael@0 | 171 | NS_IMETHODIMP |
michael@0 | 172 | nsHTMLEditor::InsertTableCell(int32_t aNumber, bool aAfter) |
michael@0 | 173 | { |
michael@0 | 174 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 175 | nsCOMPtr<nsIDOMElement> curCell; |
michael@0 | 176 | nsCOMPtr<nsIDOMNode> cellParent; |
michael@0 | 177 | int32_t cellOffset, startRowIndex, startColIndex; |
michael@0 | 178 | nsresult res = GetCellContext(nullptr, |
michael@0 | 179 | getter_AddRefs(table), |
michael@0 | 180 | getter_AddRefs(curCell), |
michael@0 | 181 | getter_AddRefs(cellParent), &cellOffset, |
michael@0 | 182 | &startRowIndex, &startColIndex); |
michael@0 | 183 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 184 | // Don't fail if no cell found |
michael@0 | 185 | NS_ENSURE_TRUE(curCell, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 186 | |
michael@0 | 187 | // Get more data for current cell in row we are inserting at (we need COLSPAN) |
michael@0 | 188 | int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 189 | bool isSelected; |
michael@0 | 190 | res = GetCellDataAt(table, startRowIndex, startColIndex, |
michael@0 | 191 | getter_AddRefs(curCell), |
michael@0 | 192 | &curStartRowIndex, &curStartColIndex, &rowSpan, &colSpan, |
michael@0 | 193 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 194 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 195 | NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE); |
michael@0 | 196 | int32_t newCellIndex = aAfter ? (startColIndex+colSpan) : startColIndex; |
michael@0 | 197 | //We control selection resetting after the insert... |
michael@0 | 198 | nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, newCellIndex, ePreviousColumn, false); |
michael@0 | 199 | //...so suppress Rules System selection munging |
michael@0 | 200 | nsAutoTxnsConserveSelection dontChangeSelection(this); |
michael@0 | 201 | |
michael@0 | 202 | int32_t i; |
michael@0 | 203 | for (i = 0; i < aNumber; i++) |
michael@0 | 204 | { |
michael@0 | 205 | nsCOMPtr<nsIDOMElement> newCell; |
michael@0 | 206 | res = CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell)); |
michael@0 | 207 | if (NS_SUCCEEDED(res) && newCell) |
michael@0 | 208 | { |
michael@0 | 209 | if (aAfter) cellOffset++; |
michael@0 | 210 | res = InsertNode(newCell, cellParent, cellOffset); |
michael@0 | 211 | if(NS_FAILED(res)) break; |
michael@0 | 212 | } |
michael@0 | 213 | } |
michael@0 | 214 | return res; |
michael@0 | 215 | } |
michael@0 | 216 | |
michael@0 | 217 | |
michael@0 | 218 | NS_IMETHODIMP |
michael@0 | 219 | nsHTMLEditor::GetFirstRow(nsIDOMElement* aTableElement, nsIDOMNode** aRowNode) |
michael@0 | 220 | { |
michael@0 | 221 | NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER); |
michael@0 | 222 | |
michael@0 | 223 | *aRowNode = nullptr; |
michael@0 | 224 | |
michael@0 | 225 | NS_ENSURE_TRUE(aTableElement, NS_ERROR_NULL_POINTER); |
michael@0 | 226 | |
michael@0 | 227 | nsCOMPtr<nsIDOMElement> tableElement; |
michael@0 | 228 | nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aTableElement, getter_AddRefs(tableElement)); |
michael@0 | 229 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 230 | NS_ENSURE_TRUE(tableElement, NS_ERROR_NULL_POINTER); |
michael@0 | 231 | |
michael@0 | 232 | nsCOMPtr<nsIDOMNode> tableChild; |
michael@0 | 233 | res = tableElement->GetFirstChild(getter_AddRefs(tableChild)); |
michael@0 | 234 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 235 | |
michael@0 | 236 | while (tableChild) |
michael@0 | 237 | { |
michael@0 | 238 | nsCOMPtr<nsIContent> content = do_QueryInterface(tableChild); |
michael@0 | 239 | if (content) |
michael@0 | 240 | { |
michael@0 | 241 | nsIAtom *atom = content->Tag(); |
michael@0 | 242 | |
michael@0 | 243 | if (atom == nsEditProperty::tr) |
michael@0 | 244 | { |
michael@0 | 245 | // Found a row directly under <table> |
michael@0 | 246 | *aRowNode = tableChild; |
michael@0 | 247 | NS_ADDREF(*aRowNode); |
michael@0 | 248 | return NS_OK; |
michael@0 | 249 | } |
michael@0 | 250 | // Look for row in one of the row container elements |
michael@0 | 251 | if (atom == nsEditProperty::tbody || |
michael@0 | 252 | atom == nsEditProperty::thead || |
michael@0 | 253 | atom == nsEditProperty::tfoot) |
michael@0 | 254 | { |
michael@0 | 255 | nsCOMPtr<nsIDOMNode> rowNode; |
michael@0 | 256 | res = tableChild->GetFirstChild(getter_AddRefs(rowNode)); |
michael@0 | 257 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 258 | |
michael@0 | 259 | // We can encounter textnodes here -- must find a row |
michael@0 | 260 | while (rowNode && !nsHTMLEditUtils::IsTableRow(rowNode)) |
michael@0 | 261 | { |
michael@0 | 262 | nsCOMPtr<nsIDOMNode> nextNode; |
michael@0 | 263 | res = rowNode->GetNextSibling(getter_AddRefs(nextNode)); |
michael@0 | 264 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 265 | |
michael@0 | 266 | rowNode = nextNode; |
michael@0 | 267 | } |
michael@0 | 268 | if(rowNode) |
michael@0 | 269 | { |
michael@0 | 270 | *aRowNode = rowNode.get(); |
michael@0 | 271 | NS_ADDREF(*aRowNode); |
michael@0 | 272 | return NS_OK; |
michael@0 | 273 | } |
michael@0 | 274 | } |
michael@0 | 275 | } |
michael@0 | 276 | // Here if table child was a CAPTION or COLGROUP |
michael@0 | 277 | // or child of a row parent wasn't a row (bad HTML?), |
michael@0 | 278 | // or first child was a textnode |
michael@0 | 279 | // Look in next table child |
michael@0 | 280 | nsCOMPtr<nsIDOMNode> nextChild; |
michael@0 | 281 | res = tableChild->GetNextSibling(getter_AddRefs(nextChild)); |
michael@0 | 282 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 283 | |
michael@0 | 284 | tableChild = nextChild; |
michael@0 | 285 | }; |
michael@0 | 286 | // If here, row was not found |
michael@0 | 287 | return NS_EDITOR_ELEMENT_NOT_FOUND; |
michael@0 | 288 | } |
michael@0 | 289 | |
michael@0 | 290 | NS_IMETHODIMP |
michael@0 | 291 | nsHTMLEditor::GetNextRow(nsIDOMNode* aCurrentRowNode, nsIDOMNode **aRowNode) |
michael@0 | 292 | { |
michael@0 | 293 | NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER); |
michael@0 | 294 | |
michael@0 | 295 | *aRowNode = nullptr; |
michael@0 | 296 | |
michael@0 | 297 | NS_ENSURE_TRUE(aCurrentRowNode, NS_ERROR_NULL_POINTER); |
michael@0 | 298 | |
michael@0 | 299 | if (!nsHTMLEditUtils::IsTableRow(aCurrentRowNode)) |
michael@0 | 300 | return NS_ERROR_FAILURE; |
michael@0 | 301 | |
michael@0 | 302 | nsCOMPtr<nsIDOMNode> nextRow; |
michael@0 | 303 | nsresult res = aCurrentRowNode->GetNextSibling(getter_AddRefs(nextRow)); |
michael@0 | 304 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 305 | |
michael@0 | 306 | nsCOMPtr<nsIDOMNode> nextNode; |
michael@0 | 307 | |
michael@0 | 308 | // Skip over any textnodes here |
michael@0 | 309 | while (nextRow && !nsHTMLEditUtils::IsTableRow(nextRow)) |
michael@0 | 310 | { |
michael@0 | 311 | res = nextRow->GetNextSibling(getter_AddRefs(nextNode)); |
michael@0 | 312 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 313 | |
michael@0 | 314 | nextRow = nextNode; |
michael@0 | 315 | } |
michael@0 | 316 | if(nextRow) |
michael@0 | 317 | { |
michael@0 | 318 | *aRowNode = nextRow.get(); |
michael@0 | 319 | NS_ADDREF(*aRowNode); |
michael@0 | 320 | return NS_OK; |
michael@0 | 321 | } |
michael@0 | 322 | |
michael@0 | 323 | // No row found, search for rows in other table sections |
michael@0 | 324 | nsCOMPtr<nsIDOMNode> rowParent; |
michael@0 | 325 | res = aCurrentRowNode->GetParentNode(getter_AddRefs(rowParent)); |
michael@0 | 326 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 327 | NS_ENSURE_TRUE(rowParent, NS_ERROR_NULL_POINTER); |
michael@0 | 328 | |
michael@0 | 329 | nsCOMPtr<nsIDOMNode> parentSibling; |
michael@0 | 330 | res = rowParent->GetNextSibling(getter_AddRefs(parentSibling)); |
michael@0 | 331 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 332 | |
michael@0 | 333 | while (parentSibling) |
michael@0 | 334 | { |
michael@0 | 335 | res = parentSibling->GetFirstChild(getter_AddRefs(nextRow)); |
michael@0 | 336 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 337 | |
michael@0 | 338 | // We can encounter textnodes here -- must find a row |
michael@0 | 339 | while (nextRow && !nsHTMLEditUtils::IsTableRow(nextRow)) |
michael@0 | 340 | { |
michael@0 | 341 | res = nextRow->GetNextSibling(getter_AddRefs(nextNode)); |
michael@0 | 342 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 343 | |
michael@0 | 344 | nextRow = nextNode; |
michael@0 | 345 | } |
michael@0 | 346 | if(nextRow) |
michael@0 | 347 | { |
michael@0 | 348 | *aRowNode = nextRow.get(); |
michael@0 | 349 | NS_ADDREF(*aRowNode); |
michael@0 | 350 | return NS_OK; |
michael@0 | 351 | } |
michael@0 | 352 | #ifdef DEBUG_cmanske |
michael@0 | 353 | printf("GetNextRow: firstChild of row's parent's sibling is not a TR!\n"); |
michael@0 | 354 | #endif |
michael@0 | 355 | // We arrive here only if a table section has no children |
michael@0 | 356 | // or first child of section is not a row (bad HTML or more "_moz_text" nodes!) |
michael@0 | 357 | // So look for another section sibling |
michael@0 | 358 | res = parentSibling->GetNextSibling(getter_AddRefs(nextNode)); |
michael@0 | 359 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 360 | |
michael@0 | 361 | parentSibling = nextNode; |
michael@0 | 362 | } |
michael@0 | 363 | // If here, row was not found |
michael@0 | 364 | return NS_EDITOR_ELEMENT_NOT_FOUND; |
michael@0 | 365 | } |
michael@0 | 366 | |
michael@0 | 367 | NS_IMETHODIMP |
michael@0 | 368 | nsHTMLEditor::GetLastCellInRow(nsIDOMNode* aRowNode, nsIDOMNode** aCellNode) |
michael@0 | 369 | { |
michael@0 | 370 | NS_ENSURE_TRUE(aCellNode, NS_ERROR_NULL_POINTER); |
michael@0 | 371 | |
michael@0 | 372 | *aCellNode = nullptr; |
michael@0 | 373 | |
michael@0 | 374 | NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER); |
michael@0 | 375 | |
michael@0 | 376 | nsCOMPtr<nsIDOMNode> rowChild; |
michael@0 | 377 | nsresult res = aRowNode->GetLastChild(getter_AddRefs(rowChild)); |
michael@0 | 378 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 379 | |
michael@0 | 380 | while (rowChild && !nsHTMLEditUtils::IsTableCell(rowChild)) |
michael@0 | 381 | { |
michael@0 | 382 | // Skip over textnodes |
michael@0 | 383 | nsCOMPtr<nsIDOMNode> previousChild; |
michael@0 | 384 | res = rowChild->GetPreviousSibling(getter_AddRefs(previousChild)); |
michael@0 | 385 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 386 | |
michael@0 | 387 | rowChild = previousChild; |
michael@0 | 388 | }; |
michael@0 | 389 | if (rowChild) |
michael@0 | 390 | { |
michael@0 | 391 | *aCellNode = rowChild.get(); |
michael@0 | 392 | NS_ADDREF(*aCellNode); |
michael@0 | 393 | return NS_OK; |
michael@0 | 394 | } |
michael@0 | 395 | // If here, cell was not found |
michael@0 | 396 | return NS_EDITOR_ELEMENT_NOT_FOUND; |
michael@0 | 397 | } |
michael@0 | 398 | |
michael@0 | 399 | NS_IMETHODIMP |
michael@0 | 400 | nsHTMLEditor::InsertTableColumn(int32_t aNumber, bool aAfter) |
michael@0 | 401 | { |
michael@0 | 402 | nsCOMPtr<nsISelection> selection; |
michael@0 | 403 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 404 | nsCOMPtr<nsIDOMElement> curCell; |
michael@0 | 405 | int32_t startRowIndex, startColIndex; |
michael@0 | 406 | nsresult res = GetCellContext(getter_AddRefs(selection), |
michael@0 | 407 | getter_AddRefs(table), |
michael@0 | 408 | getter_AddRefs(curCell), |
michael@0 | 409 | nullptr, nullptr, |
michael@0 | 410 | &startRowIndex, &startColIndex); |
michael@0 | 411 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 412 | // Don't fail if no cell found |
michael@0 | 413 | NS_ENSURE_TRUE(curCell, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 414 | |
michael@0 | 415 | // Get more data for current cell (we need ROWSPAN) |
michael@0 | 416 | int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 417 | bool isSelected; |
michael@0 | 418 | res = GetCellDataAt(table, startRowIndex, startColIndex, |
michael@0 | 419 | getter_AddRefs(curCell), |
michael@0 | 420 | &curStartRowIndex, &curStartColIndex, |
michael@0 | 421 | &rowSpan, &colSpan, |
michael@0 | 422 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 423 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 424 | NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE); |
michael@0 | 425 | |
michael@0 | 426 | nsAutoEditBatch beginBatching(this); |
michael@0 | 427 | // Prevent auto insertion of BR in new cell until we're done |
michael@0 | 428 | nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); |
michael@0 | 429 | |
michael@0 | 430 | // Use column after current cell if requested |
michael@0 | 431 | if (aAfter) |
michael@0 | 432 | { |
michael@0 | 433 | startColIndex += actualColSpan; |
michael@0 | 434 | //Detect when user is adding after a COLSPAN=0 case |
michael@0 | 435 | // Assume they want to stop the "0" behavior and |
michael@0 | 436 | // really add a new column. Thus we set the |
michael@0 | 437 | // colspan to its true value |
michael@0 | 438 | if (colSpan == 0) |
michael@0 | 439 | SetColSpan(curCell, actualColSpan); |
michael@0 | 440 | } |
michael@0 | 441 | |
michael@0 | 442 | int32_t rowCount, colCount, rowIndex; |
michael@0 | 443 | res = GetTableSize(table, &rowCount, &colCount); |
michael@0 | 444 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 445 | |
michael@0 | 446 | //We reset caret in destructor... |
michael@0 | 447 | nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow, false); |
michael@0 | 448 | //.. so suppress Rules System selection munging |
michael@0 | 449 | nsAutoTxnsConserveSelection dontChangeSelection(this); |
michael@0 | 450 | |
michael@0 | 451 | // If we are inserting after all existing columns |
michael@0 | 452 | // Make sure table is "well formed" |
michael@0 | 453 | // before appending new column |
michael@0 | 454 | if (startColIndex >= colCount) |
michael@0 | 455 | NormalizeTable(table); |
michael@0 | 456 | |
michael@0 | 457 | nsCOMPtr<nsIDOMNode> rowNode; |
michael@0 | 458 | for ( rowIndex = 0; rowIndex < rowCount; rowIndex++) |
michael@0 | 459 | { |
michael@0 | 460 | #ifdef DEBUG_cmanske |
michael@0 | 461 | if (rowIndex == rowCount-1) |
michael@0 | 462 | printf(" ***InsertTableColumn: Inserting cell at last row: %d\n", rowIndex); |
michael@0 | 463 | #endif |
michael@0 | 464 | |
michael@0 | 465 | if (startColIndex < colCount) |
michael@0 | 466 | { |
michael@0 | 467 | // We are inserting before an existing column |
michael@0 | 468 | res = GetCellDataAt(table, rowIndex, startColIndex, |
michael@0 | 469 | getter_AddRefs(curCell), |
michael@0 | 470 | &curStartRowIndex, &curStartColIndex, |
michael@0 | 471 | &rowSpan, &colSpan, |
michael@0 | 472 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 473 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 474 | |
michael@0 | 475 | // Don't fail entire process if we fail to find a cell |
michael@0 | 476 | // (may fail just in particular rows with < adequate cells per row) |
michael@0 | 477 | if (curCell) |
michael@0 | 478 | { |
michael@0 | 479 | if (curStartColIndex < startColIndex) |
michael@0 | 480 | { |
michael@0 | 481 | // We have a cell spanning this location |
michael@0 | 482 | // Simply increase its colspan to keep table rectangular |
michael@0 | 483 | // Note: we do nothing if colsSpan=0, |
michael@0 | 484 | // since it should automatically span the new column |
michael@0 | 485 | if (colSpan > 0) |
michael@0 | 486 | SetColSpan(curCell, colSpan+aNumber); |
michael@0 | 487 | } else { |
michael@0 | 488 | // Simply set selection to the current cell |
michael@0 | 489 | // so we can let InsertTableCell() do the work |
michael@0 | 490 | // Insert a new cell before current one |
michael@0 | 491 | selection->Collapse(curCell, 0); |
michael@0 | 492 | res = InsertTableCell(aNumber, false); |
michael@0 | 493 | } |
michael@0 | 494 | } |
michael@0 | 495 | } else { |
michael@0 | 496 | // Get current row and append new cells after last cell in row |
michael@0 | 497 | if(rowIndex == 0) |
michael@0 | 498 | res = GetFirstRow(table.get(), getter_AddRefs(rowNode)); |
michael@0 | 499 | else |
michael@0 | 500 | { |
michael@0 | 501 | nsCOMPtr<nsIDOMNode> nextRow; |
michael@0 | 502 | res = GetNextRow(rowNode.get(), getter_AddRefs(nextRow)); |
michael@0 | 503 | rowNode = nextRow; |
michael@0 | 504 | } |
michael@0 | 505 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 506 | |
michael@0 | 507 | if (rowNode) |
michael@0 | 508 | { |
michael@0 | 509 | nsCOMPtr<nsIDOMNode> lastCell; |
michael@0 | 510 | res = GetLastCellInRow(rowNode, getter_AddRefs(lastCell)); |
michael@0 | 511 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 512 | NS_ENSURE_TRUE(lastCell, NS_ERROR_FAILURE); |
michael@0 | 513 | |
michael@0 | 514 | curCell = do_QueryInterface(lastCell); |
michael@0 | 515 | if (curCell) |
michael@0 | 516 | { |
michael@0 | 517 | // Simply add same number of cells to each row |
michael@0 | 518 | // Although tempted to check cell indexes for curCell, |
michael@0 | 519 | // the effects of COLSPAN>1 in some cells makes this futile! |
michael@0 | 520 | // We must use NormalizeTable first to assure |
michael@0 | 521 | // that there are cells in each cellmap location |
michael@0 | 522 | selection->Collapse(curCell, 0); |
michael@0 | 523 | res = InsertTableCell(aNumber, true); |
michael@0 | 524 | } |
michael@0 | 525 | } |
michael@0 | 526 | } |
michael@0 | 527 | } |
michael@0 | 528 | return res; |
michael@0 | 529 | } |
michael@0 | 530 | |
michael@0 | 531 | NS_IMETHODIMP |
michael@0 | 532 | nsHTMLEditor::InsertTableRow(int32_t aNumber, bool aAfter) |
michael@0 | 533 | { |
michael@0 | 534 | nsCOMPtr<nsISelection> selection; |
michael@0 | 535 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 536 | nsCOMPtr<nsIDOMElement> curCell; |
michael@0 | 537 | |
michael@0 | 538 | int32_t startRowIndex, startColIndex; |
michael@0 | 539 | nsresult res = GetCellContext(nullptr, |
michael@0 | 540 | getter_AddRefs(table), |
michael@0 | 541 | getter_AddRefs(curCell), |
michael@0 | 542 | nullptr, nullptr, |
michael@0 | 543 | &startRowIndex, &startColIndex); |
michael@0 | 544 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 545 | // Don't fail if no cell found |
michael@0 | 546 | NS_ENSURE_TRUE(curCell, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 547 | |
michael@0 | 548 | // Get more data for current cell in row we are inserting at (we need COLSPAN) |
michael@0 | 549 | int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 550 | bool isSelected; |
michael@0 | 551 | res = GetCellDataAt(table, startRowIndex, startColIndex, |
michael@0 | 552 | getter_AddRefs(curCell), |
michael@0 | 553 | &curStartRowIndex, &curStartColIndex, |
michael@0 | 554 | &rowSpan, &colSpan, |
michael@0 | 555 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 556 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 557 | NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE); |
michael@0 | 558 | |
michael@0 | 559 | int32_t rowCount, colCount; |
michael@0 | 560 | res = GetTableSize(table, &rowCount, &colCount); |
michael@0 | 561 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 562 | |
michael@0 | 563 | nsAutoEditBatch beginBatching(this); |
michael@0 | 564 | // Prevent auto insertion of BR in new cell until we're done |
michael@0 | 565 | nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); |
michael@0 | 566 | |
michael@0 | 567 | if (aAfter) |
michael@0 | 568 | { |
michael@0 | 569 | // Use row after current cell |
michael@0 | 570 | startRowIndex += actualRowSpan; |
michael@0 | 571 | |
michael@0 | 572 | //Detect when user is adding after a ROWSPAN=0 case |
michael@0 | 573 | // Assume they want to stop the "0" behavior and |
michael@0 | 574 | // really add a new row. Thus we set the |
michael@0 | 575 | // rowspan to its true value |
michael@0 | 576 | if (rowSpan == 0) |
michael@0 | 577 | SetRowSpan(curCell, actualRowSpan); |
michael@0 | 578 | } |
michael@0 | 579 | |
michael@0 | 580 | //We control selection resetting after the insert... |
michael@0 | 581 | nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); |
michael@0 | 582 | //...so suppress Rules System selection munging |
michael@0 | 583 | nsAutoTxnsConserveSelection dontChangeSelection(this); |
michael@0 | 584 | |
michael@0 | 585 | nsCOMPtr<nsIDOMElement> cellForRowParent; |
michael@0 | 586 | int32_t cellsInRow = 0; |
michael@0 | 587 | if (startRowIndex < rowCount) |
michael@0 | 588 | { |
michael@0 | 589 | // We are inserting above an existing row |
michael@0 | 590 | // Get each cell in the insert row to adjust for COLSPAN effects while we |
michael@0 | 591 | // count how many cells are needed |
michael@0 | 592 | int32_t colIndex = 0; |
michael@0 | 593 | // This returns NS_TABLELAYOUT_CELL_NOT_FOUND when we run past end of row, |
michael@0 | 594 | // which passes the NS_SUCCEEDED macro |
michael@0 | 595 | while ( NS_OK == GetCellDataAt(table, startRowIndex, colIndex, |
michael@0 | 596 | getter_AddRefs(curCell), |
michael@0 | 597 | &curStartRowIndex, &curStartColIndex, |
michael@0 | 598 | &rowSpan, &colSpan, |
michael@0 | 599 | &actualRowSpan, &actualColSpan, |
michael@0 | 600 | &isSelected) ) |
michael@0 | 601 | { |
michael@0 | 602 | if (curCell) |
michael@0 | 603 | { |
michael@0 | 604 | if (curStartRowIndex < startRowIndex) |
michael@0 | 605 | { |
michael@0 | 606 | // We have a cell spanning this location |
michael@0 | 607 | // Simply increase its rowspan |
michael@0 | 608 | //Note that if rowSpan == 0, we do nothing, |
michael@0 | 609 | // since that cell should automatically extend into the new row |
michael@0 | 610 | if (rowSpan > 0) |
michael@0 | 611 | SetRowSpan(curCell, rowSpan+aNumber); |
michael@0 | 612 | } else { |
michael@0 | 613 | // We have a cell in the insert row |
michael@0 | 614 | |
michael@0 | 615 | // Count the number of cells we need to add to the new row |
michael@0 | 616 | cellsInRow += actualColSpan; |
michael@0 | 617 | |
michael@0 | 618 | // Save cell we will use below |
michael@0 | 619 | if (!cellForRowParent) |
michael@0 | 620 | cellForRowParent = curCell; |
michael@0 | 621 | } |
michael@0 | 622 | // Next cell in row |
michael@0 | 623 | colIndex += actualColSpan; |
michael@0 | 624 | } |
michael@0 | 625 | else |
michael@0 | 626 | colIndex++; |
michael@0 | 627 | } |
michael@0 | 628 | } else { |
michael@0 | 629 | // We are adding a new row after all others |
michael@0 | 630 | // If it weren't for colspan=0 effect, |
michael@0 | 631 | // we could simply use colCount for number of new cells... |
michael@0 | 632 | cellsInRow = colCount; |
michael@0 | 633 | |
michael@0 | 634 | // ...but we must compensate for all cells with rowSpan = 0 in the last row |
michael@0 | 635 | int32_t lastRow = rowCount-1; |
michael@0 | 636 | int32_t tempColIndex = 0; |
michael@0 | 637 | while ( NS_OK == GetCellDataAt(table, lastRow, tempColIndex, |
michael@0 | 638 | getter_AddRefs(curCell), |
michael@0 | 639 | &curStartRowIndex, &curStartColIndex, |
michael@0 | 640 | &rowSpan, &colSpan, |
michael@0 | 641 | &actualRowSpan, &actualColSpan, |
michael@0 | 642 | &isSelected) ) |
michael@0 | 643 | { |
michael@0 | 644 | if (rowSpan == 0) |
michael@0 | 645 | cellsInRow -= actualColSpan; |
michael@0 | 646 | |
michael@0 | 647 | tempColIndex += actualColSpan; |
michael@0 | 648 | |
michael@0 | 649 | // Save cell from the last row that we will use below |
michael@0 | 650 | if (!cellForRowParent && curStartRowIndex == lastRow) |
michael@0 | 651 | cellForRowParent = curCell; |
michael@0 | 652 | } |
michael@0 | 653 | } |
michael@0 | 654 | |
michael@0 | 655 | if (cellsInRow > 0) |
michael@0 | 656 | { |
michael@0 | 657 | // The row parent and offset where we will insert new row |
michael@0 | 658 | nsCOMPtr<nsIDOMNode> parentOfRow; |
michael@0 | 659 | int32_t newRowOffset; |
michael@0 | 660 | |
michael@0 | 661 | NS_NAMED_LITERAL_STRING(trStr, "tr"); |
michael@0 | 662 | if (cellForRowParent) |
michael@0 | 663 | { |
michael@0 | 664 | nsCOMPtr<nsIDOMElement> parentRow; |
michael@0 | 665 | res = GetElementOrParentByTagName(trStr, cellForRowParent, getter_AddRefs(parentRow)); |
michael@0 | 666 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 667 | NS_ENSURE_TRUE(parentRow, NS_ERROR_NULL_POINTER); |
michael@0 | 668 | |
michael@0 | 669 | parentRow->GetParentNode(getter_AddRefs(parentOfRow)); |
michael@0 | 670 | NS_ENSURE_TRUE(parentOfRow, NS_ERROR_NULL_POINTER); |
michael@0 | 671 | |
michael@0 | 672 | newRowOffset = GetChildOffset(parentRow, parentOfRow); |
michael@0 | 673 | |
michael@0 | 674 | // Adjust for when adding past the end |
michael@0 | 675 | if (aAfter && startRowIndex >= rowCount) |
michael@0 | 676 | newRowOffset++; |
michael@0 | 677 | } |
michael@0 | 678 | else |
michael@0 | 679 | return NS_ERROR_FAILURE; |
michael@0 | 680 | |
michael@0 | 681 | for (int32_t row = 0; row < aNumber; row++) |
michael@0 | 682 | { |
michael@0 | 683 | // Create a new row |
michael@0 | 684 | nsCOMPtr<nsIDOMElement> newRow; |
michael@0 | 685 | res = CreateElementWithDefaults(trStr, getter_AddRefs(newRow)); |
michael@0 | 686 | if (NS_SUCCEEDED(res)) |
michael@0 | 687 | { |
michael@0 | 688 | NS_ENSURE_TRUE(newRow, NS_ERROR_FAILURE); |
michael@0 | 689 | |
michael@0 | 690 | for (int32_t i = 0; i < cellsInRow; i++) |
michael@0 | 691 | { |
michael@0 | 692 | nsCOMPtr<nsIDOMElement> newCell; |
michael@0 | 693 | res = CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell)); |
michael@0 | 694 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 695 | NS_ENSURE_TRUE(newCell, NS_ERROR_FAILURE); |
michael@0 | 696 | |
michael@0 | 697 | // Don't use transaction system yet! (not until entire row is inserted) |
michael@0 | 698 | nsCOMPtr<nsIDOMNode>resultNode; |
michael@0 | 699 | res = newRow->AppendChild(newCell, getter_AddRefs(resultNode)); |
michael@0 | 700 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 701 | } |
michael@0 | 702 | // Use transaction system to insert the entire row+cells |
michael@0 | 703 | // (Note that rows are inserted at same childoffset each time) |
michael@0 | 704 | res = InsertNode(newRow, parentOfRow, newRowOffset); |
michael@0 | 705 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 706 | } |
michael@0 | 707 | } |
michael@0 | 708 | } |
michael@0 | 709 | return res; |
michael@0 | 710 | } |
michael@0 | 711 | |
michael@0 | 712 | // Editor helper only |
michael@0 | 713 | // XXX Code changed for bug 217717 and now we don't need aSelection param |
michael@0 | 714 | // TODO: Remove aSelection param |
michael@0 | 715 | NS_IMETHODIMP |
michael@0 | 716 | nsHTMLEditor::DeleteTable2(nsIDOMElement *aTable, nsISelection *aSelection) |
michael@0 | 717 | { |
michael@0 | 718 | NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); |
michael@0 | 719 | |
michael@0 | 720 | // Select the table |
michael@0 | 721 | nsresult res = ClearSelection(); |
michael@0 | 722 | if (NS_SUCCEEDED(res)) |
michael@0 | 723 | res = AppendNodeToSelectionAsRange(aTable); |
michael@0 | 724 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 725 | |
michael@0 | 726 | return DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip); |
michael@0 | 727 | } |
michael@0 | 728 | |
michael@0 | 729 | NS_IMETHODIMP |
michael@0 | 730 | nsHTMLEditor::DeleteTable() |
michael@0 | 731 | { |
michael@0 | 732 | nsCOMPtr<nsISelection> selection; |
michael@0 | 733 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 734 | nsresult res = GetCellContext(getter_AddRefs(selection), |
michael@0 | 735 | getter_AddRefs(table), |
michael@0 | 736 | nullptr, nullptr, nullptr, nullptr, nullptr); |
michael@0 | 737 | |
michael@0 | 738 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 739 | |
michael@0 | 740 | nsAutoEditBatch beginBatching(this); |
michael@0 | 741 | return DeleteTable2(table, selection); |
michael@0 | 742 | } |
michael@0 | 743 | |
michael@0 | 744 | NS_IMETHODIMP |
michael@0 | 745 | nsHTMLEditor::DeleteTableCell(int32_t aNumber) |
michael@0 | 746 | { |
michael@0 | 747 | nsCOMPtr<nsISelection> selection; |
michael@0 | 748 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 749 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 750 | int32_t startRowIndex, startColIndex; |
michael@0 | 751 | |
michael@0 | 752 | |
michael@0 | 753 | nsresult res = GetCellContext(getter_AddRefs(selection), |
michael@0 | 754 | getter_AddRefs(table), |
michael@0 | 755 | getter_AddRefs(cell), |
michael@0 | 756 | nullptr, nullptr, |
michael@0 | 757 | &startRowIndex, &startColIndex); |
michael@0 | 758 | |
michael@0 | 759 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 760 | // Don't fail if we didn't find a table or cell |
michael@0 | 761 | NS_ENSURE_TRUE(table && cell, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 762 | |
michael@0 | 763 | nsAutoEditBatch beginBatching(this); |
michael@0 | 764 | // Prevent rules testing until we're done |
michael@0 | 765 | nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); |
michael@0 | 766 | |
michael@0 | 767 | nsCOMPtr<nsIDOMElement> firstCell; |
michael@0 | 768 | nsCOMPtr<nsIDOMRange> range; |
michael@0 | 769 | res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell)); |
michael@0 | 770 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 771 | |
michael@0 | 772 | int32_t rangeCount; |
michael@0 | 773 | res = selection->GetRangeCount(&rangeCount); |
michael@0 | 774 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 775 | |
michael@0 | 776 | if (firstCell && rangeCount > 1) |
michael@0 | 777 | { |
michael@0 | 778 | // When > 1 selected cell, |
michael@0 | 779 | // ignore aNumber and use selected cells |
michael@0 | 780 | cell = firstCell; |
michael@0 | 781 | |
michael@0 | 782 | int32_t rowCount, colCount; |
michael@0 | 783 | res = GetTableSize(table, &rowCount, &colCount); |
michael@0 | 784 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 785 | |
michael@0 | 786 | // Get indexes -- may be different than original cell |
michael@0 | 787 | res = GetCellIndexes(cell, &startRowIndex, &startColIndex); |
michael@0 | 788 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 789 | |
michael@0 | 790 | // The setCaret object will call SetSelectionAfterTableEdit in its destructor |
michael@0 | 791 | nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); |
michael@0 | 792 | nsAutoTxnsConserveSelection dontChangeSelection(this); |
michael@0 | 793 | |
michael@0 | 794 | bool checkToDeleteRow = true; |
michael@0 | 795 | bool checkToDeleteColumn = true; |
michael@0 | 796 | while (cell) |
michael@0 | 797 | { |
michael@0 | 798 | bool deleteRow = false; |
michael@0 | 799 | bool deleteCol = false; |
michael@0 | 800 | |
michael@0 | 801 | if (checkToDeleteRow) |
michael@0 | 802 | { |
michael@0 | 803 | // Optimize to delete an entire row |
michael@0 | 804 | // Clear so we don't repeat AllCellsInRowSelected within the same row |
michael@0 | 805 | checkToDeleteRow = false; |
michael@0 | 806 | |
michael@0 | 807 | deleteRow = AllCellsInRowSelected(table, startRowIndex, colCount); |
michael@0 | 808 | if (deleteRow) |
michael@0 | 809 | { |
michael@0 | 810 | // First, find the next cell in a different row |
michael@0 | 811 | // to continue after we delete this row |
michael@0 | 812 | int32_t nextRow = startRowIndex; |
michael@0 | 813 | while (nextRow == startRowIndex) |
michael@0 | 814 | { |
michael@0 | 815 | res = GetNextSelectedCell(nullptr, getter_AddRefs(cell)); |
michael@0 | 816 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 817 | if (!cell) break; |
michael@0 | 818 | res = GetCellIndexes(cell, &nextRow, &startColIndex); |
michael@0 | 819 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 820 | } |
michael@0 | 821 | // Delete entire row |
michael@0 | 822 | res = DeleteRow(table, startRowIndex); |
michael@0 | 823 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 824 | |
michael@0 | 825 | if (cell) |
michael@0 | 826 | { |
michael@0 | 827 | // For the next cell: Subtract 1 for row we deleted |
michael@0 | 828 | startRowIndex = nextRow - 1; |
michael@0 | 829 | // Set true since we know we will look at a new row next |
michael@0 | 830 | checkToDeleteRow = true; |
michael@0 | 831 | } |
michael@0 | 832 | } |
michael@0 | 833 | } |
michael@0 | 834 | if (!deleteRow) |
michael@0 | 835 | { |
michael@0 | 836 | if (checkToDeleteColumn) |
michael@0 | 837 | { |
michael@0 | 838 | // Optimize to delete an entire column |
michael@0 | 839 | // Clear this so we don't repeat AllCellsInColSelected within the same Col |
michael@0 | 840 | checkToDeleteColumn = false; |
michael@0 | 841 | |
michael@0 | 842 | deleteCol = AllCellsInColumnSelected(table, startColIndex, colCount); |
michael@0 | 843 | if (deleteCol) |
michael@0 | 844 | { |
michael@0 | 845 | // First, find the next cell in a different column |
michael@0 | 846 | // to continue after we delete this column |
michael@0 | 847 | int32_t nextCol = startColIndex; |
michael@0 | 848 | while (nextCol == startColIndex) |
michael@0 | 849 | { |
michael@0 | 850 | res = GetNextSelectedCell(nullptr, getter_AddRefs(cell)); |
michael@0 | 851 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 852 | if (!cell) break; |
michael@0 | 853 | res = GetCellIndexes(cell, &startRowIndex, &nextCol); |
michael@0 | 854 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 855 | } |
michael@0 | 856 | // Delete entire Col |
michael@0 | 857 | res = DeleteColumn(table, startColIndex); |
michael@0 | 858 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 859 | if (cell) |
michael@0 | 860 | { |
michael@0 | 861 | // For the next cell, subtract 1 for col. deleted |
michael@0 | 862 | startColIndex = nextCol - 1; |
michael@0 | 863 | // Set true since we know we will look at a new column next |
michael@0 | 864 | checkToDeleteColumn = true; |
michael@0 | 865 | } |
michael@0 | 866 | } |
michael@0 | 867 | } |
michael@0 | 868 | if (!deleteCol) |
michael@0 | 869 | { |
michael@0 | 870 | // First get the next cell to delete |
michael@0 | 871 | nsCOMPtr<nsIDOMElement> nextCell; |
michael@0 | 872 | res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(nextCell)); |
michael@0 | 873 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 874 | |
michael@0 | 875 | // Then delete the cell |
michael@0 | 876 | res = DeleteNode(cell); |
michael@0 | 877 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 878 | |
michael@0 | 879 | // The next cell to delete |
michael@0 | 880 | cell = nextCell; |
michael@0 | 881 | if (cell) |
michael@0 | 882 | { |
michael@0 | 883 | res = GetCellIndexes(cell, &startRowIndex, &startColIndex); |
michael@0 | 884 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 885 | } |
michael@0 | 886 | } |
michael@0 | 887 | } |
michael@0 | 888 | } |
michael@0 | 889 | } |
michael@0 | 890 | else for (int32_t i = 0; i < aNumber; i++) |
michael@0 | 891 | { |
michael@0 | 892 | res = GetCellContext(getter_AddRefs(selection), |
michael@0 | 893 | getter_AddRefs(table), |
michael@0 | 894 | getter_AddRefs(cell), |
michael@0 | 895 | nullptr, nullptr, |
michael@0 | 896 | &startRowIndex, &startColIndex); |
michael@0 | 897 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 898 | // Don't fail if no cell found |
michael@0 | 899 | NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 900 | |
michael@0 | 901 | if (1 == GetNumberOfCellsInRow(table, startRowIndex)) |
michael@0 | 902 | { |
michael@0 | 903 | nsCOMPtr<nsIDOMElement> parentRow; |
michael@0 | 904 | res = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cell, getter_AddRefs(parentRow)); |
michael@0 | 905 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 906 | NS_ENSURE_TRUE(parentRow, NS_ERROR_NULL_POINTER); |
michael@0 | 907 | |
michael@0 | 908 | // We should delete the row instead, |
michael@0 | 909 | // but first check if its the only row left |
michael@0 | 910 | // so we can delete the entire table |
michael@0 | 911 | int32_t rowCount, colCount; |
michael@0 | 912 | res = GetTableSize(table, &rowCount, &colCount); |
michael@0 | 913 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 914 | |
michael@0 | 915 | if (rowCount == 1) |
michael@0 | 916 | return DeleteTable2(table, selection); |
michael@0 | 917 | |
michael@0 | 918 | // We need to call DeleteTableRow to handle cells with rowspan |
michael@0 | 919 | res = DeleteTableRow(1); |
michael@0 | 920 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 921 | } |
michael@0 | 922 | else |
michael@0 | 923 | { |
michael@0 | 924 | // More than 1 cell in the row |
michael@0 | 925 | |
michael@0 | 926 | // The setCaret object will call SetSelectionAfterTableEdit in its destructor |
michael@0 | 927 | nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); |
michael@0 | 928 | nsAutoTxnsConserveSelection dontChangeSelection(this); |
michael@0 | 929 | |
michael@0 | 930 | res = DeleteNode(cell); |
michael@0 | 931 | // If we fail, don't try to delete any more cells??? |
michael@0 | 932 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 933 | } |
michael@0 | 934 | } |
michael@0 | 935 | return NS_OK; |
michael@0 | 936 | } |
michael@0 | 937 | |
michael@0 | 938 | NS_IMETHODIMP |
michael@0 | 939 | nsHTMLEditor::DeleteTableCellContents() |
michael@0 | 940 | { |
michael@0 | 941 | nsCOMPtr<nsISelection> selection; |
michael@0 | 942 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 943 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 944 | int32_t startRowIndex, startColIndex; |
michael@0 | 945 | nsresult res; |
michael@0 | 946 | res = GetCellContext(getter_AddRefs(selection), |
michael@0 | 947 | getter_AddRefs(table), |
michael@0 | 948 | getter_AddRefs(cell), |
michael@0 | 949 | nullptr, nullptr, |
michael@0 | 950 | &startRowIndex, &startColIndex); |
michael@0 | 951 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 952 | // Don't fail if no cell found |
michael@0 | 953 | NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 954 | |
michael@0 | 955 | |
michael@0 | 956 | nsAutoEditBatch beginBatching(this); |
michael@0 | 957 | // Prevent rules testing until we're done |
michael@0 | 958 | nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); |
michael@0 | 959 | //Don't let Rules System change the selection |
michael@0 | 960 | nsAutoTxnsConserveSelection dontChangeSelection(this); |
michael@0 | 961 | |
michael@0 | 962 | |
michael@0 | 963 | nsCOMPtr<nsIDOMElement> firstCell; |
michael@0 | 964 | nsCOMPtr<nsIDOMRange> range; |
michael@0 | 965 | res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell)); |
michael@0 | 966 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 967 | |
michael@0 | 968 | |
michael@0 | 969 | if (firstCell) |
michael@0 | 970 | { |
michael@0 | 971 | cell = firstCell; |
michael@0 | 972 | res = GetCellIndexes(cell, &startRowIndex, &startColIndex); |
michael@0 | 973 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 974 | } |
michael@0 | 975 | |
michael@0 | 976 | nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); |
michael@0 | 977 | |
michael@0 | 978 | while (cell) |
michael@0 | 979 | { |
michael@0 | 980 | DeleteCellContents(cell); |
michael@0 | 981 | if (firstCell) |
michael@0 | 982 | { |
michael@0 | 983 | // We doing a selected cells, so do all of them |
michael@0 | 984 | res = GetNextSelectedCell(nullptr, getter_AddRefs(cell)); |
michael@0 | 985 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 986 | } |
michael@0 | 987 | else |
michael@0 | 988 | cell = nullptr; |
michael@0 | 989 | } |
michael@0 | 990 | return NS_OK; |
michael@0 | 991 | } |
michael@0 | 992 | |
michael@0 | 993 | NS_IMETHODIMP |
michael@0 | 994 | nsHTMLEditor::DeleteCellContents(nsIDOMElement *aCell) |
michael@0 | 995 | { |
michael@0 | 996 | NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); |
michael@0 | 997 | |
michael@0 | 998 | // Prevent rules testing until we're done |
michael@0 | 999 | nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); |
michael@0 | 1000 | |
michael@0 | 1001 | nsCOMPtr<nsIDOMNode> child; |
michael@0 | 1002 | bool hasChild; |
michael@0 | 1003 | aCell->HasChildNodes(&hasChild); |
michael@0 | 1004 | |
michael@0 | 1005 | while (hasChild) |
michael@0 | 1006 | { |
michael@0 | 1007 | aCell->GetLastChild(getter_AddRefs(child)); |
michael@0 | 1008 | nsresult res = DeleteNode(child); |
michael@0 | 1009 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1010 | aCell->HasChildNodes(&hasChild); |
michael@0 | 1011 | } |
michael@0 | 1012 | return NS_OK; |
michael@0 | 1013 | } |
michael@0 | 1014 | |
michael@0 | 1015 | NS_IMETHODIMP |
michael@0 | 1016 | nsHTMLEditor::DeleteTableColumn(int32_t aNumber) |
michael@0 | 1017 | { |
michael@0 | 1018 | nsCOMPtr<nsISelection> selection; |
michael@0 | 1019 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 1020 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 1021 | int32_t startRowIndex, startColIndex, rowCount, colCount; |
michael@0 | 1022 | nsresult res = GetCellContext(getter_AddRefs(selection), |
michael@0 | 1023 | getter_AddRefs(table), |
michael@0 | 1024 | getter_AddRefs(cell), |
michael@0 | 1025 | nullptr, nullptr, |
michael@0 | 1026 | &startRowIndex, &startColIndex); |
michael@0 | 1027 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1028 | // Don't fail if no cell found |
michael@0 | 1029 | NS_ENSURE_TRUE(table && cell, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 1030 | |
michael@0 | 1031 | res = GetTableSize(table, &rowCount, &colCount); |
michael@0 | 1032 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1033 | |
michael@0 | 1034 | // Shortcut the case of deleting all columns in table |
michael@0 | 1035 | if(startColIndex == 0 && aNumber >= colCount) |
michael@0 | 1036 | return DeleteTable2(table, selection); |
michael@0 | 1037 | |
michael@0 | 1038 | // Check for counts too high |
michael@0 | 1039 | aNumber = std::min(aNumber,(colCount-startColIndex)); |
michael@0 | 1040 | |
michael@0 | 1041 | nsAutoEditBatch beginBatching(this); |
michael@0 | 1042 | // Prevent rules testing until we're done |
michael@0 | 1043 | nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); |
michael@0 | 1044 | |
michael@0 | 1045 | // Test if deletion is controlled by selected cells |
michael@0 | 1046 | nsCOMPtr<nsIDOMElement> firstCell; |
michael@0 | 1047 | nsCOMPtr<nsIDOMRange> range; |
michael@0 | 1048 | res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell)); |
michael@0 | 1049 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1050 | |
michael@0 | 1051 | int32_t rangeCount; |
michael@0 | 1052 | res = selection->GetRangeCount(&rangeCount); |
michael@0 | 1053 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1054 | |
michael@0 | 1055 | if (firstCell && rangeCount > 1) |
michael@0 | 1056 | { |
michael@0 | 1057 | // Fetch indexes again - may be different for selected cells |
michael@0 | 1058 | res = GetCellIndexes(firstCell, &startRowIndex, &startColIndex); |
michael@0 | 1059 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1060 | } |
michael@0 | 1061 | //We control selection resetting after the insert... |
michael@0 | 1062 | nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow, false); |
michael@0 | 1063 | |
michael@0 | 1064 | if (firstCell && rangeCount > 1) |
michael@0 | 1065 | { |
michael@0 | 1066 | // Use selected cells to determine what rows to delete |
michael@0 | 1067 | cell = firstCell; |
michael@0 | 1068 | |
michael@0 | 1069 | while (cell) |
michael@0 | 1070 | { |
michael@0 | 1071 | if (cell != firstCell) |
michael@0 | 1072 | { |
michael@0 | 1073 | res = GetCellIndexes(cell, &startRowIndex, &startColIndex); |
michael@0 | 1074 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1075 | } |
michael@0 | 1076 | // Find the next cell in a different column |
michael@0 | 1077 | // to continue after we delete this column |
michael@0 | 1078 | int32_t nextCol = startColIndex; |
michael@0 | 1079 | while (nextCol == startColIndex) |
michael@0 | 1080 | { |
michael@0 | 1081 | res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell)); |
michael@0 | 1082 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1083 | if (!cell) break; |
michael@0 | 1084 | res = GetCellIndexes(cell, &startRowIndex, &nextCol); |
michael@0 | 1085 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1086 | } |
michael@0 | 1087 | res = DeleteColumn(table, startColIndex); |
michael@0 | 1088 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1089 | } |
michael@0 | 1090 | } |
michael@0 | 1091 | else for (int32_t i = 0; i < aNumber; i++) |
michael@0 | 1092 | { |
michael@0 | 1093 | res = DeleteColumn(table, startColIndex); |
michael@0 | 1094 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1095 | } |
michael@0 | 1096 | return NS_OK; |
michael@0 | 1097 | } |
michael@0 | 1098 | |
michael@0 | 1099 | NS_IMETHODIMP |
michael@0 | 1100 | nsHTMLEditor::DeleteColumn(nsIDOMElement *aTable, int32_t aColIndex) |
michael@0 | 1101 | { |
michael@0 | 1102 | NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); |
michael@0 | 1103 | |
michael@0 | 1104 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 1105 | nsCOMPtr<nsIDOMElement> cellInDeleteCol; |
michael@0 | 1106 | int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 1107 | bool isSelected; |
michael@0 | 1108 | int32_t rowIndex = 0; |
michael@0 | 1109 | nsresult res = NS_OK; |
michael@0 | 1110 | |
michael@0 | 1111 | do { |
michael@0 | 1112 | res = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell), |
michael@0 | 1113 | &startRowIndex, &startColIndex, &rowSpan, &colSpan, |
michael@0 | 1114 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 1115 | |
michael@0 | 1116 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1117 | |
michael@0 | 1118 | if (cell) |
michael@0 | 1119 | { |
michael@0 | 1120 | // Find cells that don't start in column we are deleting |
michael@0 | 1121 | if (startColIndex < aColIndex || colSpan > 1 || colSpan == 0) |
michael@0 | 1122 | { |
michael@0 | 1123 | // We have a cell spanning this location |
michael@0 | 1124 | // Decrease its colspan to keep table rectangular, |
michael@0 | 1125 | // but if colSpan=0, it will adjust automatically |
michael@0 | 1126 | if (colSpan > 0) |
michael@0 | 1127 | { |
michael@0 | 1128 | NS_ASSERTION((colSpan > 1),"Bad COLSPAN in DeleteTableColumn"); |
michael@0 | 1129 | SetColSpan(cell, colSpan-1); |
michael@0 | 1130 | } |
michael@0 | 1131 | if (startColIndex == aColIndex) |
michael@0 | 1132 | { |
michael@0 | 1133 | // Cell is in column to be deleted, but must have colspan > 1, |
michael@0 | 1134 | // so delete contents of cell instead of cell itself |
michael@0 | 1135 | // (We must have reset colspan above) |
michael@0 | 1136 | DeleteCellContents(cell); |
michael@0 | 1137 | } |
michael@0 | 1138 | // To next cell in column |
michael@0 | 1139 | rowIndex += actualRowSpan; |
michael@0 | 1140 | } |
michael@0 | 1141 | else |
michael@0 | 1142 | { |
michael@0 | 1143 | // Delete the cell |
michael@0 | 1144 | if (1 == GetNumberOfCellsInRow(aTable, rowIndex)) |
michael@0 | 1145 | { |
michael@0 | 1146 | // Only 1 cell in row - delete the row |
michael@0 | 1147 | nsCOMPtr<nsIDOMElement> parentRow; |
michael@0 | 1148 | res = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cell, getter_AddRefs(parentRow)); |
michael@0 | 1149 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1150 | if(!parentRow) return NS_ERROR_NULL_POINTER; |
michael@0 | 1151 | |
michael@0 | 1152 | // But first check if its the only row left |
michael@0 | 1153 | // so we can delete the entire table |
michael@0 | 1154 | // (This should never happen but it's the safe thing to do) |
michael@0 | 1155 | int32_t rowCount, colCount; |
michael@0 | 1156 | res = GetTableSize(aTable, &rowCount, &colCount); |
michael@0 | 1157 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1158 | |
michael@0 | 1159 | if (rowCount == 1) |
michael@0 | 1160 | { |
michael@0 | 1161 | nsCOMPtr<nsISelection> selection; |
michael@0 | 1162 | res = GetSelection(getter_AddRefs(selection)); |
michael@0 | 1163 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1164 | NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); |
michael@0 | 1165 | return DeleteTable2(aTable, selection); |
michael@0 | 1166 | } |
michael@0 | 1167 | |
michael@0 | 1168 | // Delete the row by placing caret in cell we were to delete |
michael@0 | 1169 | // We need to call DeleteTableRow to handle cells with rowspan |
michael@0 | 1170 | res = DeleteRow(aTable, startRowIndex); |
michael@0 | 1171 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1172 | |
michael@0 | 1173 | // Note that we don't incremenet rowIndex |
michael@0 | 1174 | // since a row was deleted and "next" |
michael@0 | 1175 | // row now has current rowIndex |
michael@0 | 1176 | } |
michael@0 | 1177 | else |
michael@0 | 1178 | { |
michael@0 | 1179 | // A more "normal" deletion |
michael@0 | 1180 | res = DeleteNode(cell); |
michael@0 | 1181 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1182 | |
michael@0 | 1183 | //Skip over any rows spanned by this cell |
michael@0 | 1184 | rowIndex += actualRowSpan; |
michael@0 | 1185 | } |
michael@0 | 1186 | } |
michael@0 | 1187 | } |
michael@0 | 1188 | } while (cell); |
michael@0 | 1189 | |
michael@0 | 1190 | return NS_OK; |
michael@0 | 1191 | } |
michael@0 | 1192 | |
michael@0 | 1193 | NS_IMETHODIMP |
michael@0 | 1194 | nsHTMLEditor::DeleteTableRow(int32_t aNumber) |
michael@0 | 1195 | { |
michael@0 | 1196 | nsCOMPtr<nsISelection> selection; |
michael@0 | 1197 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 1198 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 1199 | int32_t startRowIndex, startColIndex; |
michael@0 | 1200 | int32_t rowCount, colCount; |
michael@0 | 1201 | nsresult res = GetCellContext(getter_AddRefs(selection), |
michael@0 | 1202 | getter_AddRefs(table), |
michael@0 | 1203 | getter_AddRefs(cell), |
michael@0 | 1204 | nullptr, nullptr, |
michael@0 | 1205 | &startRowIndex, &startColIndex); |
michael@0 | 1206 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1207 | // Don't fail if no cell found |
michael@0 | 1208 | NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 1209 | |
michael@0 | 1210 | res = GetTableSize(table, &rowCount, &colCount); |
michael@0 | 1211 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1212 | |
michael@0 | 1213 | // Shortcut the case of deleting all rows in table |
michael@0 | 1214 | if(startRowIndex == 0 && aNumber >= rowCount) |
michael@0 | 1215 | return DeleteTable2(table, selection); |
michael@0 | 1216 | |
michael@0 | 1217 | nsAutoEditBatch beginBatching(this); |
michael@0 | 1218 | // Prevent rules testing until we're done |
michael@0 | 1219 | nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); |
michael@0 | 1220 | |
michael@0 | 1221 | nsCOMPtr<nsIDOMElement> firstCell; |
michael@0 | 1222 | nsCOMPtr<nsIDOMRange> range; |
michael@0 | 1223 | res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell)); |
michael@0 | 1224 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1225 | |
michael@0 | 1226 | int32_t rangeCount; |
michael@0 | 1227 | res = selection->GetRangeCount(&rangeCount); |
michael@0 | 1228 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1229 | |
michael@0 | 1230 | if (firstCell && rangeCount > 1) |
michael@0 | 1231 | { |
michael@0 | 1232 | // Fetch indexes again - may be different for selected cells |
michael@0 | 1233 | res = GetCellIndexes(firstCell, &startRowIndex, &startColIndex); |
michael@0 | 1234 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1235 | } |
michael@0 | 1236 | |
michael@0 | 1237 | //We control selection resetting after the insert... |
michael@0 | 1238 | nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow, false); |
michael@0 | 1239 | // Don't change selection during deletions |
michael@0 | 1240 | nsAutoTxnsConserveSelection dontChangeSelection(this); |
michael@0 | 1241 | |
michael@0 | 1242 | if (firstCell && rangeCount > 1) |
michael@0 | 1243 | { |
michael@0 | 1244 | // Use selected cells to determine what rows to delete |
michael@0 | 1245 | cell = firstCell; |
michael@0 | 1246 | |
michael@0 | 1247 | while (cell) |
michael@0 | 1248 | { |
michael@0 | 1249 | if (cell != firstCell) |
michael@0 | 1250 | { |
michael@0 | 1251 | res = GetCellIndexes(cell, &startRowIndex, &startColIndex); |
michael@0 | 1252 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1253 | } |
michael@0 | 1254 | // Find the next cell in a different row |
michael@0 | 1255 | // to continue after we delete this row |
michael@0 | 1256 | int32_t nextRow = startRowIndex; |
michael@0 | 1257 | while (nextRow == startRowIndex) |
michael@0 | 1258 | { |
michael@0 | 1259 | res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell)); |
michael@0 | 1260 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1261 | if (!cell) break; |
michael@0 | 1262 | res = GetCellIndexes(cell, &nextRow, &startColIndex); |
michael@0 | 1263 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1264 | } |
michael@0 | 1265 | // Delete entire row |
michael@0 | 1266 | res = DeleteRow(table, startRowIndex); |
michael@0 | 1267 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1268 | } |
michael@0 | 1269 | } |
michael@0 | 1270 | else |
michael@0 | 1271 | { |
michael@0 | 1272 | // Check for counts too high |
michael@0 | 1273 | aNumber = std::min(aNumber,(rowCount-startRowIndex)); |
michael@0 | 1274 | |
michael@0 | 1275 | for (int32_t i = 0; i < aNumber; i++) |
michael@0 | 1276 | { |
michael@0 | 1277 | res = DeleteRow(table, startRowIndex); |
michael@0 | 1278 | // If failed in current row, try the next |
michael@0 | 1279 | if (NS_FAILED(res)) |
michael@0 | 1280 | startRowIndex++; |
michael@0 | 1281 | |
michael@0 | 1282 | // Check if there's a cell in the "next" row |
michael@0 | 1283 | res = GetCellAt(table, startRowIndex, startColIndex, getter_AddRefs(cell)); |
michael@0 | 1284 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1285 | if(!cell) |
michael@0 | 1286 | break; |
michael@0 | 1287 | } |
michael@0 | 1288 | } |
michael@0 | 1289 | return NS_OK; |
michael@0 | 1290 | } |
michael@0 | 1291 | |
michael@0 | 1292 | // Helper that doesn't batch or change the selection |
michael@0 | 1293 | NS_IMETHODIMP |
michael@0 | 1294 | nsHTMLEditor::DeleteRow(nsIDOMElement *aTable, int32_t aRowIndex) |
michael@0 | 1295 | { |
michael@0 | 1296 | NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); |
michael@0 | 1297 | |
michael@0 | 1298 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 1299 | nsCOMPtr<nsIDOMElement> cellInDeleteRow; |
michael@0 | 1300 | int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 1301 | bool isSelected; |
michael@0 | 1302 | int32_t colIndex = 0; |
michael@0 | 1303 | nsresult res = NS_OK; |
michael@0 | 1304 | |
michael@0 | 1305 | // Prevent rules testing until we're done |
michael@0 | 1306 | nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); |
michael@0 | 1307 | |
michael@0 | 1308 | // The list of cells we will change rowspan in |
michael@0 | 1309 | // and the new rowspan values for each |
michael@0 | 1310 | nsTArray<nsCOMPtr<nsIDOMElement> > spanCellList; |
michael@0 | 1311 | nsTArray<int32_t> newSpanList; |
michael@0 | 1312 | |
michael@0 | 1313 | int32_t rowCount, colCount; |
michael@0 | 1314 | res = GetTableSize(aTable, &rowCount, &colCount); |
michael@0 | 1315 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1316 | |
michael@0 | 1317 | // Scan through cells in row to do rowspan adjustments |
michael@0 | 1318 | // Note that after we delete row, startRowIndex will point to the |
michael@0 | 1319 | // cells in the next row to be deleted |
michael@0 | 1320 | do { |
michael@0 | 1321 | if (aRowIndex >= rowCount || colIndex >= colCount) |
michael@0 | 1322 | break; |
michael@0 | 1323 | |
michael@0 | 1324 | res = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), |
michael@0 | 1325 | &startRowIndex, &startColIndex, &rowSpan, &colSpan, |
michael@0 | 1326 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 1327 | |
michael@0 | 1328 | // We don't fail if we don't find a cell, so this must be real bad |
michael@0 | 1329 | if(NS_FAILED(res)) return res; |
michael@0 | 1330 | |
michael@0 | 1331 | // Compensate for cells that don't start or extend below the row we are deleting |
michael@0 | 1332 | if (cell) |
michael@0 | 1333 | { |
michael@0 | 1334 | if (startRowIndex < aRowIndex) |
michael@0 | 1335 | { |
michael@0 | 1336 | // Cell starts in row above us |
michael@0 | 1337 | // Decrease its rowspan to keep table rectangular |
michael@0 | 1338 | // but we don't need to do this if rowspan=0, |
michael@0 | 1339 | // since it will automatically adjust |
michael@0 | 1340 | if (rowSpan > 0) |
michael@0 | 1341 | { |
michael@0 | 1342 | // Build list of cells to change rowspan |
michael@0 | 1343 | // We can't do it now since it upsets cell map, |
michael@0 | 1344 | // so we will do it after deleting the row |
michael@0 | 1345 | spanCellList.AppendElement(cell); |
michael@0 | 1346 | newSpanList.AppendElement(std::max((aRowIndex - startRowIndex), actualRowSpan-1)); |
michael@0 | 1347 | } |
michael@0 | 1348 | } |
michael@0 | 1349 | else |
michael@0 | 1350 | { |
michael@0 | 1351 | if (rowSpan > 1) |
michael@0 | 1352 | { |
michael@0 | 1353 | //Cell spans below row to delete, |
michael@0 | 1354 | // so we must insert new cells to keep rows below even |
michael@0 | 1355 | // Note that we test "rowSpan" so we don't do this if rowSpan = 0 (automatic readjustment) |
michael@0 | 1356 | res = SplitCellIntoRows(aTable, startRowIndex, startColIndex, |
michael@0 | 1357 | aRowIndex - startRowIndex + 1, // The row above the row to insert new cell into |
michael@0 | 1358 | actualRowSpan - 1, nullptr); // Span remaining below |
michael@0 | 1359 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1360 | } |
michael@0 | 1361 | if (!cellInDeleteRow) |
michael@0 | 1362 | cellInDeleteRow = cell; // Reference cell to find row to delete |
michael@0 | 1363 | } |
michael@0 | 1364 | // Skip over other columns spanned by this cell |
michael@0 | 1365 | colIndex += actualColSpan; |
michael@0 | 1366 | } |
michael@0 | 1367 | } while (cell); |
michael@0 | 1368 | |
michael@0 | 1369 | // Things are messed up if we didn't find a cell in the row! |
michael@0 | 1370 | NS_ENSURE_TRUE(cellInDeleteRow, NS_ERROR_FAILURE); |
michael@0 | 1371 | |
michael@0 | 1372 | // Delete the entire row |
michael@0 | 1373 | nsCOMPtr<nsIDOMElement> parentRow; |
michael@0 | 1374 | res = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cellInDeleteRow, getter_AddRefs(parentRow)); |
michael@0 | 1375 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1376 | |
michael@0 | 1377 | if (parentRow) |
michael@0 | 1378 | { |
michael@0 | 1379 | res = DeleteNode(parentRow); |
michael@0 | 1380 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1381 | } |
michael@0 | 1382 | |
michael@0 | 1383 | // Now we can set new rowspans for cells stored above |
michael@0 | 1384 | for (uint32_t i = 0, n = spanCellList.Length(); i < n; i++) |
michael@0 | 1385 | { |
michael@0 | 1386 | nsIDOMElement *cellPtr = spanCellList[i]; |
michael@0 | 1387 | if (cellPtr) |
michael@0 | 1388 | { |
michael@0 | 1389 | res = SetRowSpan(cellPtr, newSpanList[i]); |
michael@0 | 1390 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1391 | } |
michael@0 | 1392 | } |
michael@0 | 1393 | return NS_OK; |
michael@0 | 1394 | } |
michael@0 | 1395 | |
michael@0 | 1396 | |
michael@0 | 1397 | NS_IMETHODIMP |
michael@0 | 1398 | nsHTMLEditor::SelectTable() |
michael@0 | 1399 | { |
michael@0 | 1400 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 1401 | nsresult res = NS_ERROR_FAILURE; |
michael@0 | 1402 | res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr, getter_AddRefs(table)); |
michael@0 | 1403 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1404 | // Don't fail if we didn't find a table |
michael@0 | 1405 | NS_ENSURE_TRUE(table, NS_OK); |
michael@0 | 1406 | |
michael@0 | 1407 | res = ClearSelection(); |
michael@0 | 1408 | if (NS_SUCCEEDED(res)) |
michael@0 | 1409 | res = AppendNodeToSelectionAsRange(table); |
michael@0 | 1410 | |
michael@0 | 1411 | return res; |
michael@0 | 1412 | } |
michael@0 | 1413 | |
michael@0 | 1414 | NS_IMETHODIMP |
michael@0 | 1415 | nsHTMLEditor::SelectTableCell() |
michael@0 | 1416 | { |
michael@0 | 1417 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 1418 | nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); |
michael@0 | 1419 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1420 | NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 1421 | |
michael@0 | 1422 | res = ClearSelection(); |
michael@0 | 1423 | if (NS_SUCCEEDED(res)) |
michael@0 | 1424 | res = AppendNodeToSelectionAsRange(cell); |
michael@0 | 1425 | |
michael@0 | 1426 | return res; |
michael@0 | 1427 | } |
michael@0 | 1428 | |
michael@0 | 1429 | NS_IMETHODIMP |
michael@0 | 1430 | nsHTMLEditor::SelectBlockOfCells(nsIDOMElement *aStartCell, nsIDOMElement *aEndCell) |
michael@0 | 1431 | { |
michael@0 | 1432 | NS_ENSURE_TRUE(aStartCell && aEndCell, NS_ERROR_NULL_POINTER); |
michael@0 | 1433 | |
michael@0 | 1434 | nsCOMPtr<nsISelection> selection; |
michael@0 | 1435 | nsresult res = GetSelection(getter_AddRefs(selection)); |
michael@0 | 1436 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1437 | NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); |
michael@0 | 1438 | |
michael@0 | 1439 | NS_NAMED_LITERAL_STRING(tableStr, "table"); |
michael@0 | 1440 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 1441 | res = GetElementOrParentByTagName(tableStr, aStartCell, getter_AddRefs(table)); |
michael@0 | 1442 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1443 | NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); |
michael@0 | 1444 | |
michael@0 | 1445 | nsCOMPtr<nsIDOMElement> endTable; |
michael@0 | 1446 | res = GetElementOrParentByTagName(tableStr, aEndCell, getter_AddRefs(endTable)); |
michael@0 | 1447 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1448 | NS_ENSURE_TRUE(endTable, NS_ERROR_FAILURE); |
michael@0 | 1449 | |
michael@0 | 1450 | // We can only select a block if within the same table, |
michael@0 | 1451 | // so do nothing if not within one table |
michael@0 | 1452 | if (table != endTable) return NS_OK; |
michael@0 | 1453 | |
michael@0 | 1454 | int32_t startRowIndex, startColIndex, endRowIndex, endColIndex; |
michael@0 | 1455 | |
michael@0 | 1456 | // Get starting and ending cells' location in the cellmap |
michael@0 | 1457 | res = GetCellIndexes(aStartCell, &startRowIndex, &startColIndex); |
michael@0 | 1458 | if(NS_FAILED(res)) return res; |
michael@0 | 1459 | |
michael@0 | 1460 | res = GetCellIndexes(aEndCell, &endRowIndex, &endColIndex); |
michael@0 | 1461 | if(NS_FAILED(res)) return res; |
michael@0 | 1462 | |
michael@0 | 1463 | // Suppress nsISelectionListener notification |
michael@0 | 1464 | // until all selection changes are finished |
michael@0 | 1465 | nsSelectionBatcherForTable selectionBatcher(selection); |
michael@0 | 1466 | |
michael@0 | 1467 | // Examine all cell nodes in current selection and |
michael@0 | 1468 | // remove those outside the new block cell region |
michael@0 | 1469 | int32_t minColumn = std::min(startColIndex, endColIndex); |
michael@0 | 1470 | int32_t minRow = std::min(startRowIndex, endRowIndex); |
michael@0 | 1471 | int32_t maxColumn = std::max(startColIndex, endColIndex); |
michael@0 | 1472 | int32_t maxRow = std::max(startRowIndex, endRowIndex); |
michael@0 | 1473 | |
michael@0 | 1474 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 1475 | int32_t currentRowIndex, currentColIndex; |
michael@0 | 1476 | nsCOMPtr<nsIDOMRange> range; |
michael@0 | 1477 | res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(cell)); |
michael@0 | 1478 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1479 | if (res == NS_EDITOR_ELEMENT_NOT_FOUND) return NS_OK; |
michael@0 | 1480 | |
michael@0 | 1481 | while (cell) |
michael@0 | 1482 | { |
michael@0 | 1483 | res = GetCellIndexes(cell, ¤tRowIndex, ¤tColIndex); |
michael@0 | 1484 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1485 | |
michael@0 | 1486 | if (currentRowIndex < maxRow || currentRowIndex > maxRow || |
michael@0 | 1487 | currentColIndex < maxColumn || currentColIndex > maxColumn) |
michael@0 | 1488 | { |
michael@0 | 1489 | selection->RemoveRange(range); |
michael@0 | 1490 | // Since we've removed the range, decrement pointer to next range |
michael@0 | 1491 | mSelectedCellIndex--; |
michael@0 | 1492 | } |
michael@0 | 1493 | res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell)); |
michael@0 | 1494 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1495 | } |
michael@0 | 1496 | |
michael@0 | 1497 | int32_t rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 1498 | bool isSelected; |
michael@0 | 1499 | for (int32_t row = minRow; row <= maxRow; row++) |
michael@0 | 1500 | { |
michael@0 | 1501 | for(int32_t col = minColumn; col <= maxColumn; col += std::max(actualColSpan, 1)) |
michael@0 | 1502 | { |
michael@0 | 1503 | res = GetCellDataAt(table, row, col, getter_AddRefs(cell), |
michael@0 | 1504 | ¤tRowIndex, ¤tColIndex, |
michael@0 | 1505 | &rowSpan, &colSpan, |
michael@0 | 1506 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 1507 | if (NS_FAILED(res)) break; |
michael@0 | 1508 | // Skip cells that already selected or are spanned from previous locations |
michael@0 | 1509 | if (!isSelected && cell && row == currentRowIndex && col == currentColIndex) |
michael@0 | 1510 | { |
michael@0 | 1511 | res = AppendNodeToSelectionAsRange(cell); |
michael@0 | 1512 | if (NS_FAILED(res)) break; |
michael@0 | 1513 | } |
michael@0 | 1514 | } |
michael@0 | 1515 | } |
michael@0 | 1516 | return res; |
michael@0 | 1517 | } |
michael@0 | 1518 | |
michael@0 | 1519 | NS_IMETHODIMP |
michael@0 | 1520 | nsHTMLEditor::SelectAllTableCells() |
michael@0 | 1521 | { |
michael@0 | 1522 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 1523 | nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); |
michael@0 | 1524 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1525 | |
michael@0 | 1526 | // Don't fail if we didn't find a cell |
michael@0 | 1527 | NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 1528 | |
michael@0 | 1529 | nsCOMPtr<nsIDOMElement> startCell = cell; |
michael@0 | 1530 | |
michael@0 | 1531 | // Get parent table |
michael@0 | 1532 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 1533 | res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), cell, getter_AddRefs(table)); |
michael@0 | 1534 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1535 | if(!table) return NS_ERROR_NULL_POINTER; |
michael@0 | 1536 | |
michael@0 | 1537 | int32_t rowCount, colCount; |
michael@0 | 1538 | res = GetTableSize(table, &rowCount, &colCount); |
michael@0 | 1539 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1540 | |
michael@0 | 1541 | nsCOMPtr<nsISelection> selection; |
michael@0 | 1542 | res = GetSelection(getter_AddRefs(selection)); |
michael@0 | 1543 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1544 | NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); |
michael@0 | 1545 | |
michael@0 | 1546 | // Suppress nsISelectionListener notification |
michael@0 | 1547 | // until all selection changes are finished |
michael@0 | 1548 | nsSelectionBatcherForTable selectionBatcher(selection); |
michael@0 | 1549 | |
michael@0 | 1550 | // It is now safe to clear the selection |
michael@0 | 1551 | // BE SURE TO RESET IT BEFORE LEAVING! |
michael@0 | 1552 | res = ClearSelection(); |
michael@0 | 1553 | |
michael@0 | 1554 | // Select all cells in the same column as current cell |
michael@0 | 1555 | bool cellSelected = false; |
michael@0 | 1556 | int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex; |
michael@0 | 1557 | bool isSelected; |
michael@0 | 1558 | for(int32_t row = 0; row < rowCount; row++) |
michael@0 | 1559 | { |
michael@0 | 1560 | for(int32_t col = 0; col < colCount; col += std::max(actualColSpan, 1)) |
michael@0 | 1561 | { |
michael@0 | 1562 | res = GetCellDataAt(table, row, col, getter_AddRefs(cell), |
michael@0 | 1563 | ¤tRowIndex, ¤tColIndex, |
michael@0 | 1564 | &rowSpan, &colSpan, |
michael@0 | 1565 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 1566 | if (NS_FAILED(res)) break; |
michael@0 | 1567 | // Skip cells that are spanned from previous rows or columns |
michael@0 | 1568 | if (cell && row == currentRowIndex && col == currentColIndex) |
michael@0 | 1569 | { |
michael@0 | 1570 | res = AppendNodeToSelectionAsRange(cell); |
michael@0 | 1571 | if (NS_FAILED(res)) break; |
michael@0 | 1572 | cellSelected = true; |
michael@0 | 1573 | } |
michael@0 | 1574 | } |
michael@0 | 1575 | } |
michael@0 | 1576 | // Safety code to select starting cell if nothing else was selected |
michael@0 | 1577 | if (!cellSelected) |
michael@0 | 1578 | { |
michael@0 | 1579 | return AppendNodeToSelectionAsRange(startCell); |
michael@0 | 1580 | } |
michael@0 | 1581 | return res; |
michael@0 | 1582 | } |
michael@0 | 1583 | |
michael@0 | 1584 | NS_IMETHODIMP |
michael@0 | 1585 | nsHTMLEditor::SelectTableRow() |
michael@0 | 1586 | { |
michael@0 | 1587 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 1588 | nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); |
michael@0 | 1589 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1590 | |
michael@0 | 1591 | // Don't fail if we didn't find a cell |
michael@0 | 1592 | NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 1593 | nsCOMPtr<nsIDOMElement> startCell = cell; |
michael@0 | 1594 | |
michael@0 | 1595 | // Get table and location of cell: |
michael@0 | 1596 | nsCOMPtr<nsISelection> selection; |
michael@0 | 1597 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 1598 | int32_t startRowIndex, startColIndex; |
michael@0 | 1599 | |
michael@0 | 1600 | res = GetCellContext(getter_AddRefs(selection), |
michael@0 | 1601 | getter_AddRefs(table), |
michael@0 | 1602 | getter_AddRefs(cell), |
michael@0 | 1603 | nullptr, nullptr, |
michael@0 | 1604 | &startRowIndex, &startColIndex); |
michael@0 | 1605 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1606 | NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); |
michael@0 | 1607 | |
michael@0 | 1608 | int32_t rowCount, colCount; |
michael@0 | 1609 | res = GetTableSize(table, &rowCount, &colCount); |
michael@0 | 1610 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1611 | |
michael@0 | 1612 | //Note: At this point, we could get first and last cells in row, |
michael@0 | 1613 | // then call SelectBlockOfCells, but that would take just |
michael@0 | 1614 | // a little less code, so the following is more efficient |
michael@0 | 1615 | |
michael@0 | 1616 | // Suppress nsISelectionListener notification |
michael@0 | 1617 | // until all selection changes are finished |
michael@0 | 1618 | nsSelectionBatcherForTable selectionBatcher(selection); |
michael@0 | 1619 | |
michael@0 | 1620 | // It is now safe to clear the selection |
michael@0 | 1621 | // BE SURE TO RESET IT BEFORE LEAVING! |
michael@0 | 1622 | res = ClearSelection(); |
michael@0 | 1623 | |
michael@0 | 1624 | // Select all cells in the same row as current cell |
michael@0 | 1625 | bool cellSelected = false; |
michael@0 | 1626 | int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex; |
michael@0 | 1627 | bool isSelected; |
michael@0 | 1628 | for(int32_t col = 0; col < colCount; col += std::max(actualColSpan, 1)) |
michael@0 | 1629 | { |
michael@0 | 1630 | res = GetCellDataAt(table, startRowIndex, col, getter_AddRefs(cell), |
michael@0 | 1631 | ¤tRowIndex, ¤tColIndex, &rowSpan, &colSpan, |
michael@0 | 1632 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 1633 | if (NS_FAILED(res)) break; |
michael@0 | 1634 | // Skip cells that are spanned from previous rows or columns |
michael@0 | 1635 | if (cell && currentRowIndex == startRowIndex && currentColIndex == col) |
michael@0 | 1636 | { |
michael@0 | 1637 | res = AppendNodeToSelectionAsRange(cell); |
michael@0 | 1638 | if (NS_FAILED(res)) break; |
michael@0 | 1639 | cellSelected = true; |
michael@0 | 1640 | } |
michael@0 | 1641 | } |
michael@0 | 1642 | // Safety code to select starting cell if nothing else was selected |
michael@0 | 1643 | if (!cellSelected) |
michael@0 | 1644 | { |
michael@0 | 1645 | return AppendNodeToSelectionAsRange(startCell); |
michael@0 | 1646 | } |
michael@0 | 1647 | return res; |
michael@0 | 1648 | } |
michael@0 | 1649 | |
michael@0 | 1650 | NS_IMETHODIMP |
michael@0 | 1651 | nsHTMLEditor::SelectTableColumn() |
michael@0 | 1652 | { |
michael@0 | 1653 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 1654 | nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); |
michael@0 | 1655 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1656 | |
michael@0 | 1657 | // Don't fail if we didn't find a cell |
michael@0 | 1658 | NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 1659 | |
michael@0 | 1660 | nsCOMPtr<nsIDOMElement> startCell = cell; |
michael@0 | 1661 | |
michael@0 | 1662 | // Get location of cell: |
michael@0 | 1663 | nsCOMPtr<nsISelection> selection; |
michael@0 | 1664 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 1665 | int32_t startRowIndex, startColIndex; |
michael@0 | 1666 | |
michael@0 | 1667 | res = GetCellContext(getter_AddRefs(selection), |
michael@0 | 1668 | getter_AddRefs(table), |
michael@0 | 1669 | getter_AddRefs(cell), |
michael@0 | 1670 | nullptr, nullptr, |
michael@0 | 1671 | &startRowIndex, &startColIndex); |
michael@0 | 1672 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1673 | NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); |
michael@0 | 1674 | |
michael@0 | 1675 | int32_t rowCount, colCount; |
michael@0 | 1676 | res = GetTableSize(table, &rowCount, &colCount); |
michael@0 | 1677 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1678 | |
michael@0 | 1679 | // Suppress nsISelectionListener notification |
michael@0 | 1680 | // until all selection changes are finished |
michael@0 | 1681 | nsSelectionBatcherForTable selectionBatcher(selection); |
michael@0 | 1682 | |
michael@0 | 1683 | // It is now safe to clear the selection |
michael@0 | 1684 | // BE SURE TO RESET IT BEFORE LEAVING! |
michael@0 | 1685 | res = ClearSelection(); |
michael@0 | 1686 | |
michael@0 | 1687 | // Select all cells in the same column as current cell |
michael@0 | 1688 | bool cellSelected = false; |
michael@0 | 1689 | int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex; |
michael@0 | 1690 | bool isSelected; |
michael@0 | 1691 | for(int32_t row = 0; row < rowCount; row += std::max(actualRowSpan, 1)) |
michael@0 | 1692 | { |
michael@0 | 1693 | res = GetCellDataAt(table, row, startColIndex, getter_AddRefs(cell), |
michael@0 | 1694 | ¤tRowIndex, ¤tColIndex, &rowSpan, &colSpan, |
michael@0 | 1695 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 1696 | if (NS_FAILED(res)) break; |
michael@0 | 1697 | // Skip cells that are spanned from previous rows or columns |
michael@0 | 1698 | if (cell && currentRowIndex == row && currentColIndex == startColIndex) |
michael@0 | 1699 | { |
michael@0 | 1700 | res = AppendNodeToSelectionAsRange(cell); |
michael@0 | 1701 | if (NS_FAILED(res)) break; |
michael@0 | 1702 | cellSelected = true; |
michael@0 | 1703 | } |
michael@0 | 1704 | } |
michael@0 | 1705 | // Safety code to select starting cell if nothing else was selected |
michael@0 | 1706 | if (!cellSelected) |
michael@0 | 1707 | { |
michael@0 | 1708 | return AppendNodeToSelectionAsRange(startCell); |
michael@0 | 1709 | } |
michael@0 | 1710 | return res; |
michael@0 | 1711 | } |
michael@0 | 1712 | |
michael@0 | 1713 | NS_IMETHODIMP |
michael@0 | 1714 | nsHTMLEditor::SplitTableCell() |
michael@0 | 1715 | { |
michael@0 | 1716 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 1717 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 1718 | int32_t startRowIndex, startColIndex, actualRowSpan, actualColSpan; |
michael@0 | 1719 | // Get cell, table, etc. at selection anchor node |
michael@0 | 1720 | nsresult res = GetCellContext(nullptr, |
michael@0 | 1721 | getter_AddRefs(table), |
michael@0 | 1722 | getter_AddRefs(cell), |
michael@0 | 1723 | nullptr, nullptr, |
michael@0 | 1724 | &startRowIndex, &startColIndex); |
michael@0 | 1725 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1726 | if(!table || !cell) return NS_EDITOR_ELEMENT_NOT_FOUND; |
michael@0 | 1727 | |
michael@0 | 1728 | // We need rowspan and colspan data |
michael@0 | 1729 | res = GetCellSpansAt(table, startRowIndex, startColIndex, actualRowSpan, actualColSpan); |
michael@0 | 1730 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1731 | |
michael@0 | 1732 | // Must have some span to split |
michael@0 | 1733 | if (actualRowSpan <= 1 && actualColSpan <= 1) |
michael@0 | 1734 | return NS_OK; |
michael@0 | 1735 | |
michael@0 | 1736 | nsAutoEditBatch beginBatching(this); |
michael@0 | 1737 | // Prevent auto insertion of BR in new cell until we're done |
michael@0 | 1738 | nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); |
michael@0 | 1739 | |
michael@0 | 1740 | // We reset selection |
michael@0 | 1741 | nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); |
michael@0 | 1742 | //...so suppress Rules System selection munging |
michael@0 | 1743 | nsAutoTxnsConserveSelection dontChangeSelection(this); |
michael@0 | 1744 | |
michael@0 | 1745 | nsCOMPtr<nsIDOMElement> newCell; |
michael@0 | 1746 | int32_t rowIndex = startRowIndex; |
michael@0 | 1747 | int32_t rowSpanBelow, colSpanAfter; |
michael@0 | 1748 | |
michael@0 | 1749 | // Split up cell row-wise first into rowspan=1 above, and the rest below, |
michael@0 | 1750 | // whittling away at the cell below until no more extra span |
michael@0 | 1751 | for (rowSpanBelow = actualRowSpan-1; rowSpanBelow >= 0; rowSpanBelow--) |
michael@0 | 1752 | { |
michael@0 | 1753 | // We really split row-wise only if we had rowspan > 1 |
michael@0 | 1754 | if (rowSpanBelow > 0) |
michael@0 | 1755 | { |
michael@0 | 1756 | res = SplitCellIntoRows(table, rowIndex, startColIndex, 1, rowSpanBelow, getter_AddRefs(newCell)); |
michael@0 | 1757 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1758 | CopyCellBackgroundColor(newCell, cell); |
michael@0 | 1759 | } |
michael@0 | 1760 | int32_t colIndex = startColIndex; |
michael@0 | 1761 | // Now split the cell with rowspan = 1 into cells if it has colSpan > 1 |
michael@0 | 1762 | for (colSpanAfter = actualColSpan-1; colSpanAfter > 0; colSpanAfter--) |
michael@0 | 1763 | { |
michael@0 | 1764 | res = SplitCellIntoColumns(table, rowIndex, colIndex, 1, colSpanAfter, getter_AddRefs(newCell)); |
michael@0 | 1765 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1766 | CopyCellBackgroundColor(newCell, cell); |
michael@0 | 1767 | colIndex++; |
michael@0 | 1768 | } |
michael@0 | 1769 | // Point to the new cell and repeat |
michael@0 | 1770 | rowIndex++; |
michael@0 | 1771 | } |
michael@0 | 1772 | return res; |
michael@0 | 1773 | } |
michael@0 | 1774 | |
michael@0 | 1775 | nsresult |
michael@0 | 1776 | nsHTMLEditor::CopyCellBackgroundColor(nsIDOMElement *destCell, nsIDOMElement *sourceCell) |
michael@0 | 1777 | { |
michael@0 | 1778 | NS_ENSURE_TRUE(destCell && sourceCell, NS_ERROR_NULL_POINTER); |
michael@0 | 1779 | |
michael@0 | 1780 | // Copy backgournd color to new cell |
michael@0 | 1781 | NS_NAMED_LITERAL_STRING(bgcolor, "bgcolor"); |
michael@0 | 1782 | nsAutoString color; |
michael@0 | 1783 | bool isSet; |
michael@0 | 1784 | nsresult res = GetAttributeValue(sourceCell, bgcolor, color, &isSet); |
michael@0 | 1785 | |
michael@0 | 1786 | if (NS_SUCCEEDED(res) && isSet) |
michael@0 | 1787 | res = SetAttribute(destCell, bgcolor, color); |
michael@0 | 1788 | |
michael@0 | 1789 | return res; |
michael@0 | 1790 | } |
michael@0 | 1791 | |
michael@0 | 1792 | NS_IMETHODIMP |
michael@0 | 1793 | nsHTMLEditor::SplitCellIntoColumns(nsIDOMElement *aTable, int32_t aRowIndex, int32_t aColIndex, |
michael@0 | 1794 | int32_t aColSpanLeft, int32_t aColSpanRight, |
michael@0 | 1795 | nsIDOMElement **aNewCell) |
michael@0 | 1796 | { |
michael@0 | 1797 | NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); |
michael@0 | 1798 | if (aNewCell) *aNewCell = nullptr; |
michael@0 | 1799 | |
michael@0 | 1800 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 1801 | int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 1802 | bool isSelected; |
michael@0 | 1803 | nsresult res = GetCellDataAt(aTable, aRowIndex, aColIndex, getter_AddRefs(cell), |
michael@0 | 1804 | &startRowIndex, &startColIndex, |
michael@0 | 1805 | &rowSpan, &colSpan, |
michael@0 | 1806 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 1807 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1808 | NS_ENSURE_TRUE(cell, NS_ERROR_NULL_POINTER); |
michael@0 | 1809 | |
michael@0 | 1810 | // We can't split! |
michael@0 | 1811 | if (actualColSpan <= 1 || (aColSpanLeft + aColSpanRight) > actualColSpan) |
michael@0 | 1812 | return NS_OK; |
michael@0 | 1813 | |
michael@0 | 1814 | // Reduce colspan of cell to split |
michael@0 | 1815 | res = SetColSpan(cell, aColSpanLeft); |
michael@0 | 1816 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1817 | |
michael@0 | 1818 | // Insert new cell after using the remaining span |
michael@0 | 1819 | // and always get the new cell so we can copy the background color; |
michael@0 | 1820 | nsCOMPtr<nsIDOMElement> newCell; |
michael@0 | 1821 | res = InsertCell(cell, actualRowSpan, aColSpanRight, true, false, getter_AddRefs(newCell)); |
michael@0 | 1822 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1823 | if (newCell) |
michael@0 | 1824 | { |
michael@0 | 1825 | if (aNewCell) |
michael@0 | 1826 | { |
michael@0 | 1827 | *aNewCell = newCell.get(); |
michael@0 | 1828 | NS_ADDREF(*aNewCell); |
michael@0 | 1829 | } |
michael@0 | 1830 | res = CopyCellBackgroundColor(newCell, cell); |
michael@0 | 1831 | } |
michael@0 | 1832 | return res; |
michael@0 | 1833 | } |
michael@0 | 1834 | |
michael@0 | 1835 | NS_IMETHODIMP |
michael@0 | 1836 | nsHTMLEditor::SplitCellIntoRows(nsIDOMElement *aTable, int32_t aRowIndex, int32_t aColIndex, |
michael@0 | 1837 | int32_t aRowSpanAbove, int32_t aRowSpanBelow, |
michael@0 | 1838 | nsIDOMElement **aNewCell) |
michael@0 | 1839 | { |
michael@0 | 1840 | NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); |
michael@0 | 1841 | if (aNewCell) *aNewCell = nullptr; |
michael@0 | 1842 | |
michael@0 | 1843 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 1844 | int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 1845 | bool isSelected; |
michael@0 | 1846 | nsresult res = GetCellDataAt(aTable, aRowIndex, aColIndex, getter_AddRefs(cell), |
michael@0 | 1847 | &startRowIndex, &startColIndex, |
michael@0 | 1848 | &rowSpan, &colSpan, |
michael@0 | 1849 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 1850 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1851 | NS_ENSURE_TRUE(cell, NS_ERROR_NULL_POINTER); |
michael@0 | 1852 | |
michael@0 | 1853 | // We can't split! |
michael@0 | 1854 | if (actualRowSpan <= 1 || (aRowSpanAbove + aRowSpanBelow) > actualRowSpan) |
michael@0 | 1855 | return NS_OK; |
michael@0 | 1856 | |
michael@0 | 1857 | int32_t rowCount, colCount; |
michael@0 | 1858 | res = GetTableSize(aTable, &rowCount, &colCount); |
michael@0 | 1859 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1860 | |
michael@0 | 1861 | nsCOMPtr<nsIDOMElement> cell2; |
michael@0 | 1862 | nsCOMPtr<nsIDOMElement> lastCellFound; |
michael@0 | 1863 | int32_t startRowIndex2, startColIndex2, rowSpan2, colSpan2, actualRowSpan2, actualColSpan2; |
michael@0 | 1864 | bool isSelected2; |
michael@0 | 1865 | int32_t colIndex = 0; |
michael@0 | 1866 | bool insertAfter = (startColIndex > 0); |
michael@0 | 1867 | // This is the row we will insert new cell into |
michael@0 | 1868 | int32_t rowBelowIndex = startRowIndex+aRowSpanAbove; |
michael@0 | 1869 | |
michael@0 | 1870 | // Find a cell to insert before or after |
michael@0 | 1871 | do |
michael@0 | 1872 | { |
michael@0 | 1873 | // Search for a cell to insert before |
michael@0 | 1874 | res = GetCellDataAt(aTable, rowBelowIndex, |
michael@0 | 1875 | colIndex, getter_AddRefs(cell2), |
michael@0 | 1876 | &startRowIndex2, &startColIndex2, &rowSpan2, &colSpan2, |
michael@0 | 1877 | &actualRowSpan2, &actualColSpan2, &isSelected2); |
michael@0 | 1878 | // If we fail here, it could be because row has bad rowspan values, |
michael@0 | 1879 | // such as all cells having rowspan > 1 (Call FixRowSpan first!) |
michael@0 | 1880 | if (NS_FAILED(res) || !cell) return NS_ERROR_FAILURE; |
michael@0 | 1881 | |
michael@0 | 1882 | // Skip over cells spanned from above (like the one we are splitting!) |
michael@0 | 1883 | if (cell2 && startRowIndex2 == rowBelowIndex) |
michael@0 | 1884 | { |
michael@0 | 1885 | if (insertAfter) |
michael@0 | 1886 | { |
michael@0 | 1887 | // New cell isn't first in row, |
michael@0 | 1888 | // so stop after we find the cell just before new cell's column |
michael@0 | 1889 | if ((startColIndex2 + actualColSpan2) == startColIndex) |
michael@0 | 1890 | break; |
michael@0 | 1891 | |
michael@0 | 1892 | // If cell found is AFTER desired new cell colum, |
michael@0 | 1893 | // we have multiple cells with rowspan > 1 that |
michael@0 | 1894 | // prevented us from finding a cell to insert after... |
michael@0 | 1895 | if (startColIndex2 > startColIndex) |
michael@0 | 1896 | { |
michael@0 | 1897 | // ... so instead insert before the cell we found |
michael@0 | 1898 | insertAfter = false; |
michael@0 | 1899 | break; |
michael@0 | 1900 | } |
michael@0 | 1901 | } |
michael@0 | 1902 | else |
michael@0 | 1903 | { |
michael@0 | 1904 | break; // Inserting before, so stop at first cell in row we want to insert into |
michael@0 | 1905 | } |
michael@0 | 1906 | lastCellFound = cell2; |
michael@0 | 1907 | } |
michael@0 | 1908 | // Skip to next available cellmap location |
michael@0 | 1909 | colIndex += std::max(actualColSpan2, 1); |
michael@0 | 1910 | |
michael@0 | 1911 | // Done when past end of total number of columns |
michael@0 | 1912 | if (colIndex > colCount) |
michael@0 | 1913 | break; |
michael@0 | 1914 | |
michael@0 | 1915 | } while(true); |
michael@0 | 1916 | |
michael@0 | 1917 | if (!cell2 && lastCellFound) |
michael@0 | 1918 | { |
michael@0 | 1919 | // Edge case where we didn't find a cell to insert after |
michael@0 | 1920 | // or before because column(s) before desired column |
michael@0 | 1921 | // and all columns after it are spanned from above. |
michael@0 | 1922 | // We can insert after the last cell we found |
michael@0 | 1923 | cell2 = lastCellFound; |
michael@0 | 1924 | insertAfter = true; // Should always be true, but let's be sure |
michael@0 | 1925 | } |
michael@0 | 1926 | |
michael@0 | 1927 | // Reduce rowspan of cell to split |
michael@0 | 1928 | res = SetRowSpan(cell, aRowSpanAbove); |
michael@0 | 1929 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1930 | |
michael@0 | 1931 | |
michael@0 | 1932 | // Insert new cell after using the remaining span |
michael@0 | 1933 | // and always get the new cell so we can copy the background color; |
michael@0 | 1934 | nsCOMPtr<nsIDOMElement> newCell; |
michael@0 | 1935 | res = InsertCell(cell2, aRowSpanBelow, actualColSpan, insertAfter, false, getter_AddRefs(newCell)); |
michael@0 | 1936 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1937 | if (newCell) |
michael@0 | 1938 | { |
michael@0 | 1939 | if (aNewCell) |
michael@0 | 1940 | { |
michael@0 | 1941 | *aNewCell = newCell.get(); |
michael@0 | 1942 | NS_ADDREF(*aNewCell); |
michael@0 | 1943 | } |
michael@0 | 1944 | res = CopyCellBackgroundColor(newCell, cell2); |
michael@0 | 1945 | } |
michael@0 | 1946 | return res; |
michael@0 | 1947 | } |
michael@0 | 1948 | |
michael@0 | 1949 | NS_IMETHODIMP |
michael@0 | 1950 | nsHTMLEditor::SwitchTableCellHeaderType(nsIDOMElement *aSourceCell, nsIDOMElement **aNewCell) |
michael@0 | 1951 | { |
michael@0 | 1952 | NS_ENSURE_TRUE(aSourceCell, NS_ERROR_NULL_POINTER); |
michael@0 | 1953 | |
michael@0 | 1954 | nsAutoEditBatch beginBatching(this); |
michael@0 | 1955 | // Prevent auto insertion of BR in new cell created by ReplaceContainer |
michael@0 | 1956 | nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); |
michael@0 | 1957 | |
michael@0 | 1958 | nsCOMPtr<nsIDOMNode> newNode; |
michael@0 | 1959 | |
michael@0 | 1960 | // Save current selection to restore when done |
michael@0 | 1961 | // This is needed so ReplaceContainer can monitor selection |
michael@0 | 1962 | // when replacing nodes |
michael@0 | 1963 | nsRefPtr<Selection> selection = GetSelection(); |
michael@0 | 1964 | NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); |
michael@0 | 1965 | nsAutoSelectionReset selectionResetter(selection, this); |
michael@0 | 1966 | |
michael@0 | 1967 | // Set to the opposite of current type |
michael@0 | 1968 | nsCOMPtr<nsIAtom> atom = nsEditor::GetTag(aSourceCell); |
michael@0 | 1969 | nsString newCellType( (atom == nsEditProperty::td) ? NS_LITERAL_STRING("th") : NS_LITERAL_STRING("td")); |
michael@0 | 1970 | |
michael@0 | 1971 | // This creates new node, moves children, copies attributes (true) |
michael@0 | 1972 | // and manages the selection! |
michael@0 | 1973 | nsresult res = ReplaceContainer(aSourceCell, address_of(newNode), |
michael@0 | 1974 | newCellType, nullptr, nullptr, true); |
michael@0 | 1975 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 1976 | NS_ENSURE_TRUE(newNode, NS_ERROR_FAILURE); |
michael@0 | 1977 | |
michael@0 | 1978 | // Return the new cell |
michael@0 | 1979 | if (aNewCell) |
michael@0 | 1980 | { |
michael@0 | 1981 | nsCOMPtr<nsIDOMElement> newElement = do_QueryInterface(newNode); |
michael@0 | 1982 | *aNewCell = newElement.get(); |
michael@0 | 1983 | NS_ADDREF(*aNewCell); |
michael@0 | 1984 | } |
michael@0 | 1985 | |
michael@0 | 1986 | return NS_OK; |
michael@0 | 1987 | } |
michael@0 | 1988 | |
michael@0 | 1989 | NS_IMETHODIMP |
michael@0 | 1990 | nsHTMLEditor::JoinTableCells(bool aMergeNonContiguousContents) |
michael@0 | 1991 | { |
michael@0 | 1992 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 1993 | nsCOMPtr<nsIDOMElement> targetCell; |
michael@0 | 1994 | int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 1995 | bool isSelected; |
michael@0 | 1996 | nsCOMPtr<nsIDOMElement> cell2; |
michael@0 | 1997 | int32_t startRowIndex2, startColIndex2, rowSpan2, colSpan2, actualRowSpan2, actualColSpan2; |
michael@0 | 1998 | bool isSelected2; |
michael@0 | 1999 | |
michael@0 | 2000 | // Get cell, table, etc. at selection anchor node |
michael@0 | 2001 | nsresult res = GetCellContext(nullptr, |
michael@0 | 2002 | getter_AddRefs(table), |
michael@0 | 2003 | getter_AddRefs(targetCell), |
michael@0 | 2004 | nullptr, nullptr, |
michael@0 | 2005 | &startRowIndex, &startColIndex); |
michael@0 | 2006 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2007 | if(!table || !targetCell) return NS_EDITOR_ELEMENT_NOT_FOUND; |
michael@0 | 2008 | |
michael@0 | 2009 | nsAutoEditBatch beginBatching(this); |
michael@0 | 2010 | //Don't let Rules System change the selection |
michael@0 | 2011 | nsAutoTxnsConserveSelection dontChangeSelection(this); |
michael@0 | 2012 | |
michael@0 | 2013 | // Note: We dont' use nsSetSelectionAfterTableEdit here so the selection |
michael@0 | 2014 | // is retained after joining. This leaves the target cell selected |
michael@0 | 2015 | // as well as the "non-contiguous" cells, so user can see what happened. |
michael@0 | 2016 | |
michael@0 | 2017 | nsCOMPtr<nsIDOMElement> firstCell; |
michael@0 | 2018 | int32_t firstRowIndex, firstColIndex; |
michael@0 | 2019 | res = GetFirstSelectedCellInTable(&firstRowIndex, &firstColIndex, getter_AddRefs(firstCell)); |
michael@0 | 2020 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2021 | |
michael@0 | 2022 | bool joinSelectedCells = false; |
michael@0 | 2023 | if (firstCell) |
michael@0 | 2024 | { |
michael@0 | 2025 | nsCOMPtr<nsIDOMElement> secondCell; |
michael@0 | 2026 | res = GetNextSelectedCell(nullptr, getter_AddRefs(secondCell)); |
michael@0 | 2027 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2028 | |
michael@0 | 2029 | // If only one cell is selected, join with cell to the right |
michael@0 | 2030 | joinSelectedCells = (secondCell != nullptr); |
michael@0 | 2031 | } |
michael@0 | 2032 | |
michael@0 | 2033 | if (joinSelectedCells) |
michael@0 | 2034 | { |
michael@0 | 2035 | // We have selected cells: Join just contiguous cells |
michael@0 | 2036 | // and just merge contents if not contiguous |
michael@0 | 2037 | |
michael@0 | 2038 | int32_t rowCount, colCount; |
michael@0 | 2039 | res = GetTableSize(table, &rowCount, &colCount); |
michael@0 | 2040 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2041 | |
michael@0 | 2042 | // Get spans for cell we will merge into |
michael@0 | 2043 | int32_t firstRowSpan, firstColSpan; |
michael@0 | 2044 | res = GetCellSpansAt( table, firstRowIndex, firstColIndex, firstRowSpan, firstColSpan); |
michael@0 | 2045 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2046 | |
michael@0 | 2047 | // This defines the last indexes along the "edges" |
michael@0 | 2048 | // of the contiguous block of cells, telling us |
michael@0 | 2049 | // that we can join adjacent cells to the block |
michael@0 | 2050 | // Start with same as the first values, |
michael@0 | 2051 | // then expand as we find adjacent selected cells |
michael@0 | 2052 | int32_t lastRowIndex = firstRowIndex; |
michael@0 | 2053 | int32_t lastColIndex = firstColIndex; |
michael@0 | 2054 | int32_t rowIndex, colIndex; |
michael@0 | 2055 | |
michael@0 | 2056 | // First pass: Determine boundaries of contiguous rectangular block |
michael@0 | 2057 | // that we will join into one cell, |
michael@0 | 2058 | // favoring adjacent cells in the same row |
michael@0 | 2059 | for (rowIndex = firstRowIndex; rowIndex <= lastRowIndex; rowIndex++) |
michael@0 | 2060 | { |
michael@0 | 2061 | int32_t currentRowCount = rowCount; |
michael@0 | 2062 | // Be sure each row doesn't have rowspan errors |
michael@0 | 2063 | res = FixBadRowSpan(table, rowIndex, rowCount); |
michael@0 | 2064 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2065 | // Adjust rowcount by number of rows we removed |
michael@0 | 2066 | lastRowIndex -= (currentRowCount-rowCount); |
michael@0 | 2067 | |
michael@0 | 2068 | bool cellFoundInRow = false; |
michael@0 | 2069 | bool lastRowIsSet = false; |
michael@0 | 2070 | int32_t lastColInRow = 0; |
michael@0 | 2071 | int32_t firstColInRow = firstColIndex; |
michael@0 | 2072 | for (colIndex = firstColIndex; colIndex < colCount; colIndex += std::max(actualColSpan2, 1)) |
michael@0 | 2073 | { |
michael@0 | 2074 | res = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell2), |
michael@0 | 2075 | &startRowIndex2, &startColIndex2, |
michael@0 | 2076 | &rowSpan2, &colSpan2, |
michael@0 | 2077 | &actualRowSpan2, &actualColSpan2, &isSelected2); |
michael@0 | 2078 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2079 | |
michael@0 | 2080 | if (isSelected2) |
michael@0 | 2081 | { |
michael@0 | 2082 | if (!cellFoundInRow) |
michael@0 | 2083 | // We've just found the first selected cell in this row |
michael@0 | 2084 | firstColInRow = colIndex; |
michael@0 | 2085 | |
michael@0 | 2086 | if (rowIndex > firstRowIndex && firstColInRow != firstColIndex) |
michael@0 | 2087 | { |
michael@0 | 2088 | // We're in at least the second row, |
michael@0 | 2089 | // but left boundary is "ragged" (not the same as 1st row's start) |
michael@0 | 2090 | //Let's just end block on previous row |
michael@0 | 2091 | // and keep previous lastColIndex |
michael@0 | 2092 | //TODO: We could try to find the Maximum firstColInRow |
michael@0 | 2093 | // so our block can still extend down more rows? |
michael@0 | 2094 | lastRowIndex = std::max(0,rowIndex - 1); |
michael@0 | 2095 | lastRowIsSet = true; |
michael@0 | 2096 | break; |
michael@0 | 2097 | } |
michael@0 | 2098 | // Save max selected column in this row, including extra colspan |
michael@0 | 2099 | lastColInRow = colIndex + (actualColSpan2-1); |
michael@0 | 2100 | cellFoundInRow = true; |
michael@0 | 2101 | } |
michael@0 | 2102 | else if (cellFoundInRow) |
michael@0 | 2103 | { |
michael@0 | 2104 | // No cell or not selected, but at least one cell in row was found |
michael@0 | 2105 | |
michael@0 | 2106 | if (rowIndex > (firstRowIndex+1) && colIndex <= lastColIndex) |
michael@0 | 2107 | { |
michael@0 | 2108 | // Cell is in a column less than current right border in |
michael@0 | 2109 | // the third or higher selected row, so stop block at the previous row |
michael@0 | 2110 | lastRowIndex = std::max(0,rowIndex - 1); |
michael@0 | 2111 | lastRowIsSet = true; |
michael@0 | 2112 | } |
michael@0 | 2113 | // We're done with this row |
michael@0 | 2114 | break; |
michael@0 | 2115 | } |
michael@0 | 2116 | } // End of column loop |
michael@0 | 2117 | |
michael@0 | 2118 | // Done with this row |
michael@0 | 2119 | if (cellFoundInRow) |
michael@0 | 2120 | { |
michael@0 | 2121 | if (rowIndex == firstRowIndex) |
michael@0 | 2122 | { |
michael@0 | 2123 | // First row always initializes the right boundary |
michael@0 | 2124 | lastColIndex = lastColInRow; |
michael@0 | 2125 | } |
michael@0 | 2126 | |
michael@0 | 2127 | // If we didn't determine last row above... |
michael@0 | 2128 | if (!lastRowIsSet) |
michael@0 | 2129 | { |
michael@0 | 2130 | if (colIndex < lastColIndex) |
michael@0 | 2131 | { |
michael@0 | 2132 | // (don't think we ever get here?) |
michael@0 | 2133 | // Cell is in a column less than current right boundary, |
michael@0 | 2134 | // so stop block at the previous row |
michael@0 | 2135 | lastRowIndex = std::max(0,rowIndex - 1); |
michael@0 | 2136 | } |
michael@0 | 2137 | else |
michael@0 | 2138 | { |
michael@0 | 2139 | // Go on to examine next row |
michael@0 | 2140 | lastRowIndex = rowIndex+1; |
michael@0 | 2141 | } |
michael@0 | 2142 | } |
michael@0 | 2143 | // Use the minimum col we found so far for right boundary |
michael@0 | 2144 | lastColIndex = std::min(lastColIndex, lastColInRow); |
michael@0 | 2145 | } |
michael@0 | 2146 | else |
michael@0 | 2147 | { |
michael@0 | 2148 | // No selected cells in this row -- stop at row above |
michael@0 | 2149 | // and leave last column at its previous value |
michael@0 | 2150 | lastRowIndex = std::max(0,rowIndex - 1); |
michael@0 | 2151 | } |
michael@0 | 2152 | } |
michael@0 | 2153 | |
michael@0 | 2154 | // The list of cells we will delete after joining |
michael@0 | 2155 | nsTArray<nsCOMPtr<nsIDOMElement> > deleteList; |
michael@0 | 2156 | |
michael@0 | 2157 | // 2nd pass: Do the joining and merging |
michael@0 | 2158 | for (rowIndex = 0; rowIndex < rowCount; rowIndex++) |
michael@0 | 2159 | { |
michael@0 | 2160 | for (colIndex = 0; colIndex < colCount; colIndex += std::max(actualColSpan2, 1)) |
michael@0 | 2161 | { |
michael@0 | 2162 | res = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell2), |
michael@0 | 2163 | &startRowIndex2, &startColIndex2, |
michael@0 | 2164 | &rowSpan2, &colSpan2, |
michael@0 | 2165 | &actualRowSpan2, &actualColSpan2, &isSelected2); |
michael@0 | 2166 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2167 | |
michael@0 | 2168 | // If this is 0, we are past last cell in row, so exit the loop |
michael@0 | 2169 | if (actualColSpan2 == 0) |
michael@0 | 2170 | break; |
michael@0 | 2171 | |
michael@0 | 2172 | // Merge only selected cells (skip cell we're merging into, of course) |
michael@0 | 2173 | if (isSelected2 && cell2 != firstCell) |
michael@0 | 2174 | { |
michael@0 | 2175 | if (rowIndex >= firstRowIndex && rowIndex <= lastRowIndex && |
michael@0 | 2176 | colIndex >= firstColIndex && colIndex <= lastColIndex) |
michael@0 | 2177 | { |
michael@0 | 2178 | // We are within the join region |
michael@0 | 2179 | // Problem: It is very tricky to delete cells as we merge, |
michael@0 | 2180 | // since that will upset the cellmap |
michael@0 | 2181 | // Instead, build a list of cells to delete and do it later |
michael@0 | 2182 | NS_ASSERTION(startRowIndex2 == rowIndex, "JoinTableCells: StartRowIndex is in row above"); |
michael@0 | 2183 | |
michael@0 | 2184 | if (actualColSpan2 > 1) |
michael@0 | 2185 | { |
michael@0 | 2186 | //Check if cell "hangs" off the boundary because of colspan > 1 |
michael@0 | 2187 | // Use split methods to chop off excess |
michael@0 | 2188 | int32_t extraColSpan = (startColIndex2 + actualColSpan2) - (lastColIndex+1); |
michael@0 | 2189 | if ( extraColSpan > 0) |
michael@0 | 2190 | { |
michael@0 | 2191 | res = SplitCellIntoColumns(table, startRowIndex2, startColIndex2, |
michael@0 | 2192 | actualColSpan2-extraColSpan, extraColSpan, nullptr); |
michael@0 | 2193 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2194 | } |
michael@0 | 2195 | } |
michael@0 | 2196 | |
michael@0 | 2197 | res = MergeCells(firstCell, cell2, false); |
michael@0 | 2198 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2199 | |
michael@0 | 2200 | // Add cell to list to delete |
michael@0 | 2201 | deleteList.AppendElement(cell2.get()); |
michael@0 | 2202 | } |
michael@0 | 2203 | else if (aMergeNonContiguousContents) |
michael@0 | 2204 | { |
michael@0 | 2205 | // Cell is outside join region -- just merge the contents |
michael@0 | 2206 | res = MergeCells(firstCell, cell2, false); |
michael@0 | 2207 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2208 | } |
michael@0 | 2209 | } |
michael@0 | 2210 | } |
michael@0 | 2211 | } |
michael@0 | 2212 | |
michael@0 | 2213 | // All cell contents are merged. Delete the empty cells we accumulated |
michael@0 | 2214 | // Prevent rules testing until we're done |
michael@0 | 2215 | nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); |
michael@0 | 2216 | |
michael@0 | 2217 | for (uint32_t i = 0, n = deleteList.Length(); i < n; i++) |
michael@0 | 2218 | { |
michael@0 | 2219 | nsIDOMElement *elementPtr = deleteList[i]; |
michael@0 | 2220 | if (elementPtr) |
michael@0 | 2221 | { |
michael@0 | 2222 | nsCOMPtr<nsIDOMNode> node = do_QueryInterface(elementPtr); |
michael@0 | 2223 | res = DeleteNode(node); |
michael@0 | 2224 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2225 | } |
michael@0 | 2226 | } |
michael@0 | 2227 | // Cleanup selection: remove ranges where cells were deleted |
michael@0 | 2228 | nsCOMPtr<nsISelection> selection; |
michael@0 | 2229 | res = GetSelection(getter_AddRefs(selection)); |
michael@0 | 2230 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2231 | NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); |
michael@0 | 2232 | |
michael@0 | 2233 | int32_t rangeCount; |
michael@0 | 2234 | res = selection->GetRangeCount(&rangeCount); |
michael@0 | 2235 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2236 | |
michael@0 | 2237 | nsCOMPtr<nsIDOMRange> range; |
michael@0 | 2238 | int32_t i; |
michael@0 | 2239 | for (i = 0; i < rangeCount; i++) |
michael@0 | 2240 | { |
michael@0 | 2241 | res = selection->GetRangeAt(i, getter_AddRefs(range)); |
michael@0 | 2242 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2243 | NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); |
michael@0 | 2244 | |
michael@0 | 2245 | nsCOMPtr<nsIDOMElement> deletedCell; |
michael@0 | 2246 | res = GetCellFromRange(range, getter_AddRefs(deletedCell)); |
michael@0 | 2247 | if (!deletedCell) |
michael@0 | 2248 | { |
michael@0 | 2249 | selection->RemoveRange(range); |
michael@0 | 2250 | rangeCount--; |
michael@0 | 2251 | i--; |
michael@0 | 2252 | } |
michael@0 | 2253 | } |
michael@0 | 2254 | |
michael@0 | 2255 | // Set spans for the cell everthing merged into |
michael@0 | 2256 | res = SetRowSpan(firstCell, lastRowIndex-firstRowIndex+1); |
michael@0 | 2257 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2258 | res = SetColSpan(firstCell, lastColIndex-firstColIndex+1); |
michael@0 | 2259 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2260 | |
michael@0 | 2261 | |
michael@0 | 2262 | // Fixup disturbances in table layout |
michael@0 | 2263 | NormalizeTable(table); |
michael@0 | 2264 | } |
michael@0 | 2265 | else |
michael@0 | 2266 | { |
michael@0 | 2267 | // Joining with cell to the right -- get rowspan and colspan data of target cell |
michael@0 | 2268 | res = GetCellDataAt(table, startRowIndex, startColIndex, getter_AddRefs(targetCell), |
michael@0 | 2269 | &startRowIndex, &startColIndex, &rowSpan, &colSpan, |
michael@0 | 2270 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 2271 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2272 | NS_ENSURE_TRUE(targetCell, NS_ERROR_NULL_POINTER); |
michael@0 | 2273 | |
michael@0 | 2274 | // Get data for cell to the right |
michael@0 | 2275 | res = GetCellDataAt(table, startRowIndex, startColIndex+actualColSpan, getter_AddRefs(cell2), |
michael@0 | 2276 | &startRowIndex2, &startColIndex2, &rowSpan2, &colSpan2, |
michael@0 | 2277 | &actualRowSpan2, &actualColSpan2, &isSelected2); |
michael@0 | 2278 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2279 | if(!cell2) return NS_OK; // Don't fail if there's no cell |
michael@0 | 2280 | |
michael@0 | 2281 | // sanity check |
michael@0 | 2282 | NS_ASSERTION((startRowIndex >= startRowIndex2),"JoinCells: startRowIndex < startRowIndex2"); |
michael@0 | 2283 | |
michael@0 | 2284 | // Figure out span of merged cell starting from target's starting row |
michael@0 | 2285 | // to handle case of merged cell starting in a row above |
michael@0 | 2286 | int32_t spanAboveMergedCell = startRowIndex - startRowIndex2; |
michael@0 | 2287 | int32_t effectiveRowSpan2 = actualRowSpan2 - spanAboveMergedCell; |
michael@0 | 2288 | |
michael@0 | 2289 | if (effectiveRowSpan2 > actualRowSpan) |
michael@0 | 2290 | { |
michael@0 | 2291 | // Cell to the right spans into row below target |
michael@0 | 2292 | // Split off portion below target cell's bottom-most row |
michael@0 | 2293 | res = SplitCellIntoRows(table, startRowIndex2, startColIndex2, |
michael@0 | 2294 | spanAboveMergedCell+actualRowSpan, |
michael@0 | 2295 | effectiveRowSpan2-actualRowSpan, nullptr); |
michael@0 | 2296 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2297 | } |
michael@0 | 2298 | |
michael@0 | 2299 | // Move contents from cell to the right |
michael@0 | 2300 | // Delete the cell now only if it starts in the same row |
michael@0 | 2301 | // and has enough row "height" |
michael@0 | 2302 | res = MergeCells(targetCell, cell2, |
michael@0 | 2303 | (startRowIndex2 == startRowIndex) && |
michael@0 | 2304 | (effectiveRowSpan2 >= actualRowSpan)); |
michael@0 | 2305 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2306 | |
michael@0 | 2307 | if (effectiveRowSpan2 < actualRowSpan) |
michael@0 | 2308 | { |
michael@0 | 2309 | // Merged cell is "shorter" |
michael@0 | 2310 | // (there are cells(s) below it that are row-spanned by target cell) |
michael@0 | 2311 | // We could try splitting those cells, but that's REAL messy, |
michael@0 | 2312 | // so the safest thing to do is NOT really join the cells |
michael@0 | 2313 | return NS_OK; |
michael@0 | 2314 | } |
michael@0 | 2315 | |
michael@0 | 2316 | if( spanAboveMergedCell > 0 ) |
michael@0 | 2317 | { |
michael@0 | 2318 | // Cell we merged started in a row above the target cell |
michael@0 | 2319 | // Reduce rowspan to give room where target cell will extend its colspan |
michael@0 | 2320 | res = SetRowSpan(cell2, spanAboveMergedCell); |
michael@0 | 2321 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2322 | } |
michael@0 | 2323 | |
michael@0 | 2324 | // Reset target cell's colspan to encompass cell to the right |
michael@0 | 2325 | res = SetColSpan(targetCell, actualColSpan+actualColSpan2); |
michael@0 | 2326 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2327 | } |
michael@0 | 2328 | return res; |
michael@0 | 2329 | } |
michael@0 | 2330 | |
michael@0 | 2331 | NS_IMETHODIMP |
michael@0 | 2332 | nsHTMLEditor::MergeCells(nsCOMPtr<nsIDOMElement> aTargetCell, |
michael@0 | 2333 | nsCOMPtr<nsIDOMElement> aCellToMerge, |
michael@0 | 2334 | bool aDeleteCellToMerge) |
michael@0 | 2335 | { |
michael@0 | 2336 | nsCOMPtr<dom::Element> targetCell = do_QueryInterface(aTargetCell); |
michael@0 | 2337 | nsCOMPtr<dom::Element> cellToMerge = do_QueryInterface(aCellToMerge); |
michael@0 | 2338 | NS_ENSURE_TRUE(targetCell && cellToMerge, NS_ERROR_NULL_POINTER); |
michael@0 | 2339 | |
michael@0 | 2340 | // Prevent rules testing until we're done |
michael@0 | 2341 | nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); |
michael@0 | 2342 | |
michael@0 | 2343 | // Don't need to merge if cell is empty |
michael@0 | 2344 | if (!IsEmptyCell(cellToMerge)) { |
michael@0 | 2345 | // Get index of last child in target cell |
michael@0 | 2346 | // If we fail or don't have children, |
michael@0 | 2347 | // we insert at index 0 |
michael@0 | 2348 | int32_t insertIndex = 0; |
michael@0 | 2349 | |
michael@0 | 2350 | // Start inserting just after last child |
michael@0 | 2351 | uint32_t len = targetCell->GetChildCount(); |
michael@0 | 2352 | if (len == 1 && IsEmptyCell(targetCell)) { |
michael@0 | 2353 | // Delete the empty node |
michael@0 | 2354 | nsIContent* cellChild = targetCell->GetFirstChild(); |
michael@0 | 2355 | nsresult res = DeleteNode(cellChild->AsDOMNode()); |
michael@0 | 2356 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2357 | insertIndex = 0; |
michael@0 | 2358 | } else { |
michael@0 | 2359 | insertIndex = (int32_t)len; |
michael@0 | 2360 | } |
michael@0 | 2361 | |
michael@0 | 2362 | // Move the contents |
michael@0 | 2363 | while (cellToMerge->HasChildren()) { |
michael@0 | 2364 | nsCOMPtr<nsIDOMNode> cellChild = cellToMerge->GetLastChild()->AsDOMNode(); |
michael@0 | 2365 | nsresult res = DeleteNode(cellChild); |
michael@0 | 2366 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2367 | |
michael@0 | 2368 | res = InsertNode(cellChild, aTargetCell, insertIndex); |
michael@0 | 2369 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2370 | } |
michael@0 | 2371 | } |
michael@0 | 2372 | |
michael@0 | 2373 | // Delete cells whose contents were moved |
michael@0 | 2374 | if (aDeleteCellToMerge) |
michael@0 | 2375 | return DeleteNode(aCellToMerge); |
michael@0 | 2376 | |
michael@0 | 2377 | return NS_OK; |
michael@0 | 2378 | } |
michael@0 | 2379 | |
michael@0 | 2380 | |
michael@0 | 2381 | NS_IMETHODIMP |
michael@0 | 2382 | nsHTMLEditor::FixBadRowSpan(nsIDOMElement *aTable, int32_t aRowIndex, int32_t& aNewRowCount) |
michael@0 | 2383 | { |
michael@0 | 2384 | NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); |
michael@0 | 2385 | |
michael@0 | 2386 | int32_t rowCount, colCount; |
michael@0 | 2387 | nsresult res = GetTableSize(aTable, &rowCount, &colCount); |
michael@0 | 2388 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2389 | |
michael@0 | 2390 | nsCOMPtr<nsIDOMElement>cell; |
michael@0 | 2391 | int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 2392 | bool isSelected; |
michael@0 | 2393 | |
michael@0 | 2394 | int32_t minRowSpan = -1; |
michael@0 | 2395 | int32_t colIndex; |
michael@0 | 2396 | |
michael@0 | 2397 | for( colIndex = 0; colIndex < colCount; colIndex += std::max(actualColSpan, 1)) |
michael@0 | 2398 | { |
michael@0 | 2399 | res = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), |
michael@0 | 2400 | &startRowIndex, &startColIndex, &rowSpan, &colSpan, |
michael@0 | 2401 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 2402 | // NOTE: This is a *real* failure. |
michael@0 | 2403 | // GetCellDataAt passes if cell is missing from cellmap |
michael@0 | 2404 | if(NS_FAILED(res)) return res; |
michael@0 | 2405 | if (!cell) break; |
michael@0 | 2406 | if(rowSpan > 0 && |
michael@0 | 2407 | startRowIndex == aRowIndex && |
michael@0 | 2408 | (rowSpan < minRowSpan || minRowSpan == -1)) |
michael@0 | 2409 | { |
michael@0 | 2410 | minRowSpan = rowSpan; |
michael@0 | 2411 | } |
michael@0 | 2412 | NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in FixBadRowSpan"); |
michael@0 | 2413 | } |
michael@0 | 2414 | if(minRowSpan > 1) |
michael@0 | 2415 | { |
michael@0 | 2416 | // The amount to reduce everyone's rowspan |
michael@0 | 2417 | // so at least one cell has rowspan = 1 |
michael@0 | 2418 | int32_t rowsReduced = minRowSpan - 1; |
michael@0 | 2419 | for(colIndex = 0; colIndex < colCount; colIndex += std::max(actualColSpan, 1)) |
michael@0 | 2420 | { |
michael@0 | 2421 | res = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), |
michael@0 | 2422 | &startRowIndex, &startColIndex, &rowSpan, &colSpan, |
michael@0 | 2423 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 2424 | if(NS_FAILED(res)) return res; |
michael@0 | 2425 | // Fixup rowspans only for cells starting in current row |
michael@0 | 2426 | if(cell && rowSpan > 0 && |
michael@0 | 2427 | startRowIndex == aRowIndex && |
michael@0 | 2428 | startColIndex == colIndex ) |
michael@0 | 2429 | { |
michael@0 | 2430 | res = SetRowSpan(cell, rowSpan-rowsReduced); |
michael@0 | 2431 | if(NS_FAILED(res)) return res; |
michael@0 | 2432 | } |
michael@0 | 2433 | NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in FixBadRowSpan"); |
michael@0 | 2434 | } |
michael@0 | 2435 | } |
michael@0 | 2436 | return GetTableSize(aTable, &aNewRowCount, &colCount); |
michael@0 | 2437 | } |
michael@0 | 2438 | |
michael@0 | 2439 | NS_IMETHODIMP |
michael@0 | 2440 | nsHTMLEditor::FixBadColSpan(nsIDOMElement *aTable, int32_t aColIndex, int32_t& aNewColCount) |
michael@0 | 2441 | { |
michael@0 | 2442 | NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); |
michael@0 | 2443 | |
michael@0 | 2444 | int32_t rowCount, colCount; |
michael@0 | 2445 | nsresult res = GetTableSize(aTable, &rowCount, &colCount); |
michael@0 | 2446 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2447 | |
michael@0 | 2448 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 2449 | int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 2450 | bool isSelected; |
michael@0 | 2451 | |
michael@0 | 2452 | int32_t minColSpan = -1; |
michael@0 | 2453 | int32_t rowIndex; |
michael@0 | 2454 | |
michael@0 | 2455 | for( rowIndex = 0; rowIndex < rowCount; rowIndex += std::max(actualRowSpan, 1)) |
michael@0 | 2456 | { |
michael@0 | 2457 | res = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell), |
michael@0 | 2458 | &startRowIndex, &startColIndex, &rowSpan, &colSpan, |
michael@0 | 2459 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 2460 | // NOTE: This is a *real* failure. |
michael@0 | 2461 | // GetCellDataAt passes if cell is missing from cellmap |
michael@0 | 2462 | if(NS_FAILED(res)) return res; |
michael@0 | 2463 | if (!cell) break; |
michael@0 | 2464 | if(colSpan > 0 && |
michael@0 | 2465 | startColIndex == aColIndex && |
michael@0 | 2466 | (colSpan < minColSpan || minColSpan == -1)) |
michael@0 | 2467 | { |
michael@0 | 2468 | minColSpan = colSpan; |
michael@0 | 2469 | } |
michael@0 | 2470 | NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in FixBadColSpan"); |
michael@0 | 2471 | } |
michael@0 | 2472 | if(minColSpan > 1) |
michael@0 | 2473 | { |
michael@0 | 2474 | // The amount to reduce everyone's colspan |
michael@0 | 2475 | // so at least one cell has colspan = 1 |
michael@0 | 2476 | int32_t colsReduced = minColSpan - 1; |
michael@0 | 2477 | for(rowIndex = 0; rowIndex < rowCount; rowIndex += std::max(actualRowSpan, 1)) |
michael@0 | 2478 | { |
michael@0 | 2479 | res = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell), |
michael@0 | 2480 | &startRowIndex, &startColIndex, &rowSpan, &colSpan, |
michael@0 | 2481 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 2482 | if(NS_FAILED(res)) return res; |
michael@0 | 2483 | // Fixup colspans only for cells starting in current column |
michael@0 | 2484 | if(cell && colSpan > 0 && |
michael@0 | 2485 | startColIndex == aColIndex && |
michael@0 | 2486 | startRowIndex == rowIndex ) |
michael@0 | 2487 | { |
michael@0 | 2488 | res = SetColSpan(cell, colSpan-colsReduced); |
michael@0 | 2489 | if(NS_FAILED(res)) return res; |
michael@0 | 2490 | } |
michael@0 | 2491 | NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in FixBadColSpan"); |
michael@0 | 2492 | } |
michael@0 | 2493 | } |
michael@0 | 2494 | return GetTableSize(aTable, &rowCount, &aNewColCount); |
michael@0 | 2495 | } |
michael@0 | 2496 | |
michael@0 | 2497 | NS_IMETHODIMP |
michael@0 | 2498 | nsHTMLEditor::NormalizeTable(nsIDOMElement *aTable) |
michael@0 | 2499 | { |
michael@0 | 2500 | nsRefPtr<Selection> selection = GetSelection(); |
michael@0 | 2501 | NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); |
michael@0 | 2502 | |
michael@0 | 2503 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 2504 | nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), |
michael@0 | 2505 | aTable, getter_AddRefs(table)); |
michael@0 | 2506 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2507 | // Don't fail if we didn't find a table |
michael@0 | 2508 | NS_ENSURE_TRUE(table, NS_OK); |
michael@0 | 2509 | |
michael@0 | 2510 | int32_t rowCount, colCount, rowIndex, colIndex; |
michael@0 | 2511 | res = GetTableSize(table, &rowCount, &colCount); |
michael@0 | 2512 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2513 | |
michael@0 | 2514 | // Save current selection |
michael@0 | 2515 | nsAutoSelectionReset selectionResetter(selection, this); |
michael@0 | 2516 | |
michael@0 | 2517 | nsAutoEditBatch beginBatching(this); |
michael@0 | 2518 | // Prevent auto insertion of BR in new cell until we're done |
michael@0 | 2519 | nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); |
michael@0 | 2520 | |
michael@0 | 2521 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 2522 | int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 2523 | bool isSelected; |
michael@0 | 2524 | |
michael@0 | 2525 | // Scan all cells in each row to detect bad rowspan values |
michael@0 | 2526 | for(rowIndex = 0; rowIndex < rowCount; rowIndex++) |
michael@0 | 2527 | { |
michael@0 | 2528 | res = FixBadRowSpan(table, rowIndex, rowCount); |
michael@0 | 2529 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2530 | } |
michael@0 | 2531 | // and same for colspans |
michael@0 | 2532 | for(colIndex = 0; colIndex < colCount; colIndex++) |
michael@0 | 2533 | { |
michael@0 | 2534 | res = FixBadColSpan(table, colIndex, colCount); |
michael@0 | 2535 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2536 | } |
michael@0 | 2537 | |
michael@0 | 2538 | // Fill in missing cellmap locations with empty cells |
michael@0 | 2539 | for(rowIndex = 0; rowIndex < rowCount; rowIndex++) |
michael@0 | 2540 | { |
michael@0 | 2541 | nsCOMPtr<nsIDOMElement> previousCellInRow; |
michael@0 | 2542 | |
michael@0 | 2543 | for(colIndex = 0; colIndex < colCount; colIndex++) |
michael@0 | 2544 | { |
michael@0 | 2545 | res = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell), |
michael@0 | 2546 | &startRowIndex, &startColIndex, &rowSpan, &colSpan, |
michael@0 | 2547 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 2548 | // NOTE: This is a *real* failure. |
michael@0 | 2549 | // GetCellDataAt passes if cell is missing from cellmap |
michael@0 | 2550 | if(NS_FAILED(res)) return res; |
michael@0 | 2551 | if (!cell) |
michael@0 | 2552 | { |
michael@0 | 2553 | //We are missing a cell at a cellmap location |
michael@0 | 2554 | #ifdef DEBUG |
michael@0 | 2555 | printf("NormalizeTable found missing cell at row=%d, col=%d\n", rowIndex, colIndex); |
michael@0 | 2556 | #endif |
michael@0 | 2557 | // Add a cell after the previous Cell in the current row |
michael@0 | 2558 | if(previousCellInRow) |
michael@0 | 2559 | { |
michael@0 | 2560 | // Insert a new cell after (true), and return the new cell to us |
michael@0 | 2561 | res = InsertCell(previousCellInRow, 1, 1, true, false, getter_AddRefs(cell)); |
michael@0 | 2562 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2563 | |
michael@0 | 2564 | // Set this so we use returned new "cell" to set previousCellInRow below |
michael@0 | 2565 | if(cell) |
michael@0 | 2566 | startRowIndex = rowIndex; |
michael@0 | 2567 | } else { |
michael@0 | 2568 | // We don't have any cells in this row -- We are really messed up! |
michael@0 | 2569 | #ifdef DEBUG |
michael@0 | 2570 | printf("NormalizeTable found no cells in row=%d, col=%d\n", rowIndex, colIndex); |
michael@0 | 2571 | #endif |
michael@0 | 2572 | return NS_ERROR_FAILURE; |
michael@0 | 2573 | } |
michael@0 | 2574 | } |
michael@0 | 2575 | // Save the last cell found in the same row we are scanning |
michael@0 | 2576 | if(startRowIndex == rowIndex) |
michael@0 | 2577 | { |
michael@0 | 2578 | previousCellInRow = cell; |
michael@0 | 2579 | } |
michael@0 | 2580 | } |
michael@0 | 2581 | } |
michael@0 | 2582 | return res; |
michael@0 | 2583 | } |
michael@0 | 2584 | |
michael@0 | 2585 | NS_IMETHODIMP |
michael@0 | 2586 | nsHTMLEditor::GetCellIndexes(nsIDOMElement *aCell, |
michael@0 | 2587 | int32_t *aRowIndex, int32_t *aColIndex) |
michael@0 | 2588 | { |
michael@0 | 2589 | NS_ENSURE_ARG_POINTER(aRowIndex); |
michael@0 | 2590 | *aColIndex=0; // initialize out params |
michael@0 | 2591 | NS_ENSURE_ARG_POINTER(aColIndex); |
michael@0 | 2592 | *aRowIndex=0; |
michael@0 | 2593 | nsresult res=NS_ERROR_NOT_INITIALIZED; |
michael@0 | 2594 | if (!aCell) |
michael@0 | 2595 | { |
michael@0 | 2596 | // Get the selected cell or the cell enclosing the selection anchor |
michael@0 | 2597 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 2598 | res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); |
michael@0 | 2599 | if (NS_SUCCEEDED(res) && cell) |
michael@0 | 2600 | aCell = cell; |
michael@0 | 2601 | else |
michael@0 | 2602 | return NS_ERROR_FAILURE; |
michael@0 | 2603 | } |
michael@0 | 2604 | |
michael@0 | 2605 | NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 2606 | nsCOMPtr<nsIPresShell> ps = GetPresShell(); |
michael@0 | 2607 | NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 2608 | |
michael@0 | 2609 | nsCOMPtr<nsIContent> nodeAsContent( do_QueryInterface(aCell) ); |
michael@0 | 2610 | NS_ENSURE_TRUE(nodeAsContent, NS_ERROR_FAILURE); |
michael@0 | 2611 | // frames are not ref counted, so don't use an nsCOMPtr |
michael@0 | 2612 | nsIFrame *layoutObject = nodeAsContent->GetPrimaryFrame(); |
michael@0 | 2613 | NS_ENSURE_TRUE(layoutObject, NS_ERROR_FAILURE); |
michael@0 | 2614 | |
michael@0 | 2615 | nsITableCellLayout *cellLayoutObject = do_QueryFrame(layoutObject); |
michael@0 | 2616 | NS_ENSURE_TRUE(cellLayoutObject, NS_ERROR_FAILURE); |
michael@0 | 2617 | return cellLayoutObject->GetCellIndexes(*aRowIndex, *aColIndex); |
michael@0 | 2618 | } |
michael@0 | 2619 | |
michael@0 | 2620 | nsTableOuterFrame* |
michael@0 | 2621 | nsHTMLEditor::GetTableFrame(nsIDOMElement* aTable) |
michael@0 | 2622 | { |
michael@0 | 2623 | NS_ENSURE_TRUE(aTable, nullptr); |
michael@0 | 2624 | |
michael@0 | 2625 | nsCOMPtr<nsIContent> nodeAsContent( do_QueryInterface(aTable) ); |
michael@0 | 2626 | NS_ENSURE_TRUE(nodeAsContent, nullptr); |
michael@0 | 2627 | return do_QueryFrame(nodeAsContent->GetPrimaryFrame()); |
michael@0 | 2628 | } |
michael@0 | 2629 | |
michael@0 | 2630 | //Return actual number of cells (a cell with colspan > 1 counts as just 1) |
michael@0 | 2631 | int32_t nsHTMLEditor::GetNumberOfCellsInRow(nsIDOMElement* aTable, int32_t rowIndex) |
michael@0 | 2632 | { |
michael@0 | 2633 | int32_t cellCount = 0; |
michael@0 | 2634 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 2635 | int32_t colIndex = 0; |
michael@0 | 2636 | nsresult res; |
michael@0 | 2637 | do { |
michael@0 | 2638 | int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 2639 | bool isSelected; |
michael@0 | 2640 | res = GetCellDataAt(aTable, rowIndex, colIndex, getter_AddRefs(cell), |
michael@0 | 2641 | &startRowIndex, &startColIndex, &rowSpan, &colSpan, |
michael@0 | 2642 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 2643 | NS_ENSURE_SUCCESS(res, 0); |
michael@0 | 2644 | if (cell) |
michael@0 | 2645 | { |
michael@0 | 2646 | // Only count cells that start in row we are working with |
michael@0 | 2647 | if (startRowIndex == rowIndex) |
michael@0 | 2648 | cellCount++; |
michael@0 | 2649 | |
michael@0 | 2650 | //Next possible location for a cell |
michael@0 | 2651 | colIndex += actualColSpan; |
michael@0 | 2652 | } |
michael@0 | 2653 | else |
michael@0 | 2654 | colIndex++; |
michael@0 | 2655 | |
michael@0 | 2656 | } while (cell); |
michael@0 | 2657 | |
michael@0 | 2658 | return cellCount; |
michael@0 | 2659 | } |
michael@0 | 2660 | |
michael@0 | 2661 | /* Not scriptable: For convenience in C++ |
michael@0 | 2662 | Use GetTableRowCount and GetTableColumnCount from JavaScript |
michael@0 | 2663 | */ |
michael@0 | 2664 | NS_IMETHODIMP |
michael@0 | 2665 | nsHTMLEditor::GetTableSize(nsIDOMElement *aTable, |
michael@0 | 2666 | int32_t* aRowCount, int32_t* aColCount) |
michael@0 | 2667 | { |
michael@0 | 2668 | NS_ENSURE_ARG_POINTER(aRowCount); |
michael@0 | 2669 | NS_ENSURE_ARG_POINTER(aColCount); |
michael@0 | 2670 | nsresult res; |
michael@0 | 2671 | *aRowCount = 0; |
michael@0 | 2672 | *aColCount = 0; |
michael@0 | 2673 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 2674 | // Get the selected talbe or the table enclosing the selection anchor |
michael@0 | 2675 | res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aTable, getter_AddRefs(table)); |
michael@0 | 2676 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2677 | NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); |
michael@0 | 2678 | |
michael@0 | 2679 | nsTableOuterFrame* tableFrame = GetTableFrame(table.get()); |
michael@0 | 2680 | NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE); |
michael@0 | 2681 | |
michael@0 | 2682 | *aRowCount = tableFrame->GetRowCount(); |
michael@0 | 2683 | *aColCount = tableFrame->GetColCount(); |
michael@0 | 2684 | |
michael@0 | 2685 | return NS_OK; |
michael@0 | 2686 | } |
michael@0 | 2687 | |
michael@0 | 2688 | NS_IMETHODIMP |
michael@0 | 2689 | nsHTMLEditor::GetCellDataAt(nsIDOMElement* aTable, int32_t aRowIndex, |
michael@0 | 2690 | int32_t aColIndex, nsIDOMElement **aCell, |
michael@0 | 2691 | int32_t* aStartRowIndex, int32_t* aStartColIndex, |
michael@0 | 2692 | int32_t* aRowSpan, int32_t* aColSpan, |
michael@0 | 2693 | int32_t* aActualRowSpan, int32_t* aActualColSpan, |
michael@0 | 2694 | bool* aIsSelected) |
michael@0 | 2695 | { |
michael@0 | 2696 | NS_ENSURE_ARG_POINTER(aStartRowIndex); |
michael@0 | 2697 | NS_ENSURE_ARG_POINTER(aStartColIndex); |
michael@0 | 2698 | NS_ENSURE_ARG_POINTER(aRowSpan); |
michael@0 | 2699 | NS_ENSURE_ARG_POINTER(aColSpan); |
michael@0 | 2700 | NS_ENSURE_ARG_POINTER(aActualRowSpan); |
michael@0 | 2701 | NS_ENSURE_ARG_POINTER(aActualColSpan); |
michael@0 | 2702 | NS_ENSURE_ARG_POINTER(aIsSelected); |
michael@0 | 2703 | NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); |
michael@0 | 2704 | |
michael@0 | 2705 | nsresult res=NS_ERROR_FAILURE; |
michael@0 | 2706 | *aStartRowIndex = 0; |
michael@0 | 2707 | *aStartColIndex = 0; |
michael@0 | 2708 | *aRowSpan = 0; |
michael@0 | 2709 | *aColSpan = 0; |
michael@0 | 2710 | *aActualRowSpan = 0; |
michael@0 | 2711 | *aActualColSpan = 0; |
michael@0 | 2712 | *aIsSelected = false; |
michael@0 | 2713 | |
michael@0 | 2714 | *aCell = nullptr; |
michael@0 | 2715 | |
michael@0 | 2716 | if (!aTable) |
michael@0 | 2717 | { |
michael@0 | 2718 | // Get the selected table or the table enclosing the selection anchor |
michael@0 | 2719 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 2720 | res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr, getter_AddRefs(table)); |
michael@0 | 2721 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2722 | if (table) |
michael@0 | 2723 | aTable = table; |
michael@0 | 2724 | else |
michael@0 | 2725 | return NS_ERROR_FAILURE; |
michael@0 | 2726 | } |
michael@0 | 2727 | |
michael@0 | 2728 | nsTableOuterFrame* tableFrame = GetTableFrame(aTable); |
michael@0 | 2729 | NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE); |
michael@0 | 2730 | |
michael@0 | 2731 | nsTableCellFrame* cellFrame = |
michael@0 | 2732 | tableFrame->GetCellFrameAt(aRowIndex, aColIndex); |
michael@0 | 2733 | if (!cellFrame) |
michael@0 | 2734 | return NS_ERROR_FAILURE; |
michael@0 | 2735 | |
michael@0 | 2736 | *aIsSelected = cellFrame->IsSelected(); |
michael@0 | 2737 | cellFrame->GetRowIndex(*aStartRowIndex); |
michael@0 | 2738 | cellFrame->GetColIndex(*aStartColIndex); |
michael@0 | 2739 | *aRowSpan = cellFrame->GetRowSpan(); |
michael@0 | 2740 | *aColSpan = cellFrame->GetColSpan(); |
michael@0 | 2741 | *aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex); |
michael@0 | 2742 | *aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex); |
michael@0 | 2743 | nsCOMPtr<nsIDOMElement> domCell = do_QueryInterface(cellFrame->GetContent()); |
michael@0 | 2744 | domCell.forget(aCell); |
michael@0 | 2745 | |
michael@0 | 2746 | return NS_OK; |
michael@0 | 2747 | } |
michael@0 | 2748 | |
michael@0 | 2749 | // When all you want is the cell |
michael@0 | 2750 | NS_IMETHODIMP |
michael@0 | 2751 | nsHTMLEditor::GetCellAt(nsIDOMElement* aTable, int32_t aRowIndex, int32_t aColIndex, nsIDOMElement **aCell) |
michael@0 | 2752 | { |
michael@0 | 2753 | NS_ENSURE_ARG_POINTER(aCell); |
michael@0 | 2754 | *aCell = nullptr; |
michael@0 | 2755 | |
michael@0 | 2756 | if (!aTable) |
michael@0 | 2757 | { |
michael@0 | 2758 | // Get the selected table or the table enclosing the selection anchor |
michael@0 | 2759 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 2760 | nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr, getter_AddRefs(table)); |
michael@0 | 2761 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2762 | NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); |
michael@0 | 2763 | aTable = table; |
michael@0 | 2764 | } |
michael@0 | 2765 | |
michael@0 | 2766 | nsTableOuterFrame* tableFrame = GetTableFrame(aTable); |
michael@0 | 2767 | if (!tableFrame) |
michael@0 | 2768 | return NS_ERROR_FAILURE; |
michael@0 | 2769 | |
michael@0 | 2770 | nsCOMPtr<nsIDOMElement> domCell = |
michael@0 | 2771 | do_QueryInterface(tableFrame->GetCellAt(aRowIndex, aColIndex)); |
michael@0 | 2772 | domCell.forget(aCell); |
michael@0 | 2773 | |
michael@0 | 2774 | return NS_OK; |
michael@0 | 2775 | } |
michael@0 | 2776 | |
michael@0 | 2777 | // When all you want are the rowspan and colspan (not exposed in nsITableEditor) |
michael@0 | 2778 | NS_IMETHODIMP |
michael@0 | 2779 | nsHTMLEditor::GetCellSpansAt(nsIDOMElement* aTable, int32_t aRowIndex, int32_t aColIndex, |
michael@0 | 2780 | int32_t& aActualRowSpan, int32_t& aActualColSpan) |
michael@0 | 2781 | { |
michael@0 | 2782 | nsTableOuterFrame* tableFrame = GetTableFrame(aTable); |
michael@0 | 2783 | if (!tableFrame) |
michael@0 | 2784 | return NS_ERROR_FAILURE; |
michael@0 | 2785 | |
michael@0 | 2786 | aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex); |
michael@0 | 2787 | aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex); |
michael@0 | 2788 | |
michael@0 | 2789 | return NS_OK; |
michael@0 | 2790 | } |
michael@0 | 2791 | |
michael@0 | 2792 | NS_IMETHODIMP |
michael@0 | 2793 | nsHTMLEditor::GetCellContext(nsISelection **aSelection, |
michael@0 | 2794 | nsIDOMElement **aTable, |
michael@0 | 2795 | nsIDOMElement **aCell, |
michael@0 | 2796 | nsIDOMNode **aCellParent, int32_t *aCellOffset, |
michael@0 | 2797 | int32_t *aRowIndex, int32_t *aColIndex) |
michael@0 | 2798 | { |
michael@0 | 2799 | // Initialize return pointers |
michael@0 | 2800 | if (aSelection) *aSelection = nullptr; |
michael@0 | 2801 | if (aTable) *aTable = nullptr; |
michael@0 | 2802 | if (aCell) *aCell = nullptr; |
michael@0 | 2803 | if (aCellParent) *aCellParent = nullptr; |
michael@0 | 2804 | if (aCellOffset) *aCellOffset = 0; |
michael@0 | 2805 | if (aRowIndex) *aRowIndex = 0; |
michael@0 | 2806 | if (aColIndex) *aColIndex = 0; |
michael@0 | 2807 | |
michael@0 | 2808 | nsCOMPtr <nsISelection> selection; |
michael@0 | 2809 | nsresult res = GetSelection(getter_AddRefs(selection)); |
michael@0 | 2810 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2811 | NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); |
michael@0 | 2812 | |
michael@0 | 2813 | if (aSelection) |
michael@0 | 2814 | { |
michael@0 | 2815 | *aSelection = selection.get(); |
michael@0 | 2816 | NS_ADDREF(*aSelection); |
michael@0 | 2817 | } |
michael@0 | 2818 | nsCOMPtr <nsIDOMElement> table; |
michael@0 | 2819 | nsCOMPtr <nsIDOMElement> cell; |
michael@0 | 2820 | |
michael@0 | 2821 | // Caller may supply the cell... |
michael@0 | 2822 | if (aCell && *aCell) |
michael@0 | 2823 | cell = *aCell; |
michael@0 | 2824 | |
michael@0 | 2825 | // ...but if not supplied, |
michael@0 | 2826 | // get cell if it's the child of selection anchor node, |
michael@0 | 2827 | // or get the enclosing by a cell |
michael@0 | 2828 | if (!cell) |
michael@0 | 2829 | { |
michael@0 | 2830 | // Find a selected or enclosing table element |
michael@0 | 2831 | nsCOMPtr<nsIDOMElement> cellOrTableElement; |
michael@0 | 2832 | int32_t selectedCount; |
michael@0 | 2833 | nsAutoString tagName; |
michael@0 | 2834 | res = GetSelectedOrParentTableElement(tagName, &selectedCount, |
michael@0 | 2835 | getter_AddRefs(cellOrTableElement)); |
michael@0 | 2836 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2837 | if (tagName.EqualsLiteral("table")) |
michael@0 | 2838 | { |
michael@0 | 2839 | // We have a selected table, not a cell |
michael@0 | 2840 | if (aTable) |
michael@0 | 2841 | { |
michael@0 | 2842 | *aTable = cellOrTableElement.get(); |
michael@0 | 2843 | NS_ADDREF(*aTable); |
michael@0 | 2844 | } |
michael@0 | 2845 | return NS_OK; |
michael@0 | 2846 | } |
michael@0 | 2847 | if (!tagName.EqualsLiteral("td")) |
michael@0 | 2848 | return NS_EDITOR_ELEMENT_NOT_FOUND; |
michael@0 | 2849 | |
michael@0 | 2850 | // We found a cell |
michael@0 | 2851 | cell = cellOrTableElement; |
michael@0 | 2852 | } |
michael@0 | 2853 | if (aCell) |
michael@0 | 2854 | { |
michael@0 | 2855 | *aCell = cell.get(); |
michael@0 | 2856 | NS_ADDREF(*aCell); |
michael@0 | 2857 | } |
michael@0 | 2858 | |
michael@0 | 2859 | // Get containing table |
michael@0 | 2860 | res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), cell, getter_AddRefs(table)); |
michael@0 | 2861 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2862 | // Cell must be in a table, so fail if not found |
michael@0 | 2863 | NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); |
michael@0 | 2864 | if (aTable) |
michael@0 | 2865 | { |
michael@0 | 2866 | *aTable = table.get(); |
michael@0 | 2867 | NS_ADDREF(*aTable); |
michael@0 | 2868 | } |
michael@0 | 2869 | |
michael@0 | 2870 | // Get the rest of the related data only if requested |
michael@0 | 2871 | if (aRowIndex || aColIndex) |
michael@0 | 2872 | { |
michael@0 | 2873 | int32_t rowIndex, colIndex; |
michael@0 | 2874 | // Get current cell location so we can put caret back there when done |
michael@0 | 2875 | res = GetCellIndexes(cell, &rowIndex, &colIndex); |
michael@0 | 2876 | if(NS_FAILED(res)) return res; |
michael@0 | 2877 | if (aRowIndex) *aRowIndex = rowIndex; |
michael@0 | 2878 | if (aColIndex) *aColIndex = colIndex; |
michael@0 | 2879 | } |
michael@0 | 2880 | if (aCellParent) |
michael@0 | 2881 | { |
michael@0 | 2882 | nsCOMPtr <nsIDOMNode> cellParent; |
michael@0 | 2883 | // Get the immediate parent of the cell |
michael@0 | 2884 | res = cell->GetParentNode(getter_AddRefs(cellParent)); |
michael@0 | 2885 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2886 | // Cell has to have a parent, so fail if not found |
michael@0 | 2887 | NS_ENSURE_TRUE(cellParent, NS_ERROR_FAILURE); |
michael@0 | 2888 | |
michael@0 | 2889 | *aCellParent = cellParent.get(); |
michael@0 | 2890 | NS_ADDREF(*aCellParent); |
michael@0 | 2891 | |
michael@0 | 2892 | if (aCellOffset) { |
michael@0 | 2893 | *aCellOffset = GetChildOffset(cell, cellParent); |
michael@0 | 2894 | } |
michael@0 | 2895 | } |
michael@0 | 2896 | |
michael@0 | 2897 | return res; |
michael@0 | 2898 | } |
michael@0 | 2899 | |
michael@0 | 2900 | nsresult |
michael@0 | 2901 | nsHTMLEditor::GetCellFromRange(nsIDOMRange *aRange, nsIDOMElement **aCell) |
michael@0 | 2902 | { |
michael@0 | 2903 | // Note: this might return a node that is outside of the range. |
michael@0 | 2904 | // Use carefully. |
michael@0 | 2905 | NS_ENSURE_TRUE(aRange && aCell, NS_ERROR_NULL_POINTER); |
michael@0 | 2906 | |
michael@0 | 2907 | *aCell = nullptr; |
michael@0 | 2908 | |
michael@0 | 2909 | nsCOMPtr<nsIDOMNode> startParent; |
michael@0 | 2910 | nsresult res = aRange->GetStartContainer(getter_AddRefs(startParent)); |
michael@0 | 2911 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2912 | NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE); |
michael@0 | 2913 | |
michael@0 | 2914 | int32_t startOffset; |
michael@0 | 2915 | res = aRange->GetStartOffset(&startOffset); |
michael@0 | 2916 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2917 | |
michael@0 | 2918 | nsCOMPtr<nsIDOMNode> childNode = GetChildAt(startParent, startOffset); |
michael@0 | 2919 | // This means selection is probably at a text node (or end of doc?) |
michael@0 | 2920 | if (!childNode) { |
michael@0 | 2921 | return NS_ERROR_FAILURE; |
michael@0 | 2922 | } |
michael@0 | 2923 | |
michael@0 | 2924 | nsCOMPtr<nsIDOMNode> endParent; |
michael@0 | 2925 | res = aRange->GetEndContainer(getter_AddRefs(endParent)); |
michael@0 | 2926 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2927 | NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE); |
michael@0 | 2928 | |
michael@0 | 2929 | int32_t endOffset; |
michael@0 | 2930 | res = aRange->GetEndOffset(&endOffset); |
michael@0 | 2931 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2932 | |
michael@0 | 2933 | // If a cell is deleted, the range is collapse |
michael@0 | 2934 | // (startOffset == endOffset) |
michael@0 | 2935 | // so tell caller the cell wasn't found |
michael@0 | 2936 | if (startParent == endParent && |
michael@0 | 2937 | endOffset == startOffset+1 && |
michael@0 | 2938 | nsHTMLEditUtils::IsTableCell(childNode)) |
michael@0 | 2939 | { |
michael@0 | 2940 | // Should we also test if frame is selected? (Use GetCellDataAt()) |
michael@0 | 2941 | // (Let's not for now -- more efficient) |
michael@0 | 2942 | nsCOMPtr<nsIDOMElement> cellElement = do_QueryInterface(childNode); |
michael@0 | 2943 | *aCell = cellElement.get(); |
michael@0 | 2944 | NS_ADDREF(*aCell); |
michael@0 | 2945 | return NS_OK; |
michael@0 | 2946 | } |
michael@0 | 2947 | return NS_EDITOR_ELEMENT_NOT_FOUND; |
michael@0 | 2948 | } |
michael@0 | 2949 | |
michael@0 | 2950 | NS_IMETHODIMP |
michael@0 | 2951 | nsHTMLEditor::GetFirstSelectedCell(nsIDOMRange **aRange, nsIDOMElement **aCell) |
michael@0 | 2952 | { |
michael@0 | 2953 | NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); |
michael@0 | 2954 | *aCell = nullptr; |
michael@0 | 2955 | if (aRange) *aRange = nullptr; |
michael@0 | 2956 | |
michael@0 | 2957 | nsCOMPtr<nsISelection> selection; |
michael@0 | 2958 | nsresult res = GetSelection(getter_AddRefs(selection)); |
michael@0 | 2959 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2960 | NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); |
michael@0 | 2961 | |
michael@0 | 2962 | nsCOMPtr<nsIDOMRange> range; |
michael@0 | 2963 | res = selection->GetRangeAt(0, getter_AddRefs(range)); |
michael@0 | 2964 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 2965 | NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); |
michael@0 | 2966 | |
michael@0 | 2967 | mSelectedCellIndex = 0; |
michael@0 | 2968 | |
michael@0 | 2969 | res = GetCellFromRange(range, aCell); |
michael@0 | 2970 | // Failure here probably means selection is in a text node, |
michael@0 | 2971 | // so there's no selected cell |
michael@0 | 2972 | if (NS_FAILED(res)) { |
michael@0 | 2973 | return NS_EDITOR_ELEMENT_NOT_FOUND; |
michael@0 | 2974 | } |
michael@0 | 2975 | // No cell means range was collapsed (cell was deleted) |
michael@0 | 2976 | if (!*aCell) { |
michael@0 | 2977 | return NS_EDITOR_ELEMENT_NOT_FOUND; |
michael@0 | 2978 | } |
michael@0 | 2979 | |
michael@0 | 2980 | if (aRange) |
michael@0 | 2981 | { |
michael@0 | 2982 | *aRange = range.get(); |
michael@0 | 2983 | NS_ADDREF(*aRange); |
michael@0 | 2984 | } |
michael@0 | 2985 | |
michael@0 | 2986 | // Setup for next cell |
michael@0 | 2987 | mSelectedCellIndex = 1; |
michael@0 | 2988 | |
michael@0 | 2989 | return res; |
michael@0 | 2990 | } |
michael@0 | 2991 | |
michael@0 | 2992 | NS_IMETHODIMP |
michael@0 | 2993 | nsHTMLEditor::GetNextSelectedCell(nsIDOMRange **aRange, nsIDOMElement **aCell) |
michael@0 | 2994 | { |
michael@0 | 2995 | NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); |
michael@0 | 2996 | *aCell = nullptr; |
michael@0 | 2997 | if (aRange) *aRange = nullptr; |
michael@0 | 2998 | |
michael@0 | 2999 | nsCOMPtr<nsISelection> selection; |
michael@0 | 3000 | nsresult res = GetSelection(getter_AddRefs(selection)); |
michael@0 | 3001 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 3002 | NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); |
michael@0 | 3003 | |
michael@0 | 3004 | int32_t rangeCount; |
michael@0 | 3005 | res = selection->GetRangeCount(&rangeCount); |
michael@0 | 3006 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 3007 | |
michael@0 | 3008 | // Don't even try if index exceeds range count |
michael@0 | 3009 | if (mSelectedCellIndex >= rangeCount) |
michael@0 | 3010 | return NS_EDITOR_ELEMENT_NOT_FOUND; |
michael@0 | 3011 | |
michael@0 | 3012 | // Scan through ranges to find next valid selected cell |
michael@0 | 3013 | nsCOMPtr<nsIDOMRange> range; |
michael@0 | 3014 | for (; mSelectedCellIndex < rangeCount; mSelectedCellIndex++) |
michael@0 | 3015 | { |
michael@0 | 3016 | res = selection->GetRangeAt(mSelectedCellIndex, getter_AddRefs(range)); |
michael@0 | 3017 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 3018 | NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); |
michael@0 | 3019 | |
michael@0 | 3020 | res = GetCellFromRange(range, aCell); |
michael@0 | 3021 | // Failure here means the range doesn't contain a cell |
michael@0 | 3022 | NS_ENSURE_SUCCESS(res, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 3023 | |
michael@0 | 3024 | // We found a selected cell |
michael@0 | 3025 | if (*aCell) break; |
michael@0 | 3026 | #ifdef DEBUG_cmanske |
michael@0 | 3027 | else |
michael@0 | 3028 | printf("GetNextSelectedCell: Collapsed range found\n"); |
michael@0 | 3029 | #endif |
michael@0 | 3030 | |
michael@0 | 3031 | // If we didn't find a cell, continue to next range in selection |
michael@0 | 3032 | } |
michael@0 | 3033 | // No cell means all remaining ranges were collapsed (cells were deleted) |
michael@0 | 3034 | NS_ENSURE_TRUE(*aCell, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 3035 | |
michael@0 | 3036 | if (aRange) |
michael@0 | 3037 | { |
michael@0 | 3038 | *aRange = range.get(); |
michael@0 | 3039 | NS_ADDREF(*aRange); |
michael@0 | 3040 | } |
michael@0 | 3041 | |
michael@0 | 3042 | // Setup for next cell |
michael@0 | 3043 | mSelectedCellIndex++; |
michael@0 | 3044 | |
michael@0 | 3045 | return res; |
michael@0 | 3046 | } |
michael@0 | 3047 | |
michael@0 | 3048 | NS_IMETHODIMP |
michael@0 | 3049 | nsHTMLEditor::GetFirstSelectedCellInTable(int32_t *aRowIndex, int32_t *aColIndex, nsIDOMElement **aCell) |
michael@0 | 3050 | { |
michael@0 | 3051 | NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); |
michael@0 | 3052 | *aCell = nullptr; |
michael@0 | 3053 | if (aRowIndex) |
michael@0 | 3054 | *aRowIndex = 0; |
michael@0 | 3055 | if (aColIndex) |
michael@0 | 3056 | *aColIndex = 0; |
michael@0 | 3057 | |
michael@0 | 3058 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 3059 | nsresult res = GetFirstSelectedCell(nullptr, getter_AddRefs(cell)); |
michael@0 | 3060 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 3061 | NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); |
michael@0 | 3062 | |
michael@0 | 3063 | *aCell = cell.get(); |
michael@0 | 3064 | NS_ADDREF(*aCell); |
michael@0 | 3065 | |
michael@0 | 3066 | // Also return the row and/or column if requested |
michael@0 | 3067 | if (aRowIndex || aColIndex) |
michael@0 | 3068 | { |
michael@0 | 3069 | int32_t startRowIndex, startColIndex; |
michael@0 | 3070 | res = GetCellIndexes(cell, &startRowIndex, &startColIndex); |
michael@0 | 3071 | if(NS_FAILED(res)) return res; |
michael@0 | 3072 | |
michael@0 | 3073 | if (aRowIndex) |
michael@0 | 3074 | *aRowIndex = startRowIndex; |
michael@0 | 3075 | |
michael@0 | 3076 | if (aColIndex) |
michael@0 | 3077 | *aColIndex = startColIndex; |
michael@0 | 3078 | } |
michael@0 | 3079 | |
michael@0 | 3080 | return res; |
michael@0 | 3081 | } |
michael@0 | 3082 | |
michael@0 | 3083 | NS_IMETHODIMP |
michael@0 | 3084 | nsHTMLEditor::SetSelectionAfterTableEdit(nsIDOMElement* aTable, int32_t aRow, int32_t aCol, |
michael@0 | 3085 | int32_t aDirection, bool aSelected) |
michael@0 | 3086 | { |
michael@0 | 3087 | NS_ENSURE_TRUE(aTable, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 3088 | |
michael@0 | 3089 | nsCOMPtr<nsISelection>selection; |
michael@0 | 3090 | nsresult res = GetSelection(getter_AddRefs(selection)); |
michael@0 | 3091 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 3092 | |
michael@0 | 3093 | if (!selection) |
michael@0 | 3094 | { |
michael@0 | 3095 | #ifdef DEBUG_cmanske |
michael@0 | 3096 | printf("Selection not found after table manipulation!\n"); |
michael@0 | 3097 | #endif |
michael@0 | 3098 | return NS_ERROR_FAILURE; |
michael@0 | 3099 | } |
michael@0 | 3100 | |
michael@0 | 3101 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 3102 | bool done = false; |
michael@0 | 3103 | do { |
michael@0 | 3104 | res = GetCellAt(aTable, aRow, aCol, getter_AddRefs(cell)); |
michael@0 | 3105 | if (NS_SUCCEEDED(res)) |
michael@0 | 3106 | { |
michael@0 | 3107 | if (cell) |
michael@0 | 3108 | { |
michael@0 | 3109 | if (aSelected) |
michael@0 | 3110 | { |
michael@0 | 3111 | // Reselect the cell |
michael@0 | 3112 | return SelectElement(cell); |
michael@0 | 3113 | } |
michael@0 | 3114 | else |
michael@0 | 3115 | { |
michael@0 | 3116 | // Set the caret to deepest first child |
michael@0 | 3117 | // but don't go into nested tables |
michael@0 | 3118 | // TODO: Should we really be placing the caret at the END |
michael@0 | 3119 | // of the cell content? |
michael@0 | 3120 | return CollapseSelectionToDeepestNonTableFirstChild(selection, cell); |
michael@0 | 3121 | } |
michael@0 | 3122 | } else { |
michael@0 | 3123 | // Setup index to find another cell in the |
michael@0 | 3124 | // direction requested, but move in |
michael@0 | 3125 | // other direction if already at beginning of row or column |
michael@0 | 3126 | switch (aDirection) |
michael@0 | 3127 | { |
michael@0 | 3128 | case ePreviousColumn: |
michael@0 | 3129 | if (aCol == 0) |
michael@0 | 3130 | { |
michael@0 | 3131 | if (aRow > 0) |
michael@0 | 3132 | aRow--; |
michael@0 | 3133 | else |
michael@0 | 3134 | done = true; |
michael@0 | 3135 | } |
michael@0 | 3136 | else |
michael@0 | 3137 | aCol--; |
michael@0 | 3138 | break; |
michael@0 | 3139 | case ePreviousRow: |
michael@0 | 3140 | if (aRow == 0) |
michael@0 | 3141 | { |
michael@0 | 3142 | if (aCol > 0) |
michael@0 | 3143 | aCol--; |
michael@0 | 3144 | else |
michael@0 | 3145 | done = true; |
michael@0 | 3146 | } |
michael@0 | 3147 | else |
michael@0 | 3148 | aRow--; |
michael@0 | 3149 | break; |
michael@0 | 3150 | default: |
michael@0 | 3151 | done = true; |
michael@0 | 3152 | } |
michael@0 | 3153 | } |
michael@0 | 3154 | } |
michael@0 | 3155 | else |
michael@0 | 3156 | break; |
michael@0 | 3157 | } while (!done); |
michael@0 | 3158 | |
michael@0 | 3159 | // We didn't find a cell |
michael@0 | 3160 | // Set selection to just before the table |
michael@0 | 3161 | nsCOMPtr<nsIDOMNode> tableParent; |
michael@0 | 3162 | res = aTable->GetParentNode(getter_AddRefs(tableParent)); |
michael@0 | 3163 | if(NS_SUCCEEDED(res) && tableParent) |
michael@0 | 3164 | { |
michael@0 | 3165 | int32_t tableOffset = GetChildOffset(aTable, tableParent); |
michael@0 | 3166 | return selection->Collapse(tableParent, tableOffset); |
michael@0 | 3167 | } |
michael@0 | 3168 | // Last resort: Set selection to start of doc |
michael@0 | 3169 | // (it's very bad to not have a valid selection!) |
michael@0 | 3170 | return SetSelectionAtDocumentStart(selection); |
michael@0 | 3171 | } |
michael@0 | 3172 | |
michael@0 | 3173 | NS_IMETHODIMP |
michael@0 | 3174 | nsHTMLEditor::GetSelectedOrParentTableElement(nsAString& aTagName, |
michael@0 | 3175 | int32_t *aSelectedCount, |
michael@0 | 3176 | nsIDOMElement** aTableElement) |
michael@0 | 3177 | { |
michael@0 | 3178 | NS_ENSURE_ARG_POINTER(aTableElement); |
michael@0 | 3179 | NS_ENSURE_ARG_POINTER(aSelectedCount); |
michael@0 | 3180 | *aTableElement = nullptr; |
michael@0 | 3181 | aTagName.Truncate(); |
michael@0 | 3182 | *aSelectedCount = 0; |
michael@0 | 3183 | |
michael@0 | 3184 | nsCOMPtr<nsISelection> selection; |
michael@0 | 3185 | nsresult res = GetSelection(getter_AddRefs(selection)); |
michael@0 | 3186 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 3187 | NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); |
michael@0 | 3188 | |
michael@0 | 3189 | // Try to get the first selected cell |
michael@0 | 3190 | nsCOMPtr<nsIDOMElement> tableOrCellElement; |
michael@0 | 3191 | res = GetFirstSelectedCell(nullptr, getter_AddRefs(tableOrCellElement)); |
michael@0 | 3192 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 3193 | |
michael@0 | 3194 | NS_NAMED_LITERAL_STRING(tdName, "td"); |
michael@0 | 3195 | |
michael@0 | 3196 | if (tableOrCellElement) |
michael@0 | 3197 | { |
michael@0 | 3198 | // Each cell is in its own selection range, |
michael@0 | 3199 | // so count signals multiple-cell selection |
michael@0 | 3200 | res = selection->GetRangeCount(aSelectedCount); |
michael@0 | 3201 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 3202 | aTagName = tdName; |
michael@0 | 3203 | } |
michael@0 | 3204 | else |
michael@0 | 3205 | { |
michael@0 | 3206 | nsCOMPtr<nsIDOMNode> anchorNode; |
michael@0 | 3207 | res = selection->GetAnchorNode(getter_AddRefs(anchorNode)); |
michael@0 | 3208 | if(NS_FAILED(res)) return res; |
michael@0 | 3209 | NS_ENSURE_TRUE(anchorNode, NS_ERROR_FAILURE); |
michael@0 | 3210 | |
michael@0 | 3211 | nsCOMPtr<nsIDOMNode> selectedNode; |
michael@0 | 3212 | |
michael@0 | 3213 | // Get child of anchor node, if exists |
michael@0 | 3214 | bool hasChildren; |
michael@0 | 3215 | anchorNode->HasChildNodes(&hasChildren); |
michael@0 | 3216 | |
michael@0 | 3217 | if (hasChildren) |
michael@0 | 3218 | { |
michael@0 | 3219 | int32_t anchorOffset; |
michael@0 | 3220 | res = selection->GetAnchorOffset(&anchorOffset); |
michael@0 | 3221 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 3222 | selectedNode = GetChildAt(anchorNode, anchorOffset); |
michael@0 | 3223 | if (!selectedNode) |
michael@0 | 3224 | { |
michael@0 | 3225 | selectedNode = anchorNode; |
michael@0 | 3226 | // If anchor doesn't have a child, we can't be selecting a table element, |
michael@0 | 3227 | // so don't do the following: |
michael@0 | 3228 | } |
michael@0 | 3229 | else |
michael@0 | 3230 | { |
michael@0 | 3231 | nsCOMPtr<nsIAtom> atom = nsEditor::GetTag(selectedNode); |
michael@0 | 3232 | |
michael@0 | 3233 | if (atom == nsEditProperty::td) |
michael@0 | 3234 | { |
michael@0 | 3235 | tableOrCellElement = do_QueryInterface(selectedNode); |
michael@0 | 3236 | aTagName = tdName; |
michael@0 | 3237 | // Each cell is in its own selection range, |
michael@0 | 3238 | // so count signals multiple-cell selection |
michael@0 | 3239 | res = selection->GetRangeCount(aSelectedCount); |
michael@0 | 3240 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 3241 | } |
michael@0 | 3242 | else if (atom == nsEditProperty::table) |
michael@0 | 3243 | { |
michael@0 | 3244 | tableOrCellElement = do_QueryInterface(selectedNode); |
michael@0 | 3245 | aTagName.AssignLiteral("table"); |
michael@0 | 3246 | *aSelectedCount = 1; |
michael@0 | 3247 | } |
michael@0 | 3248 | else if (atom == nsEditProperty::tr) |
michael@0 | 3249 | { |
michael@0 | 3250 | tableOrCellElement = do_QueryInterface(selectedNode); |
michael@0 | 3251 | aTagName.AssignLiteral("tr"); |
michael@0 | 3252 | *aSelectedCount = 1; |
michael@0 | 3253 | } |
michael@0 | 3254 | } |
michael@0 | 3255 | } |
michael@0 | 3256 | if (!tableOrCellElement) |
michael@0 | 3257 | { |
michael@0 | 3258 | // Didn't find a table element -- find a cell parent |
michael@0 | 3259 | res = GetElementOrParentByTagName(tdName, anchorNode, getter_AddRefs(tableOrCellElement)); |
michael@0 | 3260 | if(NS_FAILED(res)) return res; |
michael@0 | 3261 | if (tableOrCellElement) |
michael@0 | 3262 | aTagName = tdName; |
michael@0 | 3263 | } |
michael@0 | 3264 | } |
michael@0 | 3265 | if (tableOrCellElement) |
michael@0 | 3266 | { |
michael@0 | 3267 | *aTableElement = tableOrCellElement.get(); |
michael@0 | 3268 | NS_ADDREF(*aTableElement); |
michael@0 | 3269 | } |
michael@0 | 3270 | return res; |
michael@0 | 3271 | } |
michael@0 | 3272 | |
michael@0 | 3273 | NS_IMETHODIMP |
michael@0 | 3274 | nsHTMLEditor::GetSelectedCellsType(nsIDOMElement *aElement, uint32_t *aSelectionType) |
michael@0 | 3275 | { |
michael@0 | 3276 | NS_ENSURE_ARG_POINTER(aSelectionType); |
michael@0 | 3277 | *aSelectionType = 0; |
michael@0 | 3278 | |
michael@0 | 3279 | // Be sure we have a table element |
michael@0 | 3280 | // (if aElement is null, this uses selection's anchor node) |
michael@0 | 3281 | nsCOMPtr<nsIDOMElement> table; |
michael@0 | 3282 | |
michael@0 | 3283 | nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aElement, getter_AddRefs(table)); |
michael@0 | 3284 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 3285 | |
michael@0 | 3286 | int32_t rowCount, colCount; |
michael@0 | 3287 | res = GetTableSize(table, &rowCount, &colCount); |
michael@0 | 3288 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 3289 | |
michael@0 | 3290 | // Traverse all selected cells |
michael@0 | 3291 | nsCOMPtr<nsIDOMElement> selectedCell; |
michael@0 | 3292 | res = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell)); |
michael@0 | 3293 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 3294 | if (res == NS_EDITOR_ELEMENT_NOT_FOUND) return NS_OK; |
michael@0 | 3295 | |
michael@0 | 3296 | // We have at least one selected cell, so set return value |
michael@0 | 3297 | *aSelectionType = nsISelectionPrivate::TABLESELECTION_CELL; |
michael@0 | 3298 | |
michael@0 | 3299 | // Store indexes of each row/col to avoid duplication of searches |
michael@0 | 3300 | nsTArray<int32_t> indexArray; |
michael@0 | 3301 | |
michael@0 | 3302 | bool allCellsInRowAreSelected = false; |
michael@0 | 3303 | bool allCellsInColAreSelected = false; |
michael@0 | 3304 | while (NS_SUCCEEDED(res) && selectedCell) |
michael@0 | 3305 | { |
michael@0 | 3306 | // Get the cell's location in the cellmap |
michael@0 | 3307 | int32_t startRowIndex, startColIndex; |
michael@0 | 3308 | res = GetCellIndexes(selectedCell, &startRowIndex, &startColIndex); |
michael@0 | 3309 | if(NS_FAILED(res)) return res; |
michael@0 | 3310 | |
michael@0 | 3311 | if (!indexArray.Contains(startColIndex)) |
michael@0 | 3312 | { |
michael@0 | 3313 | indexArray.AppendElement(startColIndex); |
michael@0 | 3314 | allCellsInRowAreSelected = AllCellsInRowSelected(table, startRowIndex, colCount); |
michael@0 | 3315 | // We're done as soon as we fail for any row |
michael@0 | 3316 | if (!allCellsInRowAreSelected) break; |
michael@0 | 3317 | } |
michael@0 | 3318 | res = GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell)); |
michael@0 | 3319 | } |
michael@0 | 3320 | |
michael@0 | 3321 | if (allCellsInRowAreSelected) |
michael@0 | 3322 | { |
michael@0 | 3323 | *aSelectionType = nsISelectionPrivate::TABLESELECTION_ROW; |
michael@0 | 3324 | return NS_OK; |
michael@0 | 3325 | } |
michael@0 | 3326 | // Test for columns |
michael@0 | 3327 | |
michael@0 | 3328 | // Empty the indexArray |
michael@0 | 3329 | indexArray.Clear(); |
michael@0 | 3330 | |
michael@0 | 3331 | // Start at first cell again |
michael@0 | 3332 | res = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell)); |
michael@0 | 3333 | while (NS_SUCCEEDED(res) && selectedCell) |
michael@0 | 3334 | { |
michael@0 | 3335 | // Get the cell's location in the cellmap |
michael@0 | 3336 | int32_t startRowIndex, startColIndex; |
michael@0 | 3337 | res = GetCellIndexes(selectedCell, &startRowIndex, &startColIndex); |
michael@0 | 3338 | if(NS_FAILED(res)) return res; |
michael@0 | 3339 | |
michael@0 | 3340 | if (!indexArray.Contains(startRowIndex)) |
michael@0 | 3341 | { |
michael@0 | 3342 | indexArray.AppendElement(startColIndex); |
michael@0 | 3343 | allCellsInColAreSelected = AllCellsInColumnSelected(table, startColIndex, rowCount); |
michael@0 | 3344 | // We're done as soon as we fail for any column |
michael@0 | 3345 | if (!allCellsInRowAreSelected) break; |
michael@0 | 3346 | } |
michael@0 | 3347 | res = GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell)); |
michael@0 | 3348 | } |
michael@0 | 3349 | if (allCellsInColAreSelected) |
michael@0 | 3350 | *aSelectionType = nsISelectionPrivate::TABLESELECTION_COLUMN; |
michael@0 | 3351 | |
michael@0 | 3352 | return NS_OK; |
michael@0 | 3353 | } |
michael@0 | 3354 | |
michael@0 | 3355 | bool |
michael@0 | 3356 | nsHTMLEditor::AllCellsInRowSelected(nsIDOMElement *aTable, int32_t aRowIndex, int32_t aNumberOfColumns) |
michael@0 | 3357 | { |
michael@0 | 3358 | NS_ENSURE_TRUE(aTable, false); |
michael@0 | 3359 | |
michael@0 | 3360 | int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 3361 | bool isSelected; |
michael@0 | 3362 | |
michael@0 | 3363 | for( int32_t col = 0; col < aNumberOfColumns; col += std::max(actualColSpan, 1)) |
michael@0 | 3364 | { |
michael@0 | 3365 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 3366 | nsresult res = GetCellDataAt(aTable, aRowIndex, col, getter_AddRefs(cell), |
michael@0 | 3367 | &curStartRowIndex, &curStartColIndex, |
michael@0 | 3368 | &rowSpan, &colSpan, |
michael@0 | 3369 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 3370 | |
michael@0 | 3371 | NS_ENSURE_SUCCESS(res, false); |
michael@0 | 3372 | // If no cell, we may have a "ragged" right edge, |
michael@0 | 3373 | // so return TRUE only if we already found a cell in the row |
michael@0 | 3374 | NS_ENSURE_TRUE(cell, (col > 0) ? true : false); |
michael@0 | 3375 | |
michael@0 | 3376 | // Return as soon as a non-selected cell is found |
michael@0 | 3377 | NS_ENSURE_TRUE(isSelected, false); |
michael@0 | 3378 | |
michael@0 | 3379 | NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in AllCellsInRowSelected"); |
michael@0 | 3380 | } |
michael@0 | 3381 | return true; |
michael@0 | 3382 | } |
michael@0 | 3383 | |
michael@0 | 3384 | bool |
michael@0 | 3385 | nsHTMLEditor::AllCellsInColumnSelected(nsIDOMElement *aTable, int32_t aColIndex, int32_t aNumberOfRows) |
michael@0 | 3386 | { |
michael@0 | 3387 | NS_ENSURE_TRUE(aTable, false); |
michael@0 | 3388 | |
michael@0 | 3389 | int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; |
michael@0 | 3390 | bool isSelected; |
michael@0 | 3391 | |
michael@0 | 3392 | for( int32_t row = 0; row < aNumberOfRows; row += std::max(actualRowSpan, 1)) |
michael@0 | 3393 | { |
michael@0 | 3394 | nsCOMPtr<nsIDOMElement> cell; |
michael@0 | 3395 | nsresult res = GetCellDataAt(aTable, row, aColIndex, getter_AddRefs(cell), |
michael@0 | 3396 | &curStartRowIndex, &curStartColIndex, |
michael@0 | 3397 | &rowSpan, &colSpan, |
michael@0 | 3398 | &actualRowSpan, &actualColSpan, &isSelected); |
michael@0 | 3399 | |
michael@0 | 3400 | NS_ENSURE_SUCCESS(res, false); |
michael@0 | 3401 | // If no cell, we must have a "ragged" right edge on the last column |
michael@0 | 3402 | // so return TRUE only if we already found a cell in the row |
michael@0 | 3403 | NS_ENSURE_TRUE(cell, (row > 0) ? true : false); |
michael@0 | 3404 | |
michael@0 | 3405 | // Return as soon as a non-selected cell is found |
michael@0 | 3406 | NS_ENSURE_TRUE(isSelected, false); |
michael@0 | 3407 | } |
michael@0 | 3408 | return true; |
michael@0 | 3409 | } |
michael@0 | 3410 | |
michael@0 | 3411 | bool |
michael@0 | 3412 | nsHTMLEditor::IsEmptyCell(dom::Element* aCell) |
michael@0 | 3413 | { |
michael@0 | 3414 | MOZ_ASSERT(aCell); |
michael@0 | 3415 | |
michael@0 | 3416 | // Check if target only contains empty text node or <br> |
michael@0 | 3417 | nsCOMPtr<nsINode> cellChild = aCell->GetFirstChild(); |
michael@0 | 3418 | if (!cellChild) { |
michael@0 | 3419 | return false; |
michael@0 | 3420 | } |
michael@0 | 3421 | |
michael@0 | 3422 | nsCOMPtr<nsINode> nextChild = cellChild->GetNextSibling(); |
michael@0 | 3423 | if (nextChild) { |
michael@0 | 3424 | return false; |
michael@0 | 3425 | } |
michael@0 | 3426 | |
michael@0 | 3427 | // We insert a single break into a cell by default |
michael@0 | 3428 | // to have some place to locate a cursor -- it is dispensable |
michael@0 | 3429 | if (cellChild->IsElement() && cellChild->AsElement()->IsHTML(nsGkAtoms::br)) { |
michael@0 | 3430 | return true; |
michael@0 | 3431 | } |
michael@0 | 3432 | |
michael@0 | 3433 | bool isEmpty; |
michael@0 | 3434 | // Or check if no real content |
michael@0 | 3435 | nsresult rv = IsEmptyNode(cellChild, &isEmpty, false, false); |
michael@0 | 3436 | NS_ENSURE_SUCCESS(rv, false); |
michael@0 | 3437 | return isEmpty; |
michael@0 | 3438 | } |