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
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/
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
15 * The Original Code is mozilla.org code.
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.
23 * Pierre Phaneuf <pp@ludusdesign.com>
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.
37 * ***** END LICENSE BLOCK ***** */
40 #include "nsIDOMDocument.h"
42 #include "nsIDOMText.h"
43 #include "nsIDOMElement.h"
44 #include "nsIDOMAttr.h"
45 #include "nsIDOMNode.h"
46 #include "nsIDOMNodeList.h"
47 #include "nsIDOMRange.h"
49 #include "nsIPresShell.h"
50 #include "nsISelection.h"
51 #include "nsISelectionPrivate.h"
52 #include "nsLayoutCID.h"
53 #include "nsIContent.h"
54 #include "nsIContentIterator.h"
56 #include "nsIDOMHTMLTableElement.h"
57 #include "nsIDOMHTMLTableCellElement.h"
58 #include "nsITableCellLayout.h" // For efficient access to table cell
59 #include "nsITableLayout.h" // data owned by the table and cell frames
60 #include "nsHTMLEditor.h"
61 #include "nsISelectionPrivate.h" // For nsISelectionPrivate::TABLESELECTION_ defines
64 #include "nsEditorUtils.h"
65 #include "nsTextEditUtils.h"
66 #include "nsHTMLEditUtils.h"
67 #include "nsLayoutErrors.h"
71 /***************************************************************************
72 * stack based helper class for restoring selection after table edit
74 class NS_STACK_CLASS nsSetSelectionAfterTableEdit
77 nsCOMPtr
<nsITableEditor
> mEd
;
78 nsCOMPtr
<nsIDOMElement
> mTable
;
79 PRInt32 mCol
, mRow
, mDirection
, mSelected
;
81 nsSetSelectionAfterTableEdit(nsITableEditor
*aEd
, nsIDOMElement
* aTable
,
82 PRInt32 aRow
, PRInt32 aCol
, PRInt32 aDirection
,
84 mEd(do_QueryInterface(aEd
))
89 mDirection
= aDirection
;
90 mSelected
= aSelected
;
93 ~nsSetSelectionAfterTableEdit()
96 mEd
->SetSelectionAfterTableEdit(mTable
, mRow
, mCol
, mDirection
, mSelected
);
98 // This is needed to abort the caret reset in the destructor
99 // when one method yields control to another
100 void CancelSetCaret() {mEd
= nsnull
; mTable
= nsnull
;}
103 // Stack-class to turn on/off selection batching for table selection
104 class NS_STACK_CLASS nsSelectionBatcherForTable
107 nsCOMPtr
<nsISelectionPrivate
> mSelection
;
109 nsSelectionBatcherForTable(nsISelection
*aSelection
)
111 nsCOMPtr
<nsISelection
> sel(aSelection
);
112 mSelection
= do_QueryInterface(sel
);
113 if (mSelection
) mSelection
->StartBatchChanges();
115 virtual ~nsSelectionBatcherForTable()
117 if (mSelection
) mSelection
->EndBatchChanges();
121 // Table Editing helper utilities (not exposed in IDL)
124 nsHTMLEditor::InsertCell(nsIDOMElement
*aCell
, PRInt32 aRowSpan
, PRInt32 aColSpan
,
125 PRBool aAfter
, PRBool aIsHeader
, nsIDOMElement
**aNewCell
)
127 NS_ENSURE_TRUE(aCell
, NS_ERROR_NULL_POINTER
);
128 if (aNewCell
) *aNewCell
= nsnull
;
130 // And the parent and offsets needed to do an insert
131 nsCOMPtr
<nsIDOMNode
> cellParent
;
132 nsresult res
= aCell
->GetParentNode(getter_AddRefs(cellParent
));
133 NS_ENSURE_SUCCESS(res
, res
);
134 NS_ENSURE_TRUE(cellParent
, NS_ERROR_NULL_POINTER
);
138 res
= GetChildOffset(aCell
, cellParent
, cellOffset
);
139 NS_ENSURE_SUCCESS(res
, res
);
141 nsCOMPtr
<nsIDOMElement
> newCell
;
143 res
= CreateElementWithDefaults(NS_LITERAL_STRING("th"), getter_AddRefs(newCell
));
145 res
= CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell
));
147 if(NS_FAILED(res
)) return res
;
148 if(!newCell
) return NS_ERROR_FAILURE
;
150 //Optional: return new cell created
153 *aNewCell
= newCell
.get();
154 NS_ADDREF(*aNewCell
);
159 // Note: Do NOT use editor transaction for this
160 nsAutoString newRowSpan
;
161 newRowSpan
.AppendInt(aRowSpan
, 10);
162 newCell
->SetAttribute(NS_LITERAL_STRING("rowspan"), newRowSpan
);
166 // Note: Do NOT use editor transaction for this
167 nsAutoString newColSpan
;
168 newColSpan
.AppendInt(aColSpan
, 10);
169 newCell
->SetAttribute(NS_LITERAL_STRING("colspan"), newColSpan
);
171 if(aAfter
) cellOffset
++;
173 //Don't let Rules System change the selection
174 nsAutoTxnsConserveSelection
dontChangeSelection(this);
175 return InsertNode(newCell
, cellParent
, cellOffset
);
178 NS_IMETHODIMP
nsHTMLEditor::SetColSpan(nsIDOMElement
*aCell
, PRInt32 aColSpan
)
180 NS_ENSURE_TRUE(aCell
, NS_ERROR_NULL_POINTER
);
181 nsAutoString newSpan
;
182 newSpan
.AppendInt(aColSpan
, 10);
183 return SetAttribute(aCell
, NS_LITERAL_STRING("colspan"), newSpan
);
186 NS_IMETHODIMP
nsHTMLEditor::SetRowSpan(nsIDOMElement
*aCell
, PRInt32 aRowSpan
)
188 NS_ENSURE_TRUE(aCell
, NS_ERROR_NULL_POINTER
);
189 nsAutoString newSpan
;
190 newSpan
.AppendInt(aRowSpan
, 10);
191 return SetAttribute(aCell
, NS_LITERAL_STRING("rowspan"), newSpan
);
194 /****************************************************************/
196 // Table Editing interface methods
199 nsHTMLEditor::InsertTableCell(PRInt32 aNumber
, PRBool aAfter
)
201 nsCOMPtr
<nsIDOMElement
> table
;
202 nsCOMPtr
<nsIDOMElement
> curCell
;
203 nsCOMPtr
<nsIDOMNode
> cellParent
;
204 PRInt32 cellOffset
, startRowIndex
, startColIndex
;
205 nsresult res
= GetCellContext(nsnull
,
206 getter_AddRefs(table
),
207 getter_AddRefs(curCell
),
208 getter_AddRefs(cellParent
), &cellOffset
,
209 &startRowIndex
, &startColIndex
);
210 NS_ENSURE_SUCCESS(res
, res
);
211 // Don't fail if no cell found
212 NS_ENSURE_TRUE(curCell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
214 // Get more data for current cell in row we are inserting at (we need COLSPAN)
215 PRInt32 curStartRowIndex
, curStartColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
217 res
= GetCellDataAt(table
, startRowIndex
, startColIndex
,
218 getter_AddRefs(curCell
),
219 &curStartRowIndex
, &curStartColIndex
, &rowSpan
, &colSpan
,
220 &actualRowSpan
, &actualColSpan
, &isSelected
);
221 NS_ENSURE_SUCCESS(res
, res
);
222 NS_ENSURE_TRUE(curCell
, NS_ERROR_FAILURE
);
223 PRInt32 newCellIndex
= aAfter
? (startColIndex
+colSpan
) : startColIndex
;
224 //We control selection resetting after the insert...
225 nsSetSelectionAfterTableEdit
setCaret(this, table
, startRowIndex
, newCellIndex
, ePreviousColumn
, PR_FALSE
);
226 //...so suppress Rules System selection munging
227 nsAutoTxnsConserveSelection
dontChangeSelection(this);
230 for (i
= 0; i
< aNumber
; i
++)
232 nsCOMPtr
<nsIDOMElement
> newCell
;
233 res
= CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell
));
234 if (NS_SUCCEEDED(res
) && newCell
)
236 if (aAfter
) cellOffset
++;
237 res
= InsertNode(newCell
, cellParent
, cellOffset
);
238 if(NS_FAILED(res
)) break;
246 nsHTMLEditor::GetFirstRow(nsIDOMElement
* aTableElement
, nsIDOMNode
** aRowNode
)
248 NS_ENSURE_TRUE(aRowNode
, NS_ERROR_NULL_POINTER
);
252 NS_ENSURE_TRUE(aTableElement
, NS_ERROR_NULL_POINTER
);
254 nsCOMPtr
<nsIDOMElement
> tableElement
;
255 nsresult res
= GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aTableElement
, getter_AddRefs(tableElement
));
256 NS_ENSURE_SUCCESS(res
, res
);
257 NS_ENSURE_TRUE(tableElement
, NS_ERROR_NULL_POINTER
);
259 nsCOMPtr
<nsIDOMNode
> tableChild
;
260 res
= tableElement
->GetFirstChild(getter_AddRefs(tableChild
));
261 NS_ENSURE_SUCCESS(res
, res
);
265 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(tableChild
);
268 nsIAtom
*atom
= content
->Tag();
270 if (atom
== nsEditProperty::tr
)
272 // Found a row directly under <table>
273 *aRowNode
= tableChild
;
274 NS_ADDREF(*aRowNode
);
277 // Look for row in one of the row container elements
278 if (atom
== nsEditProperty::tbody
||
279 atom
== nsEditProperty::thead
||
280 atom
== nsEditProperty::tfoot
)
282 nsCOMPtr
<nsIDOMNode
> rowNode
;
283 res
= tableChild
->GetFirstChild(getter_AddRefs(rowNode
));
284 NS_ENSURE_SUCCESS(res
, res
);
286 // We can encounter textnodes here -- must find a row
287 while (rowNode
&& !nsHTMLEditUtils::IsTableRow(rowNode
))
289 nsCOMPtr
<nsIDOMNode
> nextNode
;
290 res
= rowNode
->GetNextSibling(getter_AddRefs(nextNode
));
291 NS_ENSURE_SUCCESS(res
, res
);
297 *aRowNode
= rowNode
.get();
298 NS_ADDREF(*aRowNode
);
303 // Here if table child was a CAPTION or COLGROUP
304 // or child of a row parent wasn't a row (bad HTML?),
305 // or first child was a textnode
306 // Look in next table child
307 nsCOMPtr
<nsIDOMNode
> nextChild
;
308 res
= tableChild
->GetNextSibling(getter_AddRefs(nextChild
));
309 NS_ENSURE_SUCCESS(res
, res
);
311 tableChild
= nextChild
;
313 // If here, row was not found
314 return NS_EDITOR_ELEMENT_NOT_FOUND
;
318 nsHTMLEditor::GetNextRow(nsIDOMNode
* aCurrentRowNode
, nsIDOMNode
**aRowNode
)
320 NS_ENSURE_TRUE(aRowNode
, NS_ERROR_NULL_POINTER
);
324 NS_ENSURE_TRUE(aCurrentRowNode
, NS_ERROR_NULL_POINTER
);
326 if (!nsHTMLEditUtils::IsTableRow(aCurrentRowNode
))
327 return NS_ERROR_FAILURE
;
329 nsCOMPtr
<nsIDOMNode
> nextRow
;
330 nsresult res
= aCurrentRowNode
->GetNextSibling(getter_AddRefs(nextRow
));
331 NS_ENSURE_SUCCESS(res
, res
);
333 nsCOMPtr
<nsIDOMNode
> nextNode
;
335 // Skip over any textnodes here
336 while (nextRow
&& !nsHTMLEditUtils::IsTableRow(nextRow
))
338 res
= nextRow
->GetNextSibling(getter_AddRefs(nextNode
));
339 NS_ENSURE_SUCCESS(res
, res
);
345 *aRowNode
= nextRow
.get();
346 NS_ADDREF(*aRowNode
);
350 // No row found, search for rows in other table sections
351 nsCOMPtr
<nsIDOMNode
> rowParent
;
352 res
= aCurrentRowNode
->GetParentNode(getter_AddRefs(rowParent
));
353 NS_ENSURE_SUCCESS(res
, res
);
354 NS_ENSURE_TRUE(rowParent
, NS_ERROR_NULL_POINTER
);
356 nsCOMPtr
<nsIDOMNode
> parentSibling
;
357 res
= rowParent
->GetNextSibling(getter_AddRefs(parentSibling
));
358 NS_ENSURE_SUCCESS(res
, res
);
360 while (parentSibling
)
362 res
= parentSibling
->GetFirstChild(getter_AddRefs(nextRow
));
363 NS_ENSURE_SUCCESS(res
, res
);
365 // We can encounter textnodes here -- must find a row
366 while (nextRow
&& !nsHTMLEditUtils::IsTableRow(nextRow
))
368 res
= nextRow
->GetNextSibling(getter_AddRefs(nextNode
));
369 NS_ENSURE_SUCCESS(res
, res
);
375 *aRowNode
= nextRow
.get();
376 NS_ADDREF(*aRowNode
);
380 printf("GetNextRow: firstChild of row's parent's sibling is not a TR!\n");
382 // We arrive here only if a table section has no children
383 // or first child of section is not a row (bad HTML or more "_moz_text" nodes!)
384 // So look for another section sibling
385 res
= parentSibling
->GetNextSibling(getter_AddRefs(nextNode
));
386 NS_ENSURE_SUCCESS(res
, res
);
388 parentSibling
= nextNode
;
390 // If here, row was not found
391 return NS_EDITOR_ELEMENT_NOT_FOUND
;
395 nsHTMLEditor::GetLastCellInRow(nsIDOMNode
* aRowNode
, nsIDOMNode
** aCellNode
)
397 NS_ENSURE_TRUE(aCellNode
, NS_ERROR_NULL_POINTER
);
401 NS_ENSURE_TRUE(aRowNode
, NS_ERROR_NULL_POINTER
);
403 nsCOMPtr
<nsIDOMNode
> rowChild
;
404 nsresult res
= aRowNode
->GetLastChild(getter_AddRefs(rowChild
));
405 NS_ENSURE_SUCCESS(res
, res
);
407 while (rowChild
&& !nsHTMLEditUtils::IsTableCell(rowChild
))
409 // Skip over textnodes
410 nsCOMPtr
<nsIDOMNode
> previousChild
;
411 res
= rowChild
->GetPreviousSibling(getter_AddRefs(previousChild
));
412 NS_ENSURE_SUCCESS(res
, res
);
414 rowChild
= previousChild
;
418 *aCellNode
= rowChild
.get();
419 NS_ADDREF(*aCellNode
);
422 // If here, cell was not found
423 return NS_EDITOR_ELEMENT_NOT_FOUND
;
427 nsHTMLEditor::InsertTableColumn(PRInt32 aNumber
, PRBool aAfter
)
429 nsCOMPtr
<nsISelection
> selection
;
430 nsCOMPtr
<nsIDOMElement
> table
;
431 nsCOMPtr
<nsIDOMElement
> curCell
;
432 PRInt32 startRowIndex
, startColIndex
;
433 nsresult res
= GetCellContext(getter_AddRefs(selection
),
434 getter_AddRefs(table
),
435 getter_AddRefs(curCell
),
437 &startRowIndex
, &startColIndex
);
438 NS_ENSURE_SUCCESS(res
, res
);
439 // Don't fail if no cell found
440 NS_ENSURE_TRUE(curCell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
442 // Get more data for current cell (we need ROWSPAN)
443 PRInt32 curStartRowIndex
, curStartColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
445 res
= GetCellDataAt(table
, startRowIndex
, startColIndex
,
446 getter_AddRefs(curCell
),
447 &curStartRowIndex
, &curStartColIndex
,
449 &actualRowSpan
, &actualColSpan
, &isSelected
);
450 NS_ENSURE_SUCCESS(res
, res
);
451 NS_ENSURE_TRUE(curCell
, NS_ERROR_FAILURE
);
453 nsAutoEditBatch
beginBatching(this);
454 // Prevent auto insertion of BR in new cell until we're done
455 nsAutoRules
beginRulesSniffing(this, kOpInsertNode
, nsIEditor::eNext
);
457 // Use column after current cell if requested
460 startColIndex
+= actualColSpan
;
461 //Detect when user is adding after a COLSPAN=0 case
462 // Assume they want to stop the "0" behavior and
463 // really add a new column. Thus we set the
464 // colspan to its true value
466 SetColSpan(curCell
, actualColSpan
);
469 PRInt32 rowCount
, colCount
, rowIndex
;
470 res
= GetTableSize(table
, &rowCount
, &colCount
);
471 NS_ENSURE_SUCCESS(res
, res
);
473 //We reset caret in destructor...
474 nsSetSelectionAfterTableEdit
setCaret(this, table
, startRowIndex
, startColIndex
, ePreviousRow
, PR_FALSE
);
475 //.. so suppress Rules System selection munging
476 nsAutoTxnsConserveSelection
dontChangeSelection(this);
478 // If we are inserting after all existing columns
479 // Make sure table is "well formed"
480 // before appending new column
481 if (startColIndex
>= colCount
)
482 NormalizeTable(table
);
484 nsCOMPtr
<nsIDOMNode
> rowNode
;
485 for ( rowIndex
= 0; rowIndex
< rowCount
; rowIndex
++)
488 if (rowIndex
== rowCount
-1)
489 printf(" ***InsertTableColumn: Inserting cell at last row: %d\n", rowIndex
);
492 if (startColIndex
< colCount
)
494 // We are inserting before an existing column
495 res
= GetCellDataAt(table
, rowIndex
, startColIndex
,
496 getter_AddRefs(curCell
),
497 &curStartRowIndex
, &curStartColIndex
,
499 &actualRowSpan
, &actualColSpan
, &isSelected
);
500 NS_ENSURE_SUCCESS(res
, res
);
502 // Don't fail entire process if we fail to find a cell
503 // (may fail just in particular rows with < adequate cells per row)
506 if (curStartColIndex
< startColIndex
)
508 // We have a cell spanning this location
509 // Simply increase its colspan to keep table rectangular
510 // Note: we do nothing if colsSpan=0,
511 // since it should automatically span the new column
513 SetColSpan(curCell
, colSpan
+aNumber
);
515 // Simply set selection to the current cell
516 // so we can let InsertTableCell() do the work
517 // Insert a new cell before current one
518 selection
->Collapse(curCell
, 0);
519 res
= InsertTableCell(aNumber
, PR_FALSE
);
523 // Get current row and append new cells after last cell in row
525 res
= GetFirstRow(table
.get(), getter_AddRefs(rowNode
));
528 nsCOMPtr
<nsIDOMNode
> nextRow
;
529 res
= GetNextRow(rowNode
.get(), getter_AddRefs(nextRow
));
532 NS_ENSURE_SUCCESS(res
, res
);
536 nsCOMPtr
<nsIDOMNode
> lastCell
;
537 res
= GetLastCellInRow(rowNode
, getter_AddRefs(lastCell
));
538 NS_ENSURE_SUCCESS(res
, res
);
539 NS_ENSURE_TRUE(lastCell
, NS_ERROR_FAILURE
);
541 curCell
= do_QueryInterface(lastCell
);
544 // Simply add same number of cells to each row
545 // Although tempted to check cell indexes for curCell,
546 // the effects of COLSPAN>1 in some cells makes this futile!
547 // We must use NormalizeTable first to assure
548 // that there are cells in each cellmap location
549 selection
->Collapse(curCell
, 0);
550 res
= InsertTableCell(aNumber
, PR_TRUE
);
559 nsHTMLEditor::InsertTableRow(PRInt32 aNumber
, PRBool aAfter
)
561 nsCOMPtr
<nsISelection
> selection
;
562 nsCOMPtr
<nsIDOMElement
> table
;
563 nsCOMPtr
<nsIDOMElement
> curCell
;
565 PRInt32 startRowIndex
, startColIndex
;
566 nsresult res
= GetCellContext(nsnull
,
567 getter_AddRefs(table
),
568 getter_AddRefs(curCell
),
570 &startRowIndex
, &startColIndex
);
571 NS_ENSURE_SUCCESS(res
, res
);
572 // Don't fail if no cell found
573 NS_ENSURE_TRUE(curCell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
575 // Get more data for current cell in row we are inserting at (we need COLSPAN)
576 PRInt32 curStartRowIndex
, curStartColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
578 res
= GetCellDataAt(table
, startRowIndex
, startColIndex
,
579 getter_AddRefs(curCell
),
580 &curStartRowIndex
, &curStartColIndex
,
582 &actualRowSpan
, &actualColSpan
, &isSelected
);
583 NS_ENSURE_SUCCESS(res
, res
);
584 NS_ENSURE_TRUE(curCell
, NS_ERROR_FAILURE
);
586 PRInt32 rowCount
, colCount
;
587 res
= GetTableSize(table
, &rowCount
, &colCount
);
588 NS_ENSURE_SUCCESS(res
, res
);
590 nsAutoEditBatch
beginBatching(this);
591 // Prevent auto insertion of BR in new cell until we're done
592 nsAutoRules
beginRulesSniffing(this, kOpInsertNode
, nsIEditor::eNext
);
596 // Use row after current cell
597 startRowIndex
+= actualRowSpan
;
599 //Detect when user is adding after a ROWSPAN=0 case
600 // Assume they want to stop the "0" behavior and
601 // really add a new row. Thus we set the
602 // rowspan to its true value
604 SetRowSpan(curCell
, actualRowSpan
);
607 //We control selection resetting after the insert...
608 nsSetSelectionAfterTableEdit
setCaret(this, table
, startRowIndex
, startColIndex
, ePreviousColumn
, PR_FALSE
);
609 //...so suppress Rules System selection munging
610 nsAutoTxnsConserveSelection
dontChangeSelection(this);
612 nsCOMPtr
<nsIDOMElement
> cellForRowParent
;
613 PRInt32 cellsInRow
= 0;
614 if (startRowIndex
< rowCount
)
616 // We are inserting above an existing row
617 // Get each cell in the insert row to adjust for COLSPAN effects while we
618 // count how many cells are needed
619 PRInt32 colIndex
= 0;
620 // This returns NS_TABLELAYOUT_CELL_NOT_FOUND when we run past end of row,
621 // which passes the NS_SUCCEEDED macro
622 while ( NS_OK
== GetCellDataAt(table
, startRowIndex
, colIndex
,
623 getter_AddRefs(curCell
),
624 &curStartRowIndex
, &curStartColIndex
,
626 &actualRowSpan
, &actualColSpan
,
631 if (curStartRowIndex
< startRowIndex
)
633 // We have a cell spanning this location
634 // Simply increase its rowspan
635 //Note that if rowSpan == 0, we do nothing,
636 // since that cell should automatically extend into the new row
638 SetRowSpan(curCell
, rowSpan
+aNumber
);
640 // We have a cell in the insert row
642 // Count the number of cells we need to add to the new row
643 cellsInRow
+= actualColSpan
;
645 // Save cell we will use below
646 if (!cellForRowParent
)
647 cellForRowParent
= curCell
;
650 colIndex
+= actualColSpan
;
656 // We are adding a new row after all others
657 // If it weren't for colspan=0 effect,
658 // we could simply use colCount for number of new cells...
659 cellsInRow
= colCount
;
661 // ...but we must compensate for all cells with rowSpan = 0 in the last row
662 PRInt32 lastRow
= rowCount
-1;
663 PRInt32 tempColIndex
= 0;
664 while ( NS_OK
== GetCellDataAt(table
, lastRow
, tempColIndex
,
665 getter_AddRefs(curCell
),
666 &curStartRowIndex
, &curStartColIndex
,
668 &actualRowSpan
, &actualColSpan
,
672 cellsInRow
-= actualColSpan
;
674 tempColIndex
+= actualColSpan
;
676 // Save cell from the last row that we will use below
677 if (!cellForRowParent
&& curStartRowIndex
== lastRow
)
678 cellForRowParent
= curCell
;
684 // The row parent and offset where we will insert new row
685 nsCOMPtr
<nsIDOMNode
> parentOfRow
;
686 PRInt32 newRowOffset
;
688 NS_NAMED_LITERAL_STRING(trStr
, "tr");
689 if (cellForRowParent
)
691 nsCOMPtr
<nsIDOMElement
> parentRow
;
692 res
= GetElementOrParentByTagName(trStr
, cellForRowParent
, getter_AddRefs(parentRow
));
693 NS_ENSURE_SUCCESS(res
, res
);
694 NS_ENSURE_TRUE(parentRow
, NS_ERROR_NULL_POINTER
);
696 parentRow
->GetParentNode(getter_AddRefs(parentOfRow
));
697 NS_ENSURE_TRUE(parentOfRow
, NS_ERROR_NULL_POINTER
);
699 res
= GetChildOffset(parentRow
, parentOfRow
, newRowOffset
);
700 NS_ENSURE_SUCCESS(res
, res
);
702 // Adjust for when adding past the end
703 if (aAfter
&& startRowIndex
>= rowCount
)
707 return NS_ERROR_FAILURE
;
709 for (PRInt32 row
= 0; row
< aNumber
; row
++)
712 nsCOMPtr
<nsIDOMElement
> newRow
;
713 res
= CreateElementWithDefaults(trStr
, getter_AddRefs(newRow
));
714 if (NS_SUCCEEDED(res
))
716 NS_ENSURE_TRUE(newRow
, NS_ERROR_FAILURE
);
718 for (PRInt32 i
= 0; i
< cellsInRow
; i
++)
720 nsCOMPtr
<nsIDOMElement
> newCell
;
721 res
= CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell
));
722 NS_ENSURE_SUCCESS(res
, res
);
723 NS_ENSURE_TRUE(newCell
, NS_ERROR_FAILURE
);
725 // Don't use transaction system yet! (not until entire row is inserted)
726 nsCOMPtr
<nsIDOMNode
>resultNode
;
727 res
= newRow
->AppendChild(newCell
, getter_AddRefs(resultNode
));
728 NS_ENSURE_SUCCESS(res
, res
);
730 // Use transaction system to insert the entire row+cells
731 // (Note that rows are inserted at same childoffset each time)
732 res
= InsertNode(newRow
, parentOfRow
, newRowOffset
);
733 NS_ENSURE_SUCCESS(res
, res
);
740 // Editor helper only
741 // XXX Code changed for bug 217717 and now we don't need aSelection param
742 // TODO: Remove aSelection param
744 nsHTMLEditor::DeleteTable2(nsIDOMElement
*aTable
, nsISelection
*aSelection
)
746 NS_ENSURE_TRUE(aTable
, NS_ERROR_NULL_POINTER
);
749 nsresult res
= ClearSelection();
750 if (NS_SUCCEEDED(res
))
751 res
= AppendNodeToSelectionAsRange(aTable
);
752 NS_ENSURE_SUCCESS(res
, res
);
754 return DeleteSelection(nsIEditor::eNext
);
758 nsHTMLEditor::DeleteTable()
760 nsCOMPtr
<nsISelection
> selection
;
761 nsCOMPtr
<nsIDOMElement
> table
;
762 nsresult res
= GetCellContext(getter_AddRefs(selection
),
763 getter_AddRefs(table
),
764 nsnull
, nsnull
, nsnull
, nsnull
, nsnull
);
766 NS_ENSURE_SUCCESS(res
, res
);
768 nsAutoEditBatch
beginBatching(this);
769 return DeleteTable2(table
, selection
);
773 nsHTMLEditor::DeleteTableCell(PRInt32 aNumber
)
775 nsCOMPtr
<nsISelection
> selection
;
776 nsCOMPtr
<nsIDOMElement
> table
;
777 nsCOMPtr
<nsIDOMElement
> cell
;
778 PRInt32 startRowIndex
, startColIndex
;
781 nsresult res
= GetCellContext(getter_AddRefs(selection
),
782 getter_AddRefs(table
),
783 getter_AddRefs(cell
),
785 &startRowIndex
, &startColIndex
);
787 NS_ENSURE_SUCCESS(res
, res
);
788 // Don't fail if we didn't find a table or cell
789 NS_ENSURE_TRUE(table
&& cell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
791 nsAutoEditBatch
beginBatching(this);
792 // Prevent rules testing until we're done
793 nsAutoRules
beginRulesSniffing(this, kOpDeleteNode
, nsIEditor::eNext
);
795 nsCOMPtr
<nsIDOMElement
> firstCell
;
796 nsCOMPtr
<nsIDOMRange
> range
;
797 res
= GetFirstSelectedCell(getter_AddRefs(range
), getter_AddRefs(firstCell
));
798 NS_ENSURE_SUCCESS(res
, res
);
801 res
= selection
->GetRangeCount(&rangeCount
);
802 NS_ENSURE_SUCCESS(res
, res
);
804 if (firstCell
&& rangeCount
> 1)
806 // When > 1 selected cell,
807 // ignore aNumber and use selected cells
810 PRInt32 rowCount
, colCount
;
811 res
= GetTableSize(table
, &rowCount
, &colCount
);
812 NS_ENSURE_SUCCESS(res
, res
);
814 // Get indexes -- may be different than original cell
815 res
= GetCellIndexes(cell
, &startRowIndex
, &startColIndex
);
816 NS_ENSURE_SUCCESS(res
, res
);
818 // The setCaret object will call SetSelectionAfterTableEdit in it's destructor
819 nsSetSelectionAfterTableEdit
setCaret(this, table
, startRowIndex
, startColIndex
, ePreviousColumn
, PR_FALSE
);
820 nsAutoTxnsConserveSelection
dontChangeSelection(this);
822 PRBool checkToDeleteRow
= PR_TRUE
;
823 PRBool checkToDeleteColumn
= PR_TRUE
;
826 PRBool deleteRow
= PR_FALSE
;
827 PRBool deleteCol
= PR_FALSE
;
829 if (checkToDeleteRow
)
831 // Optimize to delete an entire row
832 // Clear so we don't repeat AllCellsInRowSelected within the same row
833 checkToDeleteRow
= PR_FALSE
;
835 deleteRow
= AllCellsInRowSelected(table
, startRowIndex
, colCount
);
838 // First, find the next cell in a different row
839 // to continue after we delete this row
840 PRInt32 nextRow
= startRowIndex
;
841 while (nextRow
== startRowIndex
)
843 res
= GetNextSelectedCell(nsnull
, getter_AddRefs(cell
));
844 NS_ENSURE_SUCCESS(res
, res
);
846 res
= GetCellIndexes(cell
, &nextRow
, &startColIndex
);
847 NS_ENSURE_SUCCESS(res
, res
);
850 res
= DeleteRow(table
, startRowIndex
);
851 NS_ENSURE_SUCCESS(res
, res
);
855 // For the next cell: Subtract 1 for row we deleted
856 startRowIndex
= nextRow
- 1;
857 // Set true since we know we will look at a new row next
858 checkToDeleteRow
= PR_TRUE
;
864 if (checkToDeleteColumn
)
866 // Optimize to delete an entire column
867 // Clear this so we don't repeat AllCellsInColSelected within the same Col
868 checkToDeleteColumn
= PR_FALSE
;
870 deleteCol
= AllCellsInColumnSelected(table
, startColIndex
, colCount
);
873 // First, find the next cell in a different column
874 // to continue after we delete this column
875 PRInt32 nextCol
= startColIndex
;
876 while (nextCol
== startColIndex
)
878 res
= GetNextSelectedCell(nsnull
, getter_AddRefs(cell
));
879 NS_ENSURE_SUCCESS(res
, res
);
881 res
= GetCellIndexes(cell
, &startRowIndex
, &nextCol
);
882 NS_ENSURE_SUCCESS(res
, res
);
885 res
= DeleteColumn(table
, startColIndex
);
886 NS_ENSURE_SUCCESS(res
, res
);
889 // For the next cell, subtract 1 for col. deleted
890 startColIndex
= nextCol
- 1;
891 // Set true since we know we will look at a new column next
892 checkToDeleteColumn
= PR_TRUE
;
898 // First get the next cell to delete
899 nsCOMPtr
<nsIDOMElement
> nextCell
;
900 res
= GetNextSelectedCell(getter_AddRefs(range
), getter_AddRefs(nextCell
));
901 NS_ENSURE_SUCCESS(res
, res
);
903 // Then delete the cell
904 res
= DeleteNode(cell
);
905 NS_ENSURE_SUCCESS(res
, res
);
907 // The next cell to delete
911 res
= GetCellIndexes(cell
, &startRowIndex
, &startColIndex
);
912 NS_ENSURE_SUCCESS(res
, res
);
918 else for (PRInt32 i
= 0; i
< aNumber
; i
++)
920 res
= GetCellContext(getter_AddRefs(selection
),
921 getter_AddRefs(table
),
922 getter_AddRefs(cell
),
924 &startRowIndex
, &startColIndex
);
925 NS_ENSURE_SUCCESS(res
, res
);
926 // Don't fail if no cell found
927 NS_ENSURE_TRUE(cell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
929 if (1 == GetNumberOfCellsInRow(table
, startRowIndex
))
931 nsCOMPtr
<nsIDOMElement
> parentRow
;
932 res
= GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cell
, getter_AddRefs(parentRow
));
933 NS_ENSURE_SUCCESS(res
, res
);
934 NS_ENSURE_TRUE(parentRow
, NS_ERROR_NULL_POINTER
);
936 // We should delete the row instead,
937 // but first check if its the only row left
938 // so we can delete the entire table
939 PRInt32 rowCount
, colCount
;
940 res
= GetTableSize(table
, &rowCount
, &colCount
);
941 NS_ENSURE_SUCCESS(res
, res
);
944 return DeleteTable2(table
, selection
);
946 // We need to call DeleteTableRow to handle cells with rowspan
947 res
= DeleteTableRow(1);
948 NS_ENSURE_SUCCESS(res
, res
);
952 // More than 1 cell in the row
954 // The setCaret object will call SetSelectionAfterTableEdit in it's destructor
955 nsSetSelectionAfterTableEdit
setCaret(this, table
, startRowIndex
, startColIndex
, ePreviousColumn
, PR_FALSE
);
956 nsAutoTxnsConserveSelection
dontChangeSelection(this);
958 res
= DeleteNode(cell
);
959 // If we fail, don't try to delete any more cells???
960 NS_ENSURE_SUCCESS(res
, res
);
967 nsHTMLEditor::DeleteTableCellContents()
969 nsCOMPtr
<nsISelection
> selection
;
970 nsCOMPtr
<nsIDOMElement
> table
;
971 nsCOMPtr
<nsIDOMElement
> cell
;
972 PRInt32 startRowIndex
, startColIndex
;
974 res
= GetCellContext(getter_AddRefs(selection
),
975 getter_AddRefs(table
),
976 getter_AddRefs(cell
),
978 &startRowIndex
, &startColIndex
);
979 NS_ENSURE_SUCCESS(res
, res
);
980 // Don't fail if no cell found
981 NS_ENSURE_TRUE(cell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
984 nsAutoEditBatch
beginBatching(this);
985 // Prevent rules testing until we're done
986 nsAutoRules
beginRulesSniffing(this, kOpDeleteNode
, nsIEditor::eNext
);
987 //Don't let Rules System change the selection
988 nsAutoTxnsConserveSelection
dontChangeSelection(this);
991 nsCOMPtr
<nsIDOMElement
> firstCell
;
992 nsCOMPtr
<nsIDOMRange
> range
;
993 res
= GetFirstSelectedCell(getter_AddRefs(range
), getter_AddRefs(firstCell
));
994 NS_ENSURE_SUCCESS(res
, res
);
1000 res
= GetCellIndexes(cell
, &startRowIndex
, &startColIndex
);
1001 NS_ENSURE_SUCCESS(res
, res
);
1004 nsSetSelectionAfterTableEdit
setCaret(this, table
, startRowIndex
, startColIndex
, ePreviousColumn
, PR_FALSE
);
1008 DeleteCellContents(cell
);
1011 // We doing a selected cells, so do all of them
1012 res
= GetNextSelectedCell(nsnull
, getter_AddRefs(cell
));
1013 NS_ENSURE_SUCCESS(res
, res
);
1022 nsHTMLEditor::DeleteCellContents(nsIDOMElement
*aCell
)
1024 NS_ENSURE_TRUE(aCell
, NS_ERROR_NULL_POINTER
);
1026 // Prevent rules testing until we're done
1027 nsAutoRules
beginRulesSniffing(this, kOpDeleteNode
, nsIEditor::eNext
);
1029 nsCOMPtr
<nsIDOMNode
> child
;
1031 aCell
->HasChildNodes(&hasChild
);
1035 aCell
->GetLastChild(getter_AddRefs(child
));
1036 nsresult res
= DeleteNode(child
);
1037 NS_ENSURE_SUCCESS(res
, res
);
1038 aCell
->HasChildNodes(&hasChild
);
1044 nsHTMLEditor::DeleteTableColumn(PRInt32 aNumber
)
1046 nsCOMPtr
<nsISelection
> selection
;
1047 nsCOMPtr
<nsIDOMElement
> table
;
1048 nsCOMPtr
<nsIDOMElement
> cell
;
1049 PRInt32 startRowIndex
, startColIndex
, rowCount
, colCount
;
1050 nsresult res
= GetCellContext(getter_AddRefs(selection
),
1051 getter_AddRefs(table
),
1052 getter_AddRefs(cell
),
1054 &startRowIndex
, &startColIndex
);
1055 NS_ENSURE_SUCCESS(res
, res
);
1056 // Don't fail if no cell found
1057 NS_ENSURE_TRUE(table
&& cell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
1059 res
= GetTableSize(table
, &rowCount
, &colCount
);
1060 NS_ENSURE_SUCCESS(res
, res
);
1062 // Shortcut the case of deleting all columns in table
1063 if(startColIndex
== 0 && aNumber
>= colCount
)
1064 return DeleteTable2(table
, selection
);
1066 // Check for counts too high
1067 aNumber
= NS_MIN(aNumber
,(colCount
-startColIndex
));
1069 nsAutoEditBatch
beginBatching(this);
1070 // Prevent rules testing until we're done
1071 nsAutoRules
beginRulesSniffing(this, kOpDeleteNode
, nsIEditor::eNext
);
1073 // Test if deletion is controlled by selected cells
1074 nsCOMPtr
<nsIDOMElement
> firstCell
;
1075 nsCOMPtr
<nsIDOMRange
> range
;
1076 res
= GetFirstSelectedCell(getter_AddRefs(range
), getter_AddRefs(firstCell
));
1077 NS_ENSURE_SUCCESS(res
, res
);
1080 res
= selection
->GetRangeCount(&rangeCount
);
1081 NS_ENSURE_SUCCESS(res
, res
);
1083 if (firstCell
&& rangeCount
> 1)
1085 // Fetch indexes again - may be different for selected cells
1086 res
= GetCellIndexes(firstCell
, &startRowIndex
, &startColIndex
);
1087 NS_ENSURE_SUCCESS(res
, res
);
1089 //We control selection resetting after the insert...
1090 nsSetSelectionAfterTableEdit
setCaret(this, table
, startRowIndex
, startColIndex
, ePreviousRow
, PR_FALSE
);
1092 if (firstCell
&& rangeCount
> 1)
1094 // Use selected cells to determine what rows to delete
1099 if (cell
!= firstCell
)
1101 res
= GetCellIndexes(cell
, &startRowIndex
, &startColIndex
);
1102 NS_ENSURE_SUCCESS(res
, res
);
1104 // Find the next cell in a different column
1105 // to continue after we delete this column
1106 PRInt32 nextCol
= startColIndex
;
1107 while (nextCol
== startColIndex
)
1109 res
= GetNextSelectedCell(getter_AddRefs(range
), getter_AddRefs(cell
));
1110 NS_ENSURE_SUCCESS(res
, res
);
1112 res
= GetCellIndexes(cell
, &startRowIndex
, &nextCol
);
1113 NS_ENSURE_SUCCESS(res
, res
);
1115 res
= DeleteColumn(table
, startColIndex
);
1116 NS_ENSURE_SUCCESS(res
, res
);
1119 else for (PRInt32 i
= 0; i
< aNumber
; i
++)
1121 res
= DeleteColumn(table
, startColIndex
);
1122 NS_ENSURE_SUCCESS(res
, res
);
1128 nsHTMLEditor::DeleteColumn(nsIDOMElement
*aTable
, PRInt32 aColIndex
)
1130 NS_ENSURE_TRUE(aTable
, NS_ERROR_NULL_POINTER
);
1132 nsCOMPtr
<nsIDOMElement
> cell
;
1133 nsCOMPtr
<nsIDOMElement
> cellInDeleteCol
;
1134 PRInt32 startRowIndex
, startColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
1136 PRInt32 rowIndex
= 0;
1137 nsresult res
= NS_OK
;
1140 res
= GetCellDataAt(aTable
, rowIndex
, aColIndex
, getter_AddRefs(cell
),
1141 &startRowIndex
, &startColIndex
, &rowSpan
, &colSpan
,
1142 &actualRowSpan
, &actualColSpan
, &isSelected
);
1144 NS_ENSURE_SUCCESS(res
, res
);
1148 // Find cells that don't start in column we are deleting
1149 if (startColIndex
< aColIndex
|| colSpan
> 1 || colSpan
== 0)
1151 // We have a cell spanning this location
1152 // Decrease its colspan to keep table rectangular,
1153 // but if colSpan=0, it will adjust automatically
1156 NS_ASSERTION((colSpan
> 1),"Bad COLSPAN in DeleteTableColumn");
1157 SetColSpan(cell
, colSpan
-1);
1159 if (startColIndex
== aColIndex
)
1161 // Cell is in column to be deleted, but must have colspan > 1,
1162 // so delete contents of cell instead of cell itself
1163 // (We must have reset colspan above)
1164 DeleteCellContents(cell
);
1166 // To next cell in column
1167 rowIndex
+= actualRowSpan
;
1172 if (1 == GetNumberOfCellsInRow(aTable
, rowIndex
))
1174 // Only 1 cell in row - delete the row
1175 nsCOMPtr
<nsIDOMElement
> parentRow
;
1176 res
= GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cell
, getter_AddRefs(parentRow
));
1177 NS_ENSURE_SUCCESS(res
, res
);
1178 if(!parentRow
) return NS_ERROR_NULL_POINTER
;
1180 // But first check if its the only row left
1181 // so we can delete the entire table
1182 // (This should never happen but it's the safe thing to do)
1183 PRInt32 rowCount
, colCount
;
1184 res
= GetTableSize(aTable
, &rowCount
, &colCount
);
1185 NS_ENSURE_SUCCESS(res
, res
);
1189 nsCOMPtr
<nsISelection
> selection
;
1190 res
= GetSelection(getter_AddRefs(selection
));
1191 NS_ENSURE_SUCCESS(res
, res
);
1192 NS_ENSURE_TRUE(selection
, NS_ERROR_FAILURE
);
1193 return DeleteTable2(aTable
, selection
);
1196 // Delete the row by placing caret in cell we were to delete
1197 // We need to call DeleteTableRow to handle cells with rowspan
1198 res
= DeleteRow(aTable
, startRowIndex
);
1199 NS_ENSURE_SUCCESS(res
, res
);
1201 // Note that we don't incremenet rowIndex
1202 // since a row was deleted and "next"
1203 // row now has current rowIndex
1207 // A more "normal" deletion
1208 res
= DeleteNode(cell
);
1209 NS_ENSURE_SUCCESS(res
, res
);
1211 //Skip over any rows spanned by this cell
1212 rowIndex
+= actualRowSpan
;
1222 nsHTMLEditor::DeleteTableRow(PRInt32 aNumber
)
1224 nsCOMPtr
<nsISelection
> selection
;
1225 nsCOMPtr
<nsIDOMElement
> table
;
1226 nsCOMPtr
<nsIDOMElement
> cell
;
1227 PRInt32 startRowIndex
, startColIndex
;
1228 PRInt32 rowCount
, colCount
;
1229 nsresult res
= GetCellContext(getter_AddRefs(selection
),
1230 getter_AddRefs(table
),
1231 getter_AddRefs(cell
),
1233 &startRowIndex
, &startColIndex
);
1234 NS_ENSURE_SUCCESS(res
, res
);
1235 // Don't fail if no cell found
1236 NS_ENSURE_TRUE(cell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
1238 res
= GetTableSize(table
, &rowCount
, &colCount
);
1239 NS_ENSURE_SUCCESS(res
, res
);
1241 // Shortcut the case of deleting all rows in table
1242 if(startRowIndex
== 0 && aNumber
>= rowCount
)
1243 return DeleteTable2(table
, selection
);
1245 nsAutoEditBatch
beginBatching(this);
1246 // Prevent rules testing until we're done
1247 nsAutoRules
beginRulesSniffing(this, kOpDeleteNode
, nsIEditor::eNext
);
1249 nsCOMPtr
<nsIDOMElement
> firstCell
;
1250 nsCOMPtr
<nsIDOMRange
> range
;
1251 res
= GetFirstSelectedCell(getter_AddRefs(range
), getter_AddRefs(firstCell
));
1252 NS_ENSURE_SUCCESS(res
, res
);
1255 res
= selection
->GetRangeCount(&rangeCount
);
1256 NS_ENSURE_SUCCESS(res
, res
);
1258 if (firstCell
&& rangeCount
> 1)
1260 // Fetch indexes again - may be different for selected cells
1261 res
= GetCellIndexes(firstCell
, &startRowIndex
, &startColIndex
);
1262 NS_ENSURE_SUCCESS(res
, res
);
1265 //We control selection resetting after the insert...
1266 nsSetSelectionAfterTableEdit
setCaret(this, table
, startRowIndex
, startColIndex
, ePreviousRow
, PR_FALSE
);
1267 // Don't change selection during deletions
1268 nsAutoTxnsConserveSelection
dontChangeSelection(this);
1270 if (firstCell
&& rangeCount
> 1)
1272 // Use selected cells to determine what rows to delete
1277 if (cell
!= firstCell
)
1279 res
= GetCellIndexes(cell
, &startRowIndex
, &startColIndex
);
1280 NS_ENSURE_SUCCESS(res
, res
);
1282 // Find the next cell in a different row
1283 // to continue after we delete this row
1284 PRInt32 nextRow
= startRowIndex
;
1285 while (nextRow
== startRowIndex
)
1287 res
= GetNextSelectedCell(getter_AddRefs(range
), getter_AddRefs(cell
));
1288 NS_ENSURE_SUCCESS(res
, res
);
1290 res
= GetCellIndexes(cell
, &nextRow
, &startColIndex
);
1291 NS_ENSURE_SUCCESS(res
, res
);
1293 // Delete entire row
1294 res
= DeleteRow(table
, startRowIndex
);
1295 NS_ENSURE_SUCCESS(res
, res
);
1300 // Check for counts too high
1301 aNumber
= NS_MIN(aNumber
,(rowCount
-startRowIndex
));
1303 for (PRInt32 i
= 0; i
< aNumber
; i
++)
1305 res
= DeleteRow(table
, startRowIndex
);
1306 // If failed in current row, try the next
1310 // Check if there's a cell in the "next" row
1311 res
= GetCellAt(table
, startRowIndex
, startColIndex
, getter_AddRefs(cell
));
1312 NS_ENSURE_SUCCESS(res
, res
);
1320 // Helper that doesn't batch or change the selection
1322 nsHTMLEditor::DeleteRow(nsIDOMElement
*aTable
, PRInt32 aRowIndex
)
1324 NS_ENSURE_TRUE(aTable
, NS_ERROR_NULL_POINTER
);
1326 nsCOMPtr
<nsIDOMElement
> cell
;
1327 nsCOMPtr
<nsIDOMElement
> cellInDeleteRow
;
1328 PRInt32 startRowIndex
, startColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
1330 PRInt32 colIndex
= 0;
1331 nsresult res
= NS_OK
;
1333 // Prevent rules testing until we're done
1334 nsAutoRules
beginRulesSniffing(this, kOpDeleteNode
, nsIEditor::eNext
);
1336 // The list of cells we will change rowspan in
1337 // and the new rowspan values for each
1338 nsTArray
<nsIDOMElement
*> spanCellList
;
1339 nsTArray
<PRInt32
> newSpanList
;
1341 // Scan through cells in row to do rowspan adjustments
1342 // Note that after we delete row, startRowIndex will point to the
1343 // cells in the next row to be deleted
1345 res
= GetCellDataAt(aTable
, aRowIndex
, colIndex
, getter_AddRefs(cell
),
1346 &startRowIndex
, &startColIndex
, &rowSpan
, &colSpan
,
1347 &actualRowSpan
, &actualColSpan
, &isSelected
);
1349 // We don't fail if we don't find a cell, so this must be real bad
1350 if(NS_FAILED(res
)) return res
;
1352 // Compensate for cells that don't start or extend below the row we are deleting
1355 if (startRowIndex
< aRowIndex
)
1357 // Cell starts in row above us
1358 // Decrease its rowspan to keep table rectangular
1359 // but we don't need to do this if rowspan=0,
1360 // since it will automatically adjust
1363 // Build list of cells to change rowspan
1364 // We can't do it now since it upsets cell map,
1365 // so we will do it after deleting the row
1366 spanCellList
.AppendElement(cell
);
1367 newSpanList
.AppendElement(NS_MAX((aRowIndex
- startRowIndex
), actualRowSpan
-1));
1374 //Cell spans below row to delete,
1375 // so we must insert new cells to keep rows below even
1376 // Note that we test "rowSpan" so we don't do this if rowSpan = 0 (automatic readjustment)
1377 res
= SplitCellIntoRows(aTable
, startRowIndex
, startColIndex
,
1378 aRowIndex
- startRowIndex
+ 1, // The row above the row to insert new cell into
1379 actualRowSpan
- 1, nsnull
); // Span remaining below
1380 NS_ENSURE_SUCCESS(res
, res
);
1382 if (!cellInDeleteRow
)
1383 cellInDeleteRow
= cell
; // Reference cell to find row to delete
1385 // Skip over other columns spanned by this cell
1386 colIndex
+= actualColSpan
;
1390 // Things are messed up if we didn't find a cell in the row!
1391 NS_ENSURE_TRUE(cellInDeleteRow
, NS_ERROR_FAILURE
);
1393 // Delete the entire row
1394 nsCOMPtr
<nsIDOMElement
> parentRow
;
1395 res
= GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cellInDeleteRow
, getter_AddRefs(parentRow
));
1396 NS_ENSURE_SUCCESS(res
, res
);
1400 res
= DeleteNode(parentRow
);
1401 NS_ENSURE_SUCCESS(res
, res
);
1404 // Now we can set new rowspans for cells stored above
1405 for (PRUint32 i
= 0, n
= spanCellList
.Length(); i
< n
; i
++)
1407 nsIDOMElement
*cellPtr
= spanCellList
[i
];
1410 res
= SetRowSpan(cellPtr
, newSpanList
[i
]);
1411 NS_ENSURE_SUCCESS(res
, res
);
1419 nsHTMLEditor::SelectTable()
1421 nsCOMPtr
<nsIDOMElement
> table
;
1422 nsresult res
= NS_ERROR_FAILURE
;
1423 res
= GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nsnull
, getter_AddRefs(table
));
1424 NS_ENSURE_SUCCESS(res
, res
);
1425 // Don't fail if we didn't find a table
1426 NS_ENSURE_TRUE(table
, NS_OK
);
1428 res
= ClearSelection();
1429 if (NS_SUCCEEDED(res
))
1430 res
= AppendNodeToSelectionAsRange(table
);
1436 nsHTMLEditor::SelectTableCell()
1438 nsCOMPtr
<nsIDOMElement
> cell
;
1439 nsresult res
= GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nsnull
, getter_AddRefs(cell
));
1440 NS_ENSURE_SUCCESS(res
, res
);
1441 NS_ENSURE_TRUE(cell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
1443 res
= ClearSelection();
1444 if (NS_SUCCEEDED(res
))
1445 res
= AppendNodeToSelectionAsRange(cell
);
1451 nsHTMLEditor::SelectBlockOfCells(nsIDOMElement
*aStartCell
, nsIDOMElement
*aEndCell
)
1453 NS_ENSURE_TRUE(aStartCell
&& aEndCell
, NS_ERROR_NULL_POINTER
);
1455 nsCOMPtr
<nsISelection
> selection
;
1456 nsresult res
= GetSelection(getter_AddRefs(selection
));
1457 NS_ENSURE_SUCCESS(res
, res
);
1458 NS_ENSURE_TRUE(selection
, NS_ERROR_FAILURE
);
1460 NS_NAMED_LITERAL_STRING(tableStr
, "table");
1461 nsCOMPtr
<nsIDOMElement
> table
;
1462 res
= GetElementOrParentByTagName(tableStr
, aStartCell
, getter_AddRefs(table
));
1463 NS_ENSURE_SUCCESS(res
, res
);
1464 NS_ENSURE_TRUE(table
, NS_ERROR_FAILURE
);
1466 nsCOMPtr
<nsIDOMElement
> endTable
;
1467 res
= GetElementOrParentByTagName(tableStr
, aEndCell
, getter_AddRefs(endTable
));
1468 NS_ENSURE_SUCCESS(res
, res
);
1469 NS_ENSURE_TRUE(endTable
, NS_ERROR_FAILURE
);
1471 // We can only select a block if within the same table,
1472 // so do nothing if not within one table
1473 if (table
!= endTable
) return NS_OK
;
1475 PRInt32 startRowIndex
, startColIndex
, endRowIndex
, endColIndex
;
1477 // Get starting and ending cells' location in the cellmap
1478 res
= GetCellIndexes(aStartCell
, &startRowIndex
, &startColIndex
);
1479 if(NS_FAILED(res
)) return res
;
1481 res
= GetCellIndexes(aEndCell
, &endRowIndex
, &endColIndex
);
1482 if(NS_FAILED(res
)) return res
;
1484 // Suppress nsISelectionListener notification
1485 // until all selection changes are finished
1486 nsSelectionBatcherForTable
selectionBatcher(selection
);
1488 // Examine all cell nodes in current selection and
1489 // remove those outside the new block cell region
1490 PRInt32 minColumn
= NS_MIN(startColIndex
, endColIndex
);
1491 PRInt32 minRow
= NS_MIN(startRowIndex
, endRowIndex
);
1492 PRInt32 maxColumn
= NS_MAX(startColIndex
, endColIndex
);
1493 PRInt32 maxRow
= NS_MAX(startRowIndex
, endRowIndex
);
1495 nsCOMPtr
<nsIDOMElement
> cell
;
1496 PRInt32 currentRowIndex
, currentColIndex
;
1497 nsCOMPtr
<nsIDOMRange
> range
;
1498 res
= GetFirstSelectedCell(getter_AddRefs(range
), getter_AddRefs(cell
));
1499 NS_ENSURE_SUCCESS(res
, res
);
1500 if (res
== NS_EDITOR_ELEMENT_NOT_FOUND
) return NS_OK
;
1504 res
= GetCellIndexes(cell
, ¤tRowIndex
, ¤tColIndex
);
1505 NS_ENSURE_SUCCESS(res
, res
);
1507 if (currentRowIndex
< maxRow
|| currentRowIndex
> maxRow
||
1508 currentColIndex
< maxColumn
|| currentColIndex
> maxColumn
)
1510 selection
->RemoveRange(range
);
1511 // Since we've removed the range, decrement pointer to next range
1512 mSelectedCellIndex
--;
1514 res
= GetNextSelectedCell(getter_AddRefs(range
), getter_AddRefs(cell
));
1515 NS_ENSURE_SUCCESS(res
, res
);
1518 PRInt32 rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
1520 for (PRInt32 row
= minRow
; row
<= maxRow
; row
++)
1522 for(PRInt32 col
= minColumn
; col
<= maxColumn
; col
+= NS_MAX(actualColSpan
, 1))
1524 res
= GetCellDataAt(table
, row
, col
, getter_AddRefs(cell
),
1525 ¤tRowIndex
, ¤tColIndex
,
1527 &actualRowSpan
, &actualColSpan
, &isSelected
);
1528 if (NS_FAILED(res
)) break;
1529 // Skip cells that already selected or are spanned from previous locations
1530 if (!isSelected
&& cell
&& row
== currentRowIndex
&& col
== currentColIndex
)
1532 res
= AppendNodeToSelectionAsRange(cell
);
1533 if (NS_FAILED(res
)) break;
1541 nsHTMLEditor::SelectAllTableCells()
1543 nsCOMPtr
<nsIDOMElement
> cell
;
1544 nsresult res
= GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nsnull
, getter_AddRefs(cell
));
1545 NS_ENSURE_SUCCESS(res
, res
);
1547 // Don't fail if we didn't find a cell
1548 NS_ENSURE_TRUE(cell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
1550 nsCOMPtr
<nsIDOMElement
> startCell
= cell
;
1553 nsCOMPtr
<nsIDOMElement
> table
;
1554 res
= GetElementOrParentByTagName(NS_LITERAL_STRING("table"), cell
, getter_AddRefs(table
));
1555 NS_ENSURE_SUCCESS(res
, res
);
1556 if(!table
) return NS_ERROR_NULL_POINTER
;
1558 PRInt32 rowCount
, colCount
;
1559 res
= GetTableSize(table
, &rowCount
, &colCount
);
1560 NS_ENSURE_SUCCESS(res
, res
);
1562 nsCOMPtr
<nsISelection
> selection
;
1563 res
= GetSelection(getter_AddRefs(selection
));
1564 NS_ENSURE_SUCCESS(res
, res
);
1565 NS_ENSURE_TRUE(selection
, NS_ERROR_FAILURE
);
1567 // Suppress nsISelectionListener notification
1568 // until all selection changes are finished
1569 nsSelectionBatcherForTable
selectionBatcher(selection
);
1571 // It is now safe to clear the selection
1572 // BE SURE TO RESET IT BEFORE LEAVING!
1573 res
= ClearSelection();
1575 // Select all cells in the same column as current cell
1576 PRBool cellSelected
= PR_FALSE
;
1577 PRInt32 rowSpan
, colSpan
, actualRowSpan
, actualColSpan
, currentRowIndex
, currentColIndex
;
1579 for(PRInt32 row
= 0; row
< rowCount
; row
++)
1581 for(PRInt32 col
= 0; col
< colCount
; col
+= NS_MAX(actualColSpan
, 1))
1583 res
= GetCellDataAt(table
, row
, col
, getter_AddRefs(cell
),
1584 ¤tRowIndex
, ¤tColIndex
,
1586 &actualRowSpan
, &actualColSpan
, &isSelected
);
1587 if (NS_FAILED(res
)) break;
1588 // Skip cells that are spanned from previous rows or columns
1589 if (cell
&& row
== currentRowIndex
&& col
== currentColIndex
)
1591 res
= AppendNodeToSelectionAsRange(cell
);
1592 if (NS_FAILED(res
)) break;
1593 cellSelected
= PR_TRUE
;
1597 // Safety code to select starting cell if nothing else was selected
1600 return AppendNodeToSelectionAsRange(startCell
);
1606 nsHTMLEditor::SelectTableRow()
1608 nsCOMPtr
<nsIDOMElement
> cell
;
1609 nsresult res
= GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nsnull
, getter_AddRefs(cell
));
1610 NS_ENSURE_SUCCESS(res
, res
);
1612 // Don't fail if we didn't find a cell
1613 NS_ENSURE_TRUE(cell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
1614 nsCOMPtr
<nsIDOMElement
> startCell
= cell
;
1616 // Get table and location of cell:
1617 nsCOMPtr
<nsISelection
> selection
;
1618 nsCOMPtr
<nsIDOMElement
> table
;
1619 PRInt32 startRowIndex
, startColIndex
;
1621 res
= GetCellContext(getter_AddRefs(selection
),
1622 getter_AddRefs(table
),
1623 getter_AddRefs(cell
),
1625 &startRowIndex
, &startColIndex
);
1626 NS_ENSURE_SUCCESS(res
, res
);
1627 NS_ENSURE_TRUE(table
, NS_ERROR_FAILURE
);
1629 PRInt32 rowCount
, colCount
;
1630 res
= GetTableSize(table
, &rowCount
, &colCount
);
1631 NS_ENSURE_SUCCESS(res
, res
);
1633 //Note: At this point, we could get first and last cells in row,
1634 // then call SelectBlockOfCells, but that would take just
1635 // a little less code, so the following is more efficient
1637 // Suppress nsISelectionListener notification
1638 // until all selection changes are finished
1639 nsSelectionBatcherForTable
selectionBatcher(selection
);
1641 // It is now safe to clear the selection
1642 // BE SURE TO RESET IT BEFORE LEAVING!
1643 res
= ClearSelection();
1645 // Select all cells in the same row as current cell
1646 PRBool cellSelected
= PR_FALSE
;
1647 PRInt32 rowSpan
, colSpan
, actualRowSpan
, actualColSpan
, currentRowIndex
, currentColIndex
;
1649 for(PRInt32 col
= 0; col
< colCount
; col
+= NS_MAX(actualColSpan
, 1))
1651 res
= GetCellDataAt(table
, startRowIndex
, col
, getter_AddRefs(cell
),
1652 ¤tRowIndex
, ¤tColIndex
, &rowSpan
, &colSpan
,
1653 &actualRowSpan
, &actualColSpan
, &isSelected
);
1654 if (NS_FAILED(res
)) break;
1655 // Skip cells that are spanned from previous rows or columns
1656 if (cell
&& currentRowIndex
== startRowIndex
&& currentColIndex
== col
)
1658 res
= AppendNodeToSelectionAsRange(cell
);
1659 if (NS_FAILED(res
)) break;
1660 cellSelected
= PR_TRUE
;
1663 // Safety code to select starting cell if nothing else was selected
1666 return AppendNodeToSelectionAsRange(startCell
);
1672 nsHTMLEditor::SelectTableColumn()
1674 nsCOMPtr
<nsIDOMElement
> cell
;
1675 nsresult res
= GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nsnull
, getter_AddRefs(cell
));
1676 NS_ENSURE_SUCCESS(res
, res
);
1678 // Don't fail if we didn't find a cell
1679 NS_ENSURE_TRUE(cell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
1681 nsCOMPtr
<nsIDOMElement
> startCell
= cell
;
1683 // Get location of cell:
1684 nsCOMPtr
<nsISelection
> selection
;
1685 nsCOMPtr
<nsIDOMElement
> table
;
1686 PRInt32 startRowIndex
, startColIndex
;
1688 res
= GetCellContext(getter_AddRefs(selection
),
1689 getter_AddRefs(table
),
1690 getter_AddRefs(cell
),
1692 &startRowIndex
, &startColIndex
);
1693 NS_ENSURE_SUCCESS(res
, res
);
1694 NS_ENSURE_TRUE(table
, NS_ERROR_FAILURE
);
1696 PRInt32 rowCount
, colCount
;
1697 res
= GetTableSize(table
, &rowCount
, &colCount
);
1698 NS_ENSURE_SUCCESS(res
, res
);
1700 // Suppress nsISelectionListener notification
1701 // until all selection changes are finished
1702 nsSelectionBatcherForTable
selectionBatcher(selection
);
1704 // It is now safe to clear the selection
1705 // BE SURE TO RESET IT BEFORE LEAVING!
1706 res
= ClearSelection();
1708 // Select all cells in the same column as current cell
1709 PRBool cellSelected
= PR_FALSE
;
1710 PRInt32 rowSpan
, colSpan
, actualRowSpan
, actualColSpan
, currentRowIndex
, currentColIndex
;
1712 for(PRInt32 row
= 0; row
< rowCount
; row
+= NS_MAX(actualRowSpan
, 1))
1714 res
= GetCellDataAt(table
, row
, startColIndex
, getter_AddRefs(cell
),
1715 ¤tRowIndex
, ¤tColIndex
, &rowSpan
, &colSpan
,
1716 &actualRowSpan
, &actualColSpan
, &isSelected
);
1717 if (NS_FAILED(res
)) break;
1718 // Skip cells that are spanned from previous rows or columns
1719 if (cell
&& currentRowIndex
== row
&& currentColIndex
== startColIndex
)
1721 res
= AppendNodeToSelectionAsRange(cell
);
1722 if (NS_FAILED(res
)) break;
1723 cellSelected
= PR_TRUE
;
1726 // Safety code to select starting cell if nothing else was selected
1729 return AppendNodeToSelectionAsRange(startCell
);
1735 nsHTMLEditor::SplitTableCell()
1737 nsCOMPtr
<nsIDOMElement
> table
;
1738 nsCOMPtr
<nsIDOMElement
> cell
;
1739 PRInt32 startRowIndex
, startColIndex
, actualRowSpan
, actualColSpan
;
1740 // Get cell, table, etc. at selection anchor node
1741 nsresult res
= GetCellContext(nsnull
,
1742 getter_AddRefs(table
),
1743 getter_AddRefs(cell
),
1745 &startRowIndex
, &startColIndex
);
1746 NS_ENSURE_SUCCESS(res
, res
);
1747 if(!table
|| !cell
) return NS_EDITOR_ELEMENT_NOT_FOUND
;
1749 // We need rowspan and colspan data
1750 res
= GetCellSpansAt(table
, startRowIndex
, startColIndex
, actualRowSpan
, actualColSpan
);
1751 NS_ENSURE_SUCCESS(res
, res
);
1753 // Must have some span to split
1754 if (actualRowSpan
<= 1 && actualColSpan
<= 1)
1757 nsAutoEditBatch
beginBatching(this);
1758 // Prevent auto insertion of BR in new cell until we're done
1759 nsAutoRules
beginRulesSniffing(this, kOpInsertNode
, nsIEditor::eNext
);
1761 // We reset selection
1762 nsSetSelectionAfterTableEdit
setCaret(this, table
, startRowIndex
, startColIndex
, ePreviousColumn
, PR_FALSE
);
1763 //...so suppress Rules System selection munging
1764 nsAutoTxnsConserveSelection
dontChangeSelection(this);
1766 nsCOMPtr
<nsIDOMElement
> newCell
;
1767 PRInt32 rowIndex
= startRowIndex
;
1768 PRInt32 rowSpanBelow
, colSpanAfter
;
1770 // Split up cell row-wise first into rowspan=1 above, and the rest below,
1771 // whittling away at the cell below until no more extra span
1772 for (rowSpanBelow
= actualRowSpan
-1; rowSpanBelow
>= 0; rowSpanBelow
--)
1774 // We really split row-wise only if we had rowspan > 1
1775 if (rowSpanBelow
> 0)
1777 res
= SplitCellIntoRows(table
, rowIndex
, startColIndex
, 1, rowSpanBelow
, getter_AddRefs(newCell
));
1778 NS_ENSURE_SUCCESS(res
, res
);
1779 CopyCellBackgroundColor(newCell
, cell
);
1781 PRInt32 colIndex
= startColIndex
;
1782 // Now split the cell with rowspan = 1 into cells if it has colSpan > 1
1783 for (colSpanAfter
= actualColSpan
-1; colSpanAfter
> 0; colSpanAfter
--)
1785 res
= SplitCellIntoColumns(table
, rowIndex
, colIndex
, 1, colSpanAfter
, getter_AddRefs(newCell
));
1786 NS_ENSURE_SUCCESS(res
, res
);
1787 CopyCellBackgroundColor(newCell
, cell
);
1790 // Point to the new cell and repeat
1797 nsHTMLEditor::CopyCellBackgroundColor(nsIDOMElement
*destCell
, nsIDOMElement
*sourceCell
)
1799 NS_ENSURE_TRUE(destCell
&& sourceCell
, NS_ERROR_NULL_POINTER
);
1801 // Copy backgournd color to new cell
1802 NS_NAMED_LITERAL_STRING(bgcolor
, "bgcolor");
1805 nsresult res
= GetAttributeValue(sourceCell
, bgcolor
, color
, &isSet
);
1807 if (NS_SUCCEEDED(res
) && isSet
)
1808 res
= SetAttribute(destCell
, bgcolor
, color
);
1814 nsHTMLEditor::SplitCellIntoColumns(nsIDOMElement
*aTable
, PRInt32 aRowIndex
, PRInt32 aColIndex
,
1815 PRInt32 aColSpanLeft
, PRInt32 aColSpanRight
,
1816 nsIDOMElement
**aNewCell
)
1818 NS_ENSURE_TRUE(aTable
, NS_ERROR_NULL_POINTER
);
1819 if (aNewCell
) *aNewCell
= nsnull
;
1821 nsCOMPtr
<nsIDOMElement
> cell
;
1822 PRInt32 startRowIndex
, startColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
1824 nsresult res
= GetCellDataAt(aTable
, aRowIndex
, aColIndex
, getter_AddRefs(cell
),
1825 &startRowIndex
, &startColIndex
,
1827 &actualRowSpan
, &actualColSpan
, &isSelected
);
1828 NS_ENSURE_SUCCESS(res
, res
);
1829 NS_ENSURE_TRUE(cell
, NS_ERROR_NULL_POINTER
);
1832 if (actualColSpan
<= 1 || (aColSpanLeft
+ aColSpanRight
) > actualColSpan
)
1835 // Reduce colspan of cell to split
1836 res
= SetColSpan(cell
, aColSpanLeft
);
1837 NS_ENSURE_SUCCESS(res
, res
);
1839 // Insert new cell after using the remaining span
1840 // and always get the new cell so we can copy the background color;
1841 nsCOMPtr
<nsIDOMElement
> newCell
;
1842 res
= InsertCell(cell
, actualRowSpan
, aColSpanRight
, PR_TRUE
, PR_FALSE
, getter_AddRefs(newCell
));
1843 NS_ENSURE_SUCCESS(res
, res
);
1848 *aNewCell
= newCell
.get();
1849 NS_ADDREF(*aNewCell
);
1851 res
= CopyCellBackgroundColor(newCell
, cell
);
1857 nsHTMLEditor::SplitCellIntoRows(nsIDOMElement
*aTable
, PRInt32 aRowIndex
, PRInt32 aColIndex
,
1858 PRInt32 aRowSpanAbove
, PRInt32 aRowSpanBelow
,
1859 nsIDOMElement
**aNewCell
)
1861 NS_ENSURE_TRUE(aTable
, NS_ERROR_NULL_POINTER
);
1862 if (aNewCell
) *aNewCell
= nsnull
;
1864 nsCOMPtr
<nsIDOMElement
> cell
;
1865 PRInt32 startRowIndex
, startColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
1867 nsresult res
= GetCellDataAt(aTable
, aRowIndex
, aColIndex
, getter_AddRefs(cell
),
1868 &startRowIndex
, &startColIndex
,
1870 &actualRowSpan
, &actualColSpan
, &isSelected
);
1871 NS_ENSURE_SUCCESS(res
, res
);
1872 NS_ENSURE_TRUE(cell
, NS_ERROR_NULL_POINTER
);
1875 if (actualRowSpan
<= 1 || (aRowSpanAbove
+ aRowSpanBelow
) > actualRowSpan
)
1878 PRInt32 rowCount
, colCount
;
1879 res
= GetTableSize(aTable
, &rowCount
, &colCount
);
1880 NS_ENSURE_SUCCESS(res
, res
);
1882 nsCOMPtr
<nsIDOMElement
> cell2
;
1883 nsCOMPtr
<nsIDOMElement
> lastCellFound
;
1884 PRInt32 startRowIndex2
, startColIndex2
, rowSpan2
, colSpan2
, actualRowSpan2
, actualColSpan2
;
1886 PRInt32 colIndex
= 0;
1887 PRBool insertAfter
= (startColIndex
> 0);
1888 // This is the row we will insert new cell into
1889 PRInt32 rowBelowIndex
= startRowIndex
+aRowSpanAbove
;
1891 // Find a cell to insert before or after
1894 // Search for a cell to insert before
1895 res
= GetCellDataAt(aTable
, rowBelowIndex
,
1896 colIndex
, getter_AddRefs(cell2
),
1897 &startRowIndex2
, &startColIndex2
, &rowSpan2
, &colSpan2
,
1898 &actualRowSpan2
, &actualColSpan2
, &isSelected2
);
1899 // If we fail here, it could be because row has bad rowspan values,
1900 // such as all cells having rowspan > 1 (Call FixRowSpan first!)
1901 if (NS_FAILED(res
) || !cell
) return NS_ERROR_FAILURE
;
1903 // Skip over cells spanned from above (like the one we are splitting!)
1904 if (cell2
&& startRowIndex2
== rowBelowIndex
)
1908 // New cell isn't first in row,
1909 // so stop after we find the cell just before new cell's column
1910 if ((startColIndex2
+ actualColSpan2
) == startColIndex
)
1913 // If cell found is AFTER desired new cell colum,
1914 // we have multiple cells with rowspan > 1 that
1915 // prevented us from finding a cell to insert after...
1916 if (startColIndex2
> startColIndex
)
1918 // ... so instead insert before the cell we found
1919 insertAfter
= PR_FALSE
;
1925 break; // Inserting before, so stop at first cell in row we want to insert into
1927 lastCellFound
= cell2
;
1929 // Skip to next available cellmap location
1930 colIndex
+= NS_MAX(actualColSpan2
, 1);
1932 // Done when past end of total number of columns
1933 if (colIndex
> colCount
)
1938 if (!cell2
&& lastCellFound
)
1940 // Edge case where we didn't find a cell to insert after
1941 // or before because column(s) before desired column
1942 // and all columns after it are spanned from above.
1943 // We can insert after the last cell we found
1944 cell2
= lastCellFound
;
1945 insertAfter
= PR_TRUE
; // Should always be true, but let's be sure
1948 // Reduce rowspan of cell to split
1949 res
= SetRowSpan(cell
, aRowSpanAbove
);
1950 NS_ENSURE_SUCCESS(res
, res
);
1953 // Insert new cell after using the remaining span
1954 // and always get the new cell so we can copy the background color;
1955 nsCOMPtr
<nsIDOMElement
> newCell
;
1956 res
= InsertCell(cell2
, aRowSpanBelow
, actualColSpan
, insertAfter
, PR_FALSE
, getter_AddRefs(newCell
));
1957 NS_ENSURE_SUCCESS(res
, res
);
1962 *aNewCell
= newCell
.get();
1963 NS_ADDREF(*aNewCell
);
1965 res
= CopyCellBackgroundColor(newCell
, cell2
);
1971 nsHTMLEditor::SwitchTableCellHeaderType(nsIDOMElement
*aSourceCell
, nsIDOMElement
**aNewCell
)
1973 NS_ENSURE_TRUE(aSourceCell
, NS_ERROR_NULL_POINTER
);
1975 nsAutoEditBatch
beginBatching(this);
1976 // Prevent auto insertion of BR in new cell created by ReplaceContainer
1977 nsAutoRules
beginRulesSniffing(this, kOpInsertNode
, nsIEditor::eNext
);
1979 nsCOMPtr
<nsIDOMNode
> newNode
;
1981 // Save current selection to restore when done
1982 // This is needed so ReplaceContainer can monitor selection
1983 // when replacing nodes
1984 nsCOMPtr
<nsISelection
>selection
;
1985 nsresult res
= GetSelection(getter_AddRefs(selection
));
1986 NS_ENSURE_SUCCESS(res
, res
);
1987 NS_ENSURE_TRUE(selection
, NS_ERROR_FAILURE
);
1988 nsAutoSelectionReset
selectionResetter(selection
, this);
1990 // Set to the opposite of current type
1991 nsCOMPtr
<nsIAtom
> atom
= nsEditor::GetTag(aSourceCell
);
1992 nsString
newCellType( (atom
== nsEditProperty::td
) ? NS_LITERAL_STRING("th") : NS_LITERAL_STRING("td"));
1994 // This creates new node, moves children, copies attributes (PR_TRUE)
1995 // and manages the selection!
1996 res
= ReplaceContainer(aSourceCell
, address_of(newNode
), newCellType
, nsnull
, nsnull
, PR_TRUE
);
1997 NS_ENSURE_SUCCESS(res
, res
);
1998 NS_ENSURE_TRUE(newNode
, NS_ERROR_FAILURE
);
2000 // Return the new cell
2003 nsCOMPtr
<nsIDOMElement
> newElement
= do_QueryInterface(newNode
);
2004 *aNewCell
= newElement
.get();
2005 NS_ADDREF(*aNewCell
);
2012 nsHTMLEditor::JoinTableCells(PRBool aMergeNonContiguousContents
)
2014 nsCOMPtr
<nsIDOMElement
> table
;
2015 nsCOMPtr
<nsIDOMElement
> targetCell
;
2016 PRInt32 startRowIndex
, startColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
2018 nsCOMPtr
<nsIDOMElement
> cell2
;
2019 PRInt32 startRowIndex2
, startColIndex2
, rowSpan2
, colSpan2
, actualRowSpan2
, actualColSpan2
;
2022 // Get cell, table, etc. at selection anchor node
2023 nsresult res
= GetCellContext(nsnull
,
2024 getter_AddRefs(table
),
2025 getter_AddRefs(targetCell
),
2027 &startRowIndex
, &startColIndex
);
2028 NS_ENSURE_SUCCESS(res
, res
);
2029 if(!table
|| !targetCell
) return NS_EDITOR_ELEMENT_NOT_FOUND
;
2031 nsAutoEditBatch
beginBatching(this);
2032 //Don't let Rules System change the selection
2033 nsAutoTxnsConserveSelection
dontChangeSelection(this);
2035 // Note: We dont' use nsSetSelectionAfterTableEdit here so the selection
2036 // is retained after joining. This leaves the target cell selected
2037 // as well as the "non-contiguous" cells, so user can see what happened.
2039 nsCOMPtr
<nsIDOMElement
> firstCell
;
2040 PRInt32 firstRowIndex
, firstColIndex
;
2041 res
= GetFirstSelectedCellInTable(&firstRowIndex
, &firstColIndex
, getter_AddRefs(firstCell
));
2042 NS_ENSURE_SUCCESS(res
, res
);
2044 PRBool joinSelectedCells
= PR_FALSE
;
2047 nsCOMPtr
<nsIDOMElement
> secondCell
;
2048 res
= GetNextSelectedCell(nsnull
, getter_AddRefs(secondCell
));
2049 NS_ENSURE_SUCCESS(res
, res
);
2051 // If only one cell is selected, join with cell to the right
2052 joinSelectedCells
= (secondCell
!= nsnull
);
2055 if (joinSelectedCells
)
2057 // We have selected cells: Join just contiguous cells
2058 // and just merge contents if not contiguous
2060 PRInt32 rowCount
, colCount
;
2061 res
= GetTableSize(table
, &rowCount
, &colCount
);
2062 NS_ENSURE_SUCCESS(res
, res
);
2064 // Get spans for cell we will merge into
2065 PRInt32 firstRowSpan
, firstColSpan
;
2066 res
= GetCellSpansAt( table
, firstRowIndex
, firstColIndex
, firstRowSpan
, firstColSpan
);
2067 NS_ENSURE_SUCCESS(res
, res
);
2069 // This defines the last indexes along the "edges"
2070 // of the contiguous block of cells, telling us
2071 // that we can join adjacent cells to the block
2072 // Start with same as the first values,
2073 // then expand as we find adjacent selected cells
2074 PRInt32 lastRowIndex
= firstRowIndex
;
2075 PRInt32 lastColIndex
= firstColIndex
;
2076 PRInt32 rowIndex
, colIndex
;
2078 // First pass: Determine boundaries of contiguous rectangular block
2079 // that we will join into one cell,
2080 // favoring adjacent cells in the same row
2081 for (rowIndex
= firstRowIndex
; rowIndex
<= lastRowIndex
; rowIndex
++)
2083 PRInt32 currentRowCount
= rowCount
;
2084 // Be sure each row doesn't have rowspan errors
2085 res
= FixBadRowSpan(table
, rowIndex
, rowCount
);
2086 NS_ENSURE_SUCCESS(res
, res
);
2087 // Adjust rowcount by number of rows we removed
2088 lastRowIndex
-= (currentRowCount
-rowCount
);
2090 PRBool cellFoundInRow
= PR_FALSE
;
2091 PRBool lastRowIsSet
= PR_FALSE
;
2092 PRInt32 lastColInRow
= 0;
2093 PRInt32 firstColInRow
= firstColIndex
;
2094 for (colIndex
= firstColIndex
; colIndex
< colCount
; colIndex
+= NS_MAX(actualColSpan2
, 1))
2096 res
= GetCellDataAt(table
, rowIndex
, colIndex
, getter_AddRefs(cell2
),
2097 &startRowIndex2
, &startColIndex2
,
2098 &rowSpan2
, &colSpan2
,
2099 &actualRowSpan2
, &actualColSpan2
, &isSelected2
);
2100 NS_ENSURE_SUCCESS(res
, res
);
2104 if (!cellFoundInRow
)
2105 // We've just found the first selected cell in this row
2106 firstColInRow
= colIndex
;
2108 if (rowIndex
> firstRowIndex
&& firstColInRow
!= firstColIndex
)
2110 // We're in at least the second row,
2111 // but left boundary is "ragged" (not the same as 1st row's start)
2112 //Let's just end block on previous row
2113 // and keep previous lastColIndex
2114 //TODO: We could try to find the Maximum firstColInRow
2115 // so our block can still extend down more rows?
2116 lastRowIndex
= NS_MAX(0,rowIndex
- 1);
2117 lastRowIsSet
= PR_TRUE
;
2120 // Save max selected column in this row, including extra colspan
2121 lastColInRow
= colIndex
+ (actualColSpan2
-1);
2122 cellFoundInRow
= PR_TRUE
;
2124 else if (cellFoundInRow
)
2126 // No cell or not selected, but at least one cell in row was found
2128 if (rowIndex
> (firstRowIndex
+1) && colIndex
<= lastColIndex
)
2130 // Cell is in a column less than current right border in
2131 // the third or higher selected row, so stop block at the previous row
2132 lastRowIndex
= NS_MAX(0,rowIndex
- 1);
2133 lastRowIsSet
= PR_TRUE
;
2135 // We're done with this row
2138 } // End of column loop
2140 // Done with this row
2143 if (rowIndex
== firstRowIndex
)
2145 // First row always initializes the right boundary
2146 lastColIndex
= lastColInRow
;
2149 // If we didn't determine last row above...
2152 if (colIndex
< lastColIndex
)
2154 // (don't think we ever get here?)
2155 // Cell is in a column less than current right boundary,
2156 // so stop block at the previous row
2157 lastRowIndex
= NS_MAX(0,rowIndex
- 1);
2161 // Go on to examine next row
2162 lastRowIndex
= rowIndex
+1;
2165 // Use the minimum col we found so far for right boundary
2166 lastColIndex
= NS_MIN(lastColIndex
, lastColInRow
);
2170 // No selected cells in this row -- stop at row above
2171 // and leave last column at its previous value
2172 lastRowIndex
= NS_MAX(0,rowIndex
- 1);
2176 // The list of cells we will delete after joining
2177 nsTArray
<nsIDOMElement
*> deleteList
;
2179 // 2nd pass: Do the joining and merging
2180 for (rowIndex
= 0; rowIndex
< rowCount
; rowIndex
++)
2182 for (colIndex
= 0; colIndex
< colCount
; colIndex
+= NS_MAX(actualColSpan2
, 1))
2184 res
= GetCellDataAt(table
, rowIndex
, colIndex
, getter_AddRefs(cell2
),
2185 &startRowIndex2
, &startColIndex2
,
2186 &rowSpan2
, &colSpan2
,
2187 &actualRowSpan2
, &actualColSpan2
, &isSelected2
);
2188 NS_ENSURE_SUCCESS(res
, res
);
2190 // If this is 0, we are past last cell in row, so exit the loop
2191 if (actualColSpan2
== 0)
2194 // Merge only selected cells (skip cell we're merging into, of course)
2195 if (isSelected2
&& cell2
!= firstCell
)
2197 if (rowIndex
>= firstRowIndex
&& rowIndex
<= lastRowIndex
&&
2198 colIndex
>= firstColIndex
&& colIndex
<= lastColIndex
)
2200 // We are within the join region
2201 // Problem: It is very tricky to delete cells as we merge,
2202 // since that will upset the cellmap
2203 // Instead, build a list of cells to delete and do it later
2204 NS_ASSERTION(startRowIndex2
== rowIndex
, "JoinTableCells: StartRowIndex is in row above");
2206 if (actualColSpan2
> 1)
2208 //Check if cell "hangs" off the boundary because of colspan > 1
2209 // Use split methods to chop off excess
2210 PRInt32 extraColSpan
= (startColIndex2
+ actualColSpan2
) - (lastColIndex
+1);
2211 if ( extraColSpan
> 0)
2213 res
= SplitCellIntoColumns(table
, startRowIndex2
, startColIndex2
,
2214 actualColSpan2
-extraColSpan
, extraColSpan
, nsnull
);
2215 NS_ENSURE_SUCCESS(res
, res
);
2219 res
= MergeCells(firstCell
, cell2
, PR_FALSE
);
2220 NS_ENSURE_SUCCESS(res
, res
);
2222 // Add cell to list to delete
2223 deleteList
.AppendElement(cell2
.get());
2225 else if (aMergeNonContiguousContents
)
2227 // Cell is outside join region -- just merge the contents
2228 res
= MergeCells(firstCell
, cell2
, PR_FALSE
);
2229 NS_ENSURE_SUCCESS(res
, res
);
2235 // All cell contents are merged. Delete the empty cells we accumulated
2236 // Prevent rules testing until we're done
2237 nsAutoRules
beginRulesSniffing(this, kOpDeleteNode
, nsIEditor::eNext
);
2239 for (PRUint32 i
= 0, n
= deleteList
.Length(); i
< n
; i
++)
2241 nsIDOMElement
*elementPtr
= deleteList
[i
];
2244 nsCOMPtr
<nsIDOMNode
> node
= do_QueryInterface(elementPtr
);
2245 res
= DeleteNode(node
);
2246 NS_ENSURE_SUCCESS(res
, res
);
2249 // Cleanup selection: remove ranges where cells were deleted
2250 nsCOMPtr
<nsISelection
> selection
;
2251 res
= GetSelection(getter_AddRefs(selection
));
2252 NS_ENSURE_SUCCESS(res
, res
);
2253 NS_ENSURE_TRUE(selection
, NS_ERROR_FAILURE
);
2256 res
= selection
->GetRangeCount(&rangeCount
);
2257 NS_ENSURE_SUCCESS(res
, res
);
2259 nsCOMPtr
<nsIDOMRange
> range
;
2261 for (i
= 0; i
< rangeCount
; i
++)
2263 res
= selection
->GetRangeAt(i
, getter_AddRefs(range
));
2264 NS_ENSURE_SUCCESS(res
, res
);
2265 NS_ENSURE_TRUE(range
, NS_ERROR_FAILURE
);
2267 nsCOMPtr
<nsIDOMElement
> deletedCell
;
2268 res
= GetCellFromRange(range
, getter_AddRefs(deletedCell
));
2271 selection
->RemoveRange(range
);
2277 // Set spans for the cell everthing merged into
2278 res
= SetRowSpan(firstCell
, lastRowIndex
-firstRowIndex
+1);
2279 NS_ENSURE_SUCCESS(res
, res
);
2280 res
= SetColSpan(firstCell
, lastColIndex
-firstColIndex
+1);
2281 NS_ENSURE_SUCCESS(res
, res
);
2284 // Fixup disturbances in table layout
2285 NormalizeTable(table
);
2289 // Joining with cell to the right -- get rowspan and colspan data of target cell
2290 res
= GetCellDataAt(table
, startRowIndex
, startColIndex
, getter_AddRefs(targetCell
),
2291 &startRowIndex
, &startColIndex
, &rowSpan
, &colSpan
,
2292 &actualRowSpan
, &actualColSpan
, &isSelected
);
2293 NS_ENSURE_SUCCESS(res
, res
);
2294 NS_ENSURE_TRUE(targetCell
, NS_ERROR_NULL_POINTER
);
2296 // Get data for cell to the right
2297 res
= GetCellDataAt(table
, startRowIndex
, startColIndex
+actualColSpan
, getter_AddRefs(cell2
),
2298 &startRowIndex2
, &startColIndex2
, &rowSpan2
, &colSpan2
,
2299 &actualRowSpan2
, &actualColSpan2
, &isSelected2
);
2300 NS_ENSURE_SUCCESS(res
, res
);
2301 if(!cell2
) return NS_OK
; // Don't fail if there's no cell
2304 NS_ASSERTION((startRowIndex
>= startRowIndex2
),"JoinCells: startRowIndex < startRowIndex2");
2306 // Figure out span of merged cell starting from target's starting row
2307 // to handle case of merged cell starting in a row above
2308 PRInt32 spanAboveMergedCell
= startRowIndex
- startRowIndex2
;
2309 PRInt32 effectiveRowSpan2
= actualRowSpan2
- spanAboveMergedCell
;
2311 if (effectiveRowSpan2
> actualRowSpan
)
2313 // Cell to the right spans into row below target
2314 // Split off portion below target cell's bottom-most row
2315 res
= SplitCellIntoRows(table
, startRowIndex2
, startColIndex2
,
2316 spanAboveMergedCell
+actualRowSpan
,
2317 effectiveRowSpan2
-actualRowSpan
, nsnull
);
2318 NS_ENSURE_SUCCESS(res
, res
);
2321 // Move contents from cell to the right
2322 // Delete the cell now only if it starts in the same row
2323 // and has enough row "height"
2324 res
= MergeCells(targetCell
, cell2
,
2325 (startRowIndex2
== startRowIndex
) &&
2326 (effectiveRowSpan2
>= actualRowSpan
));
2327 NS_ENSURE_SUCCESS(res
, res
);
2329 if (effectiveRowSpan2
< actualRowSpan
)
2331 // Merged cell is "shorter"
2332 // (there are cells(s) below it that are row-spanned by target cell)
2333 // We could try splitting those cells, but that's REAL messy,
2334 // so the safest thing to do is NOT really join the cells
2338 if( spanAboveMergedCell
> 0 )
2340 // Cell we merged started in a row above the target cell
2341 // Reduce rowspan to give room where target cell will extend it's colspan
2342 res
= SetRowSpan(cell2
, spanAboveMergedCell
);
2343 NS_ENSURE_SUCCESS(res
, res
);
2346 // Reset target cell's colspan to encompass cell to the right
2347 res
= SetColSpan(targetCell
, actualColSpan
+actualColSpan2
);
2348 NS_ENSURE_SUCCESS(res
, res
);
2354 nsHTMLEditor::MergeCells(nsCOMPtr
<nsIDOMElement
> aTargetCell
,
2355 nsCOMPtr
<nsIDOMElement
> aCellToMerge
,
2356 PRBool aDeleteCellToMerge
)
2358 NS_ENSURE_TRUE(aTargetCell
&& aCellToMerge
, NS_ERROR_NULL_POINTER
);
2360 nsresult res
= NS_OK
;
2362 // Prevent rules testing until we're done
2363 nsAutoRules
beginRulesSniffing(this, kOpDeleteNode
, nsIEditor::eNext
);
2365 // Don't need to merge if cell is empty
2366 if (!IsEmptyCell(aCellToMerge
))
2368 // Get index of last child in target cell
2369 nsCOMPtr
<nsIDOMNodeList
> childNodes
;
2370 nsCOMPtr
<nsIDOMNode
> cellChild
;
2371 res
= aTargetCell
->GetChildNodes(getter_AddRefs(childNodes
));
2372 // If we fail or don't have children,
2373 // we insert at index 0
2374 PRInt32 insertIndex
= 0;
2376 if ((NS_SUCCEEDED(res
)) && (childNodes
))
2378 // Start inserting just after last child
2380 res
= childNodes
->GetLength(&len
);
2381 NS_ENSURE_SUCCESS(res
, res
);
2382 if (len
== 1 && IsEmptyCell(aTargetCell
))
2384 // Delete the empty node
2385 nsCOMPtr
<nsIDOMNode
> tempNode
;
2386 res
= childNodes
->Item(0, getter_AddRefs(cellChild
));
2387 NS_ENSURE_SUCCESS(res
, res
);
2388 res
= DeleteNode(cellChild
);
2389 NS_ENSURE_SUCCESS(res
, res
);
2393 insertIndex
= (PRInt32
)len
;
2396 // Move the contents
2398 aCellToMerge
->HasChildNodes(&hasChild
);
2401 aCellToMerge
->GetLastChild(getter_AddRefs(cellChild
));
2402 res
= DeleteNode(cellChild
);
2403 NS_ENSURE_SUCCESS(res
, res
);
2405 res
= InsertNode(cellChild
, aTargetCell
, insertIndex
);
2406 NS_ENSURE_SUCCESS(res
, res
);
2408 aCellToMerge
->HasChildNodes(&hasChild
);
2412 // Delete cells whose contents were moved
2413 if (aDeleteCellToMerge
)
2414 res
= DeleteNode(aCellToMerge
);
2421 nsHTMLEditor::FixBadRowSpan(nsIDOMElement
*aTable
, PRInt32 aRowIndex
, PRInt32
& aNewRowCount
)
2423 NS_ENSURE_TRUE(aTable
, NS_ERROR_NULL_POINTER
);
2425 PRInt32 rowCount
, colCount
;
2426 nsresult res
= GetTableSize(aTable
, &rowCount
, &colCount
);
2427 NS_ENSURE_SUCCESS(res
, res
);
2429 nsCOMPtr
<nsIDOMElement
>cell
;
2430 PRInt32 startRowIndex
, startColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
2433 PRInt32 minRowSpan
= -1;
2436 for( colIndex
= 0; colIndex
< colCount
; colIndex
+= NS_MAX(actualColSpan
, 1))
2438 res
= GetCellDataAt(aTable
, aRowIndex
, colIndex
, getter_AddRefs(cell
),
2439 &startRowIndex
, &startColIndex
, &rowSpan
, &colSpan
,
2440 &actualRowSpan
, &actualColSpan
, &isSelected
);
2441 // NOTE: This is a *real* failure.
2442 // GetCellDataAt passes if cell is missing from cellmap
2443 if(NS_FAILED(res
)) return res
;
2446 startRowIndex
== aRowIndex
&&
2447 (rowSpan
< minRowSpan
|| minRowSpan
== -1))
2449 minRowSpan
= rowSpan
;
2451 NS_ASSERTION((actualColSpan
> 0),"ActualColSpan = 0 in FixBadRowSpan");
2455 // The amount to reduce everyone's rowspan
2456 // so at least one cell has rowspan = 1
2457 PRInt32 rowsReduced
= minRowSpan
- 1;
2458 for(colIndex
= 0; colIndex
< colCount
; colIndex
+= NS_MAX(actualColSpan
, 1))
2460 res
= GetCellDataAt(aTable
, aRowIndex
, colIndex
, getter_AddRefs(cell
),
2461 &startRowIndex
, &startColIndex
, &rowSpan
, &colSpan
,
2462 &actualRowSpan
, &actualColSpan
, &isSelected
);
2463 if(NS_FAILED(res
)) return res
;
2464 // Fixup rowspans only for cells starting in current row
2465 if(cell
&& rowSpan
> 0 &&
2466 startRowIndex
== aRowIndex
&&
2467 startColIndex
== colIndex
)
2469 res
= SetRowSpan(cell
, rowSpan
-rowsReduced
);
2470 if(NS_FAILED(res
)) return res
;
2472 NS_ASSERTION((actualColSpan
> 0),"ActualColSpan = 0 in FixBadRowSpan");
2475 return GetTableSize(aTable
, &aNewRowCount
, &colCount
);
2479 nsHTMLEditor::FixBadColSpan(nsIDOMElement
*aTable
, PRInt32 aColIndex
, PRInt32
& aNewColCount
)
2481 NS_ENSURE_TRUE(aTable
, NS_ERROR_NULL_POINTER
);
2483 PRInt32 rowCount
, colCount
;
2484 nsresult res
= GetTableSize(aTable
, &rowCount
, &colCount
);
2485 NS_ENSURE_SUCCESS(res
, res
);
2487 nsCOMPtr
<nsIDOMElement
> cell
;
2488 PRInt32 startRowIndex
, startColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
2491 PRInt32 minColSpan
= -1;
2494 for( rowIndex
= 0; rowIndex
< rowCount
; rowIndex
+= NS_MAX(actualRowSpan
, 1))
2496 res
= GetCellDataAt(aTable
, rowIndex
, aColIndex
, getter_AddRefs(cell
),
2497 &startRowIndex
, &startColIndex
, &rowSpan
, &colSpan
,
2498 &actualRowSpan
, &actualColSpan
, &isSelected
);
2499 // NOTE: This is a *real* failure.
2500 // GetCellDataAt passes if cell is missing from cellmap
2501 if(NS_FAILED(res
)) return res
;
2504 startColIndex
== aColIndex
&&
2505 (colSpan
< minColSpan
|| minColSpan
== -1))
2507 minColSpan
= colSpan
;
2509 NS_ASSERTION((actualRowSpan
> 0),"ActualRowSpan = 0 in FixBadColSpan");
2513 // The amount to reduce everyone's colspan
2514 // so at least one cell has colspan = 1
2515 PRInt32 colsReduced
= minColSpan
- 1;
2516 for(rowIndex
= 0; rowIndex
< rowCount
; rowIndex
+= NS_MAX(actualRowSpan
, 1))
2518 res
= GetCellDataAt(aTable
, rowIndex
, aColIndex
, getter_AddRefs(cell
),
2519 &startRowIndex
, &startColIndex
, &rowSpan
, &colSpan
,
2520 &actualRowSpan
, &actualColSpan
, &isSelected
);
2521 if(NS_FAILED(res
)) return res
;
2522 // Fixup colspans only for cells starting in current column
2523 if(cell
&& colSpan
> 0 &&
2524 startColIndex
== aColIndex
&&
2525 startRowIndex
== rowIndex
)
2527 res
= SetColSpan(cell
, colSpan
-colsReduced
);
2528 if(NS_FAILED(res
)) return res
;
2530 NS_ASSERTION((actualRowSpan
> 0),"ActualRowSpan = 0 in FixBadColSpan");
2533 return GetTableSize(aTable
, &rowCount
, &aNewColCount
);
2537 nsHTMLEditor::NormalizeTable(nsIDOMElement
*aTable
)
2539 nsCOMPtr
<nsISelection
>selection
;
2540 nsresult res
= GetSelection(getter_AddRefs(selection
));
2541 NS_ENSURE_SUCCESS(res
, res
);
2542 NS_ENSURE_TRUE(selection
, NS_ERROR_FAILURE
);
2544 nsCOMPtr
<nsIDOMElement
> table
;
2545 res
= GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aTable
, getter_AddRefs(table
));
2546 NS_ENSURE_SUCCESS(res
, res
);
2547 // Don't fail if we didn't find a table
2548 NS_ENSURE_TRUE(table
, NS_OK
);
2550 PRInt32 rowCount
, colCount
, rowIndex
, colIndex
;
2551 res
= GetTableSize(table
, &rowCount
, &colCount
);
2552 NS_ENSURE_SUCCESS(res
, res
);
2554 // Save current selection
2555 nsAutoSelectionReset
selectionResetter(selection
, this);
2557 nsAutoEditBatch
beginBatching(this);
2558 // Prevent auto insertion of BR in new cell until we're done
2559 nsAutoRules
beginRulesSniffing(this, kOpInsertNode
, nsIEditor::eNext
);
2561 nsCOMPtr
<nsIDOMElement
> cell
;
2562 PRInt32 startRowIndex
, startColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
2565 // Scan all cells in each row to detect bad rowspan values
2566 for(rowIndex
= 0; rowIndex
< rowCount
; rowIndex
++)
2568 res
= FixBadRowSpan(table
, rowIndex
, rowCount
);
2569 NS_ENSURE_SUCCESS(res
, res
);
2571 // and same for colspans
2572 for(colIndex
= 0; colIndex
< colCount
; colIndex
++)
2574 res
= FixBadColSpan(table
, colIndex
, colCount
);
2575 NS_ENSURE_SUCCESS(res
, res
);
2578 // Fill in missing cellmap locations with empty cells
2579 for(rowIndex
= 0; rowIndex
< rowCount
; rowIndex
++)
2581 nsCOMPtr
<nsIDOMElement
> previousCellInRow
;
2583 for(colIndex
= 0; colIndex
< colCount
; colIndex
++)
2585 res
= GetCellDataAt(table
, rowIndex
, colIndex
, getter_AddRefs(cell
),
2586 &startRowIndex
, &startColIndex
, &rowSpan
, &colSpan
,
2587 &actualRowSpan
, &actualColSpan
, &isSelected
);
2588 // NOTE: This is a *real* failure.
2589 // GetCellDataAt passes if cell is missing from cellmap
2590 if(NS_FAILED(res
)) return res
;
2593 //We are missing a cell at a cellmap location
2595 printf("NormalizeTable found missing cell at row=%d, col=%d\n", rowIndex
, colIndex
);
2597 // Add a cell after the previous Cell in the current row
2598 if(previousCellInRow
)
2600 // Insert a new cell after (PR_TRUE), and return the new cell to us
2601 res
= InsertCell(previousCellInRow
, 1, 1, PR_TRUE
, PR_FALSE
, getter_AddRefs(cell
));
2602 NS_ENSURE_SUCCESS(res
, res
);
2604 // Set this so we use returned new "cell" to set previousCellInRow below
2606 startRowIndex
= rowIndex
;
2608 // We don't have any cells in this row -- We are really messed up!
2610 printf("NormalizeTable found no cells in row=%d, col=%d\n", rowIndex
, colIndex
);
2612 return NS_ERROR_FAILURE
;
2615 // Save the last cell found in the same row we are scanning
2616 if(startRowIndex
== rowIndex
)
2618 previousCellInRow
= cell
;
2626 nsHTMLEditor::GetCellIndexes(nsIDOMElement
*aCell
,
2627 PRInt32
*aRowIndex
, PRInt32
*aColIndex
)
2629 NS_ENSURE_ARG_POINTER(aRowIndex
);
2630 *aColIndex
=0; // initialize out params
2631 NS_ENSURE_ARG_POINTER(aColIndex
);
2633 nsresult res
=NS_ERROR_NOT_INITIALIZED
;
2636 // Get the selected cell or the cell enclosing the selection anchor
2637 nsCOMPtr
<nsIDOMElement
> cell
;
2638 res
= GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nsnull
, getter_AddRefs(cell
));
2639 if (NS_SUCCEEDED(res
) && cell
)
2642 return NS_ERROR_FAILURE
;
2645 NS_ENSURE_TRUE(mPresShellWeak
, NS_ERROR_NOT_INITIALIZED
);
2646 nsCOMPtr
<nsIPresShell
> ps
= do_QueryReferent(mPresShellWeak
);
2647 NS_ENSURE_TRUE(ps
, NS_ERROR_NOT_INITIALIZED
);
2649 nsCOMPtr
<nsIContent
> nodeAsContent( do_QueryInterface(aCell
) );
2650 NS_ENSURE_TRUE(nodeAsContent
, NS_ERROR_FAILURE
);
2651 // frames are not ref counted, so don't use an nsCOMPtr
2652 nsIFrame
*layoutObject
= nodeAsContent
->GetPrimaryFrame();
2653 NS_ENSURE_TRUE(layoutObject
, NS_ERROR_FAILURE
);
2655 nsITableCellLayout
*cellLayoutObject
= do_QueryFrame(layoutObject
);
2656 NS_ENSURE_TRUE(cellLayoutObject
, NS_ERROR_FAILURE
);
2657 return cellLayoutObject
->GetCellIndexes(*aRowIndex
, *aColIndex
);
2661 nsHTMLEditor::GetTableLayoutObject(nsIDOMElement
* aTable
, nsITableLayout
**tableLayoutObject
)
2663 *tableLayoutObject
=nsnull
;
2664 NS_ENSURE_TRUE(aTable
, NS_ERROR_NOT_INITIALIZED
);
2665 NS_ENSURE_TRUE(mPresShellWeak
, NS_ERROR_NOT_INITIALIZED
);
2666 nsCOMPtr
<nsIPresShell
> ps
= do_QueryReferent(mPresShellWeak
);
2667 NS_ENSURE_TRUE(ps
, NS_ERROR_NOT_INITIALIZED
);
2669 nsCOMPtr
<nsIContent
> nodeAsContent( do_QueryInterface(aTable
) );
2670 NS_ENSURE_TRUE(nodeAsContent
, NS_ERROR_FAILURE
);
2671 // frames are not ref counted, so don't use an nsCOMPtr
2672 nsIFrame
*layoutObject
= nodeAsContent
->GetPrimaryFrame();
2673 NS_ENSURE_TRUE(layoutObject
, NS_ERROR_FAILURE
);
2675 *tableLayoutObject
= do_QueryFrame(layoutObject
);
2676 return *tableLayoutObject
? NS_OK
: NS_NOINTERFACE
;
2679 //Return actual number of cells (a cell with colspan > 1 counts as just 1)
2680 PRBool
nsHTMLEditor::GetNumberOfCellsInRow(nsIDOMElement
* aTable
, PRInt32 rowIndex
)
2682 PRInt32 cellCount
= 0;
2683 nsCOMPtr
<nsIDOMElement
> cell
;
2684 PRInt32 colIndex
= 0;
2687 PRInt32 startRowIndex
, startColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
2689 res
= GetCellDataAt(aTable
, rowIndex
, colIndex
, getter_AddRefs(cell
),
2690 &startRowIndex
, &startColIndex
, &rowSpan
, &colSpan
,
2691 &actualRowSpan
, &actualColSpan
, &isSelected
);
2692 NS_ENSURE_SUCCESS(res
, res
);
2695 // Only count cells that start in row we are working with
2696 if (startRowIndex
== rowIndex
)
2699 //Next possible location for a cell
2700 colIndex
+= actualColSpan
;
2710 /* Not scriptable: For convenience in C++
2711 Use GetTableRowCount and GetTableColumnCount from JavaScript
2714 nsHTMLEditor::GetTableSize(nsIDOMElement
*aTable
,
2715 PRInt32
* aRowCount
, PRInt32
* aColCount
)
2717 NS_ENSURE_ARG_POINTER(aRowCount
);
2718 NS_ENSURE_ARG_POINTER(aColCount
);
2722 nsCOMPtr
<nsIDOMElement
> table
;
2723 // Get the selected talbe or the table enclosing the selection anchor
2724 res
= GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aTable
, getter_AddRefs(table
));
2725 NS_ENSURE_SUCCESS(res
, res
);
2726 NS_ENSURE_TRUE(table
, NS_ERROR_FAILURE
);
2728 // frames are not ref counted, so don't use an nsCOMPtr
2729 nsITableLayout
*tableLayoutObject
;
2730 res
= GetTableLayoutObject(table
.get(), &tableLayoutObject
);
2731 NS_ENSURE_SUCCESS(res
, res
);
2732 NS_ENSURE_TRUE(tableLayoutObject
, NS_ERROR_FAILURE
);
2734 return tableLayoutObject
->GetTableSize(*aRowCount
, *aColCount
);
2738 nsHTMLEditor::GetCellDataAt(nsIDOMElement
* aTable
, PRInt32 aRowIndex
,
2739 PRInt32 aColIndex
, nsIDOMElement
**aCell
,
2740 PRInt32
* aStartRowIndex
, PRInt32
* aStartColIndex
,
2741 PRInt32
* aRowSpan
, PRInt32
* aColSpan
,
2742 PRInt32
* aActualRowSpan
, PRInt32
* aActualColSpan
,
2743 PRBool
* aIsSelected
)
2745 NS_ENSURE_ARG_POINTER(aStartRowIndex
);
2746 NS_ENSURE_ARG_POINTER(aStartColIndex
);
2747 NS_ENSURE_ARG_POINTER(aRowSpan
);
2748 NS_ENSURE_ARG_POINTER(aColSpan
);
2749 NS_ENSURE_ARG_POINTER(aActualRowSpan
);
2750 NS_ENSURE_ARG_POINTER(aActualColSpan
);
2751 NS_ENSURE_ARG_POINTER(aIsSelected
);
2752 NS_ENSURE_TRUE(aCell
, NS_ERROR_NULL_POINTER
);
2754 nsresult res
=NS_ERROR_FAILURE
;
2755 *aStartRowIndex
= 0;
2756 *aStartColIndex
= 0;
2759 *aActualRowSpan
= 0;
2760 *aActualColSpan
= 0;
2761 *aIsSelected
= PR_FALSE
;
2767 // Get the selected table or the table enclosing the selection anchor
2768 nsCOMPtr
<nsIDOMElement
> table
;
2769 res
= GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nsnull
, getter_AddRefs(table
));
2770 NS_ENSURE_SUCCESS(res
, res
);
2774 return NS_ERROR_FAILURE
;
2777 // frames are not ref counted, so don't use an nsCOMPtr
2778 nsITableLayout
*tableLayoutObject
;
2779 res
= GetTableLayoutObject(aTable
, &tableLayoutObject
);
2780 NS_ENSURE_SUCCESS(res
, res
);
2781 NS_ENSURE_TRUE(tableLayoutObject
, NS_ERROR_FAILURE
);
2783 // Note that this returns NS_TABLELAYOUT_CELL_NOT_FOUND when
2784 // the index(es) are out of bounds
2785 nsCOMPtr
<nsIDOMElement
> cell
;
2786 res
= tableLayoutObject
->GetCellDataAt(aRowIndex
, aColIndex
,
2787 *getter_AddRefs(cell
),
2788 *aStartRowIndex
, *aStartColIndex
,
2789 *aRowSpan
, *aColSpan
,
2790 *aActualRowSpan
, *aActualColSpan
,
2794 *aCell
= cell
.get();
2797 // Convert to editor's generic "not found" return value
2798 if (res
== NS_TABLELAYOUT_CELL_NOT_FOUND
) res
= NS_EDITOR_ELEMENT_NOT_FOUND
;
2802 // When all you want is the cell
2804 nsHTMLEditor::GetCellAt(nsIDOMElement
* aTable
, PRInt32 aRowIndex
, PRInt32 aColIndex
, nsIDOMElement
**aCell
)
2806 PRInt32 startRowIndex
, startColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
2808 return GetCellDataAt(aTable
, aRowIndex
, aColIndex
, aCell
,
2809 &startRowIndex
, &startColIndex
, &rowSpan
, &colSpan
,
2810 &actualRowSpan
, &actualColSpan
, &isSelected
);
2813 // When all you want are the rowspan and colspan (not exposed in nsITableEditor)
2815 nsHTMLEditor::GetCellSpansAt(nsIDOMElement
* aTable
, PRInt32 aRowIndex
, PRInt32 aColIndex
,
2816 PRInt32
& aActualRowSpan
, PRInt32
& aActualColSpan
)
2818 nsCOMPtr
<nsIDOMElement
> cell
;
2819 PRInt32 startRowIndex
, startColIndex
, rowSpan
, colSpan
;
2821 return GetCellDataAt(aTable
, aRowIndex
, aColIndex
, getter_AddRefs(cell
),
2822 &startRowIndex
, &startColIndex
, &rowSpan
, &colSpan
,
2823 &aActualRowSpan
, &aActualColSpan
, &isSelected
);
2827 nsHTMLEditor::GetCellContext(nsISelection
**aSelection
,
2828 nsIDOMElement
**aTable
,
2829 nsIDOMElement
**aCell
,
2830 nsIDOMNode
**aCellParent
, PRInt32
*aCellOffset
,
2831 PRInt32
*aRowIndex
, PRInt32
*aColIndex
)
2833 // Initialize return pointers
2834 if (aSelection
) *aSelection
= nsnull
;
2835 if (aTable
) *aTable
= nsnull
;
2836 if (aCell
) *aCell
= nsnull
;
2837 if (aCellParent
) *aCellParent
= nsnull
;
2838 if (aCellOffset
) *aCellOffset
= 0;
2839 if (aRowIndex
) *aRowIndex
= 0;
2840 if (aColIndex
) *aColIndex
= 0;
2842 nsCOMPtr
<nsISelection
> selection
;
2843 nsresult res
= GetSelection(getter_AddRefs(selection
));
2844 NS_ENSURE_SUCCESS(res
, res
);
2845 NS_ENSURE_TRUE(selection
, NS_ERROR_FAILURE
);
2849 *aSelection
= selection
.get();
2850 NS_ADDREF(*aSelection
);
2852 nsCOMPtr
<nsIDOMElement
> table
;
2853 nsCOMPtr
<nsIDOMElement
> cell
;
2855 // Caller may supply the cell...
2856 if (aCell
&& *aCell
)
2859 // ...but if not supplied,
2860 // get cell if it's the child of selection anchor node,
2861 // or get the enclosing by a cell
2864 // Find a selected or enclosing table element
2865 nsCOMPtr
<nsIDOMElement
> cellOrTableElement
;
2866 PRInt32 selectedCount
;
2867 nsAutoString tagName
;
2868 res
= GetSelectedOrParentTableElement(tagName
, &selectedCount
,
2869 getter_AddRefs(cellOrTableElement
));
2870 NS_ENSURE_SUCCESS(res
, res
);
2871 if (tagName
.EqualsLiteral("table"))
2873 // We have a selected table, not a cell
2876 *aTable
= cellOrTableElement
.get();
2881 if (!tagName
.EqualsLiteral("td"))
2882 return NS_EDITOR_ELEMENT_NOT_FOUND
;
2885 cell
= cellOrTableElement
;
2889 *aCell
= cell
.get();
2893 // Get containing table
2894 res
= GetElementOrParentByTagName(NS_LITERAL_STRING("table"), cell
, getter_AddRefs(table
));
2895 NS_ENSURE_SUCCESS(res
, res
);
2896 // Cell must be in a table, so fail if not found
2897 NS_ENSURE_TRUE(table
, NS_ERROR_FAILURE
);
2900 *aTable
= table
.get();
2904 // Get the rest of the related data only if requested
2905 if (aRowIndex
|| aColIndex
)
2907 PRInt32 rowIndex
, colIndex
;
2908 // Get current cell location so we can put caret back there when done
2909 res
= GetCellIndexes(cell
, &rowIndex
, &colIndex
);
2910 if(NS_FAILED(res
)) return res
;
2911 if (aRowIndex
) *aRowIndex
= rowIndex
;
2912 if (aColIndex
) *aColIndex
= colIndex
;
2916 nsCOMPtr
<nsIDOMNode
> cellParent
;
2917 // Get the immediate parent of the cell
2918 res
= cell
->GetParentNode(getter_AddRefs(cellParent
));
2919 NS_ENSURE_SUCCESS(res
, res
);
2920 // Cell has to have a parent, so fail if not found
2921 NS_ENSURE_TRUE(cellParent
, NS_ERROR_FAILURE
);
2923 *aCellParent
= cellParent
.get();
2924 NS_ADDREF(*aCellParent
);
2927 res
= GetChildOffset(cell
, cellParent
, *aCellOffset
);
2934 nsHTMLEditor::GetCellFromRange(nsIDOMRange
*aRange
, nsIDOMElement
**aCell
)
2936 // Note: this might return a node that is outside of the range.
2938 NS_ENSURE_TRUE(aRange
&& aCell
, NS_ERROR_NULL_POINTER
);
2942 nsCOMPtr
<nsIDOMNode
> startParent
;
2943 nsresult res
= aRange
->GetStartContainer(getter_AddRefs(startParent
));
2944 NS_ENSURE_SUCCESS(res
, res
);
2945 NS_ENSURE_TRUE(startParent
, NS_ERROR_FAILURE
);
2947 PRInt32 startOffset
;
2948 res
= aRange
->GetStartOffset(&startOffset
);
2949 NS_ENSURE_SUCCESS(res
, res
);
2951 nsCOMPtr
<nsIDOMNode
> childNode
= GetChildAt(startParent
, startOffset
);
2952 // This means selection is probably at a text node (or end of doc?)
2953 NS_ENSURE_TRUE(childNode
, NS_ERROR_FAILURE
);
2955 nsCOMPtr
<nsIDOMNode
> endParent
;
2956 res
= aRange
->GetEndContainer(getter_AddRefs(endParent
));
2957 NS_ENSURE_SUCCESS(res
, res
);
2958 NS_ENSURE_TRUE(startParent
, NS_ERROR_FAILURE
);
2961 res
= aRange
->GetEndOffset(&endOffset
);
2962 NS_ENSURE_SUCCESS(res
, res
);
2964 // If a cell is deleted, the range is collapse
2965 // (startOffset == endOffset)
2966 // so tell caller the cell wasn't found
2967 if (startParent
== endParent
&&
2968 endOffset
== startOffset
+1 &&
2969 nsHTMLEditUtils::IsTableCell(childNode
))
2971 // Should we also test if frame is selected? (Use GetCellDataAt())
2972 // (Let's not for now -- more efficient)
2973 nsCOMPtr
<nsIDOMElement
> cellElement
= do_QueryInterface(childNode
);
2974 *aCell
= cellElement
.get();
2978 return NS_EDITOR_ELEMENT_NOT_FOUND
;
2982 nsHTMLEditor::GetFirstSelectedCell(nsIDOMRange
**aRange
, nsIDOMElement
**aCell
)
2984 NS_ENSURE_TRUE(aCell
, NS_ERROR_NULL_POINTER
);
2986 if (aRange
) *aRange
= nsnull
;
2988 nsCOMPtr
<nsISelection
> selection
;
2989 nsresult res
= GetSelection(getter_AddRefs(selection
));
2990 NS_ENSURE_SUCCESS(res
, res
);
2991 NS_ENSURE_TRUE(selection
, NS_ERROR_FAILURE
);
2993 nsCOMPtr
<nsIDOMRange
> range
;
2994 res
= selection
->GetRangeAt(0, getter_AddRefs(range
));
2995 NS_ENSURE_SUCCESS(res
, res
);
2996 NS_ENSURE_TRUE(range
, NS_ERROR_FAILURE
);
2998 mSelectedCellIndex
= 0;
3000 res
= GetCellFromRange(range
, aCell
);
3001 // Failure here probably means selection is in a text node,
3002 // so there's no selected cell
3003 NS_ENSURE_SUCCESS(res
, NS_EDITOR_ELEMENT_NOT_FOUND
);
3004 // No cell means range was collapsed (cell was deleted)
3005 NS_ENSURE_TRUE(*aCell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
3009 *aRange
= range
.get();
3013 // Setup for next cell
3014 mSelectedCellIndex
= 1;
3020 nsHTMLEditor::GetNextSelectedCell(nsIDOMRange
**aRange
, nsIDOMElement
**aCell
)
3022 NS_ENSURE_TRUE(aCell
, NS_ERROR_NULL_POINTER
);
3024 if (aRange
) *aRange
= nsnull
;
3026 nsCOMPtr
<nsISelection
> selection
;
3027 nsresult res
= GetSelection(getter_AddRefs(selection
));
3028 NS_ENSURE_SUCCESS(res
, res
);
3029 NS_ENSURE_TRUE(selection
, NS_ERROR_FAILURE
);
3032 res
= selection
->GetRangeCount(&rangeCount
);
3033 NS_ENSURE_SUCCESS(res
, res
);
3035 // Don't even try if index exceeds range count
3036 if (mSelectedCellIndex
>= rangeCount
)
3037 return NS_EDITOR_ELEMENT_NOT_FOUND
;
3039 // Scan through ranges to find next valid selected cell
3040 nsCOMPtr
<nsIDOMRange
> range
;
3041 for (; mSelectedCellIndex
< rangeCount
; mSelectedCellIndex
++)
3043 res
= selection
->GetRangeAt(mSelectedCellIndex
, getter_AddRefs(range
));
3044 NS_ENSURE_SUCCESS(res
, res
);
3045 NS_ENSURE_TRUE(range
, NS_ERROR_FAILURE
);
3047 res
= GetCellFromRange(range
, aCell
);
3048 // Failure here means the range doesn't contain a cell
3049 NS_ENSURE_SUCCESS(res
, NS_EDITOR_ELEMENT_NOT_FOUND
);
3051 // We found a selected cell
3053 #ifdef DEBUG_cmanske
3055 printf("GetNextSelectedCell: Collapsed range found\n");
3058 // If we didn't find a cell, continue to next range in selection
3060 // No cell means all remaining ranges were collapsed (cells were deleted)
3061 NS_ENSURE_TRUE(*aCell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
3065 *aRange
= range
.get();
3069 // Setup for next cell
3070 mSelectedCellIndex
++;
3076 nsHTMLEditor::GetFirstSelectedCellInTable(PRInt32
*aRowIndex
, PRInt32
*aColIndex
, nsIDOMElement
**aCell
)
3078 NS_ENSURE_TRUE(aCell
, NS_ERROR_NULL_POINTER
);
3085 nsCOMPtr
<nsIDOMElement
> cell
;
3086 nsresult res
= GetFirstSelectedCell(nsnull
, getter_AddRefs(cell
));
3087 NS_ENSURE_SUCCESS(res
, res
);
3088 NS_ENSURE_TRUE(cell
, NS_EDITOR_ELEMENT_NOT_FOUND
);
3090 *aCell
= cell
.get();
3093 // Also return the row and/or column if requested
3094 if (aRowIndex
|| aColIndex
)
3096 PRInt32 startRowIndex
, startColIndex
;
3097 res
= GetCellIndexes(cell
, &startRowIndex
, &startColIndex
);
3098 if(NS_FAILED(res
)) return res
;
3101 *aRowIndex
= startRowIndex
;
3104 *aColIndex
= startColIndex
;
3111 nsHTMLEditor::SetSelectionAfterTableEdit(nsIDOMElement
* aTable
, PRInt32 aRow
, PRInt32 aCol
,
3112 PRInt32 aDirection
, PRBool aSelected
)
3114 NS_ENSURE_TRUE(aTable
, NS_ERROR_NOT_INITIALIZED
);
3116 nsCOMPtr
<nsISelection
>selection
;
3117 nsresult res
= GetSelection(getter_AddRefs(selection
));
3118 NS_ENSURE_SUCCESS(res
, res
);
3122 #ifdef DEBUG_cmanske
3123 printf("Selection not found after table manipulation!\n");
3125 return NS_ERROR_FAILURE
;
3128 nsCOMPtr
<nsIDOMElement
> cell
;
3129 PRBool done
= PR_FALSE
;
3131 res
= GetCellAt(aTable
, aRow
, aCol
, getter_AddRefs(cell
));
3132 if (NS_SUCCEEDED(res
))
3138 // Reselect the cell
3139 return SelectElement(cell
);
3143 // Set the caret to deepest first child
3144 // but don't go into nested tables
3145 // TODO: Should we really be placing the caret at the END
3146 // of the cell content?
3147 return CollapseSelectionToDeepestNonTableFirstChild(selection
, cell
);
3150 // Setup index to find another cell in the
3151 // direction requested, but move in
3152 // other direction if already at beginning of row or column
3155 case ePreviousColumn
:
3186 // We didn't find a cell
3187 // Set selection to just before the table
3188 nsCOMPtr
<nsIDOMNode
> tableParent
;
3189 PRInt32 tableOffset
;
3190 res
= aTable
->GetParentNode(getter_AddRefs(tableParent
));
3191 if(NS_SUCCEEDED(res
) && tableParent
)
3193 if(NS_SUCCEEDED(GetChildOffset(aTable
, tableParent
, tableOffset
)))
3194 return selection
->Collapse(tableParent
, tableOffset
);
3196 // Last resort: Set selection to start of doc
3197 // (it's very bad to not have a valid selection!)
3198 return SetSelectionAtDocumentStart(selection
);
3202 nsHTMLEditor::GetSelectedOrParentTableElement(nsAString
& aTagName
,
3203 PRInt32
*aSelectedCount
,
3204 nsIDOMElement
** aTableElement
)
3206 NS_ENSURE_ARG_POINTER(aTableElement
);
3207 NS_ENSURE_ARG_POINTER(aSelectedCount
);
3208 *aTableElement
= nsnull
;
3209 aTagName
.Truncate();
3210 *aSelectedCount
= 0;
3212 nsCOMPtr
<nsISelection
> selection
;
3213 nsresult res
= GetSelection(getter_AddRefs(selection
));
3214 NS_ENSURE_SUCCESS(res
, res
);
3215 NS_ENSURE_TRUE(selection
, NS_ERROR_FAILURE
);
3217 // Try to get the first selected cell
3218 nsCOMPtr
<nsIDOMElement
> tableOrCellElement
;
3219 res
= GetFirstSelectedCell(nsnull
, getter_AddRefs(tableOrCellElement
));
3220 NS_ENSURE_SUCCESS(res
, res
);
3222 NS_NAMED_LITERAL_STRING(tdName
, "td");
3224 if (tableOrCellElement
)
3226 // Each cell is in its own selection range,
3227 // so count signals multiple-cell selection
3228 res
= selection
->GetRangeCount(aSelectedCount
);
3229 NS_ENSURE_SUCCESS(res
, res
);
3234 nsCOMPtr
<nsIDOMNode
> anchorNode
;
3235 res
= selection
->GetAnchorNode(getter_AddRefs(anchorNode
));
3236 if(NS_FAILED(res
)) return res
;
3237 NS_ENSURE_TRUE(anchorNode
, NS_ERROR_FAILURE
);
3239 nsCOMPtr
<nsIDOMNode
> selectedNode
;
3241 // Get child of anchor node, if exists
3243 anchorNode
->HasChildNodes(&hasChildren
);
3247 PRInt32 anchorOffset
;
3248 res
= selection
->GetAnchorOffset(&anchorOffset
);
3249 NS_ENSURE_SUCCESS(res
, res
);
3250 selectedNode
= GetChildAt(anchorNode
, anchorOffset
);
3253 selectedNode
= anchorNode
;
3254 // If anchor doesn't have a child, we can't be selecting a table element,
3255 // so don't do the following:
3259 nsCOMPtr
<nsIAtom
> atom
= nsEditor::GetTag(selectedNode
);
3261 if (atom
== nsEditProperty::td
)
3263 tableOrCellElement
= do_QueryInterface(selectedNode
);
3265 // Each cell is in its own selection range,
3266 // so count signals multiple-cell selection
3267 res
= selection
->GetRangeCount(aSelectedCount
);
3268 NS_ENSURE_SUCCESS(res
, res
);
3270 else if (atom
== nsEditProperty::table
)
3272 tableOrCellElement
= do_QueryInterface(selectedNode
);
3273 aTagName
.AssignLiteral("table");
3274 *aSelectedCount
= 1;
3276 else if (atom
== nsEditProperty::tr
)
3278 tableOrCellElement
= do_QueryInterface(selectedNode
);
3279 aTagName
.AssignLiteral("tr");
3280 *aSelectedCount
= 1;
3284 if (!tableOrCellElement
)
3286 // Didn't find a table element -- find a cell parent
3287 res
= GetElementOrParentByTagName(tdName
, anchorNode
, getter_AddRefs(tableOrCellElement
));
3288 if(NS_FAILED(res
)) return res
;
3289 if (tableOrCellElement
)
3293 if (tableOrCellElement
)
3295 *aTableElement
= tableOrCellElement
.get();
3296 NS_ADDREF(*aTableElement
);
3302 nsHTMLEditor::GetSelectedCellsType(nsIDOMElement
*aElement
, PRUint32
*aSelectionType
)
3304 NS_ENSURE_ARG_POINTER(aSelectionType
);
3305 *aSelectionType
= 0;
3307 // Be sure we have a table element
3308 // (if aElement is null, this uses selection's anchor node)
3309 nsCOMPtr
<nsIDOMElement
> table
;
3311 nsresult res
= GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aElement
, getter_AddRefs(table
));
3312 NS_ENSURE_SUCCESS(res
, res
);
3314 PRInt32 rowCount
, colCount
;
3315 res
= GetTableSize(table
, &rowCount
, &colCount
);
3316 NS_ENSURE_SUCCESS(res
, res
);
3318 // Traverse all selected cells
3319 nsCOMPtr
<nsIDOMElement
> selectedCell
;
3320 res
= GetFirstSelectedCell(nsnull
, getter_AddRefs(selectedCell
));
3321 NS_ENSURE_SUCCESS(res
, res
);
3322 if (res
== NS_EDITOR_ELEMENT_NOT_FOUND
) return NS_OK
;
3324 // We have at least one selected cell, so set return value
3325 *aSelectionType
= nsISelectionPrivate::TABLESELECTION_CELL
;
3327 // Store indexes of each row/col to avoid duplication of searches
3328 nsTArray
<PRInt32
> indexArray
;
3330 PRBool allCellsInRowAreSelected
= PR_FALSE
;
3331 PRBool allCellsInColAreSelected
= PR_FALSE
;
3332 while (NS_SUCCEEDED(res
) && selectedCell
)
3334 // Get the cell's location in the cellmap
3335 PRInt32 startRowIndex
, startColIndex
;
3336 res
= GetCellIndexes(selectedCell
, &startRowIndex
, &startColIndex
);
3337 if(NS_FAILED(res
)) return res
;
3339 if (!indexArray
.Contains(startColIndex
))
3341 indexArray
.AppendElement(startColIndex
);
3342 allCellsInRowAreSelected
= AllCellsInRowSelected(table
, startRowIndex
, colCount
);
3343 // We're done as soon as we fail for any row
3344 if (!allCellsInRowAreSelected
) break;
3346 res
= GetNextSelectedCell(nsnull
, getter_AddRefs(selectedCell
));
3349 if (allCellsInRowAreSelected
)
3351 *aSelectionType
= nsISelectionPrivate::TABLESELECTION_ROW
;
3356 // Empty the indexArray
3359 // Start at first cell again
3360 res
= GetFirstSelectedCell(nsnull
, getter_AddRefs(selectedCell
));
3361 while (NS_SUCCEEDED(res
) && selectedCell
)
3363 // Get the cell's location in the cellmap
3364 PRInt32 startRowIndex
, startColIndex
;
3365 res
= GetCellIndexes(selectedCell
, &startRowIndex
, &startColIndex
);
3366 if(NS_FAILED(res
)) return res
;
3368 if (!indexArray
.Contains(startRowIndex
))
3370 indexArray
.AppendElement(startColIndex
);
3371 allCellsInColAreSelected
= AllCellsInColumnSelected(table
, startColIndex
, rowCount
);
3372 // We're done as soon as we fail for any column
3373 if (!allCellsInRowAreSelected
) break;
3375 res
= GetNextSelectedCell(nsnull
, getter_AddRefs(selectedCell
));
3377 if (allCellsInColAreSelected
)
3378 *aSelectionType
= nsISelectionPrivate::TABLESELECTION_COLUMN
;
3384 nsHTMLEditor::AllCellsInRowSelected(nsIDOMElement
*aTable
, PRInt32 aRowIndex
, PRInt32 aNumberOfColumns
)
3386 NS_ENSURE_TRUE(aTable
, PR_FALSE
);
3388 PRInt32 curStartRowIndex
, curStartColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
3391 for( PRInt32 col
= 0; col
< aNumberOfColumns
; col
+= NS_MAX(actualColSpan
, 1))
3393 nsCOMPtr
<nsIDOMElement
> cell
;
3394 nsresult res
= GetCellDataAt(aTable
, aRowIndex
, col
, getter_AddRefs(cell
),
3395 &curStartRowIndex
, &curStartColIndex
,
3397 &actualRowSpan
, &actualColSpan
, &isSelected
);
3399 NS_ENSURE_SUCCESS(res
, PR_FALSE
);
3400 // If no cell, we may have a "ragged" right edge,
3401 // so return TRUE only if we already found a cell in the row
3402 NS_ENSURE_TRUE(cell
, (col
> 0) ? PR_TRUE
: PR_FALSE
);
3404 // Return as soon as a non-selected cell is found
3405 NS_ENSURE_TRUE(isSelected
, PR_FALSE
);
3407 NS_ASSERTION((actualColSpan
> 0),"ActualColSpan = 0 in AllCellsInRowSelected");
3413 nsHTMLEditor::AllCellsInColumnSelected(nsIDOMElement
*aTable
, PRInt32 aColIndex
, PRInt32 aNumberOfRows
)
3415 NS_ENSURE_TRUE(aTable
, PR_FALSE
);
3417 PRInt32 curStartRowIndex
, curStartColIndex
, rowSpan
, colSpan
, actualRowSpan
, actualColSpan
;
3420 for( PRInt32 row
= 0; row
< aNumberOfRows
; row
+= NS_MAX(actualRowSpan
, 1))
3422 nsCOMPtr
<nsIDOMElement
> cell
;
3423 nsresult res
= GetCellDataAt(aTable
, row
, aColIndex
, getter_AddRefs(cell
),
3424 &curStartRowIndex
, &curStartColIndex
,
3426 &actualRowSpan
, &actualColSpan
, &isSelected
);
3428 NS_ENSURE_SUCCESS(res
, PR_FALSE
);
3429 // If no cell, we must have a "ragged" right edge on the last column
3430 // so return TRUE only if we already found a cell in the row
3431 NS_ENSURE_TRUE(cell
, (row
> 0) ? PR_TRUE
: PR_FALSE
);
3433 // Return as soon as a non-selected cell is found
3434 NS_ENSURE_TRUE(isSelected
, PR_FALSE
);
3440 nsHTMLEditor::IsEmptyCell(nsIDOMElement
*aCell
)
3442 nsCOMPtr
<nsIDOMNode
> cellChild
;
3444 // Check if target only contains empty text node or <br>
3445 nsresult res
= aCell
->GetFirstChild(getter_AddRefs(cellChild
));
3446 NS_ENSURE_SUCCESS(res
, PR_FALSE
);
3450 nsCOMPtr
<nsIDOMNode
> nextChild
;
3451 res
= cellChild
->GetNextSibling(getter_AddRefs(nextChild
));
3452 NS_ENSURE_SUCCESS(res
, PR_FALSE
);
3455 // We insert a single break into a cell by default
3456 // to have some place to locate a cursor -- it is dispensable
3457 PRBool isEmpty
= nsTextEditUtils::IsBreak(cellChild
);
3458 // Or check if no real content
3461 res
= IsEmptyNode(cellChild
, &isEmpty
, PR_FALSE
, PR_FALSE
);
3462 NS_ENSURE_SUCCESS(res
, PR_FALSE
);