editor/libeditor/html/nsTableEditor.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

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, &currentRowIndex, &currentColIndex);
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 &currentRowIndex, &currentColIndex,
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 &currentRowIndex, &currentColIndex,
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 &currentRowIndex, &currentColIndex, &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 &currentRowIndex, &currentColIndex, &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 }

mercurial