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